linked-list 汇总

转+修改整理。

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.PriorityQueue;
import java.util.Stack;

/**
 * http://blog.csdn.net/luckyxiaoqiang/article/details/7393134 轻松搞定面试中的链表题目
 * http://www.cnblogs.com/jax/archive/2009/12/11/1621504.html 算法大全(1)单链表
 *
 * 目录:
 * 1. 求单链表中结点的个数: getListLength
 * 2. 将单链表反转: reverseList(遍历),reverseListRec(递归)
 * 3. 查找单链表中的倒数第K个结点(k > 0): reGetKthNode
 * 4. 查找单链表的中间结点: getMiddleNode
 * 5. 从尾到头打印单链表: reversePrintListStack,reversePrintListRec(递归)
 * 6. 已知两个单链表: pHead1 和pHead2 各自有序,把它们合并成一个链表依然有序: mergeSortedList, mergeSortedListRec
 * 7. 判断一个单链表中是否有环: hasCycle
 * 8. 判断两个单链表是否相交: isIntersect
 * 9. 求两个单链表相交的第一个节点: getFirstCommonNode
 * 10. 已知一个单链表中存在环,求进入环中的第一个节点: getFirstNodeInCycle, getFirstNodeInCycleHashMap
 * 11. 给出一单链表头指针pHead和一节点指针pToBeDeleted,O(1)时间复杂度删除节点pToBeDeleted: delete
 *
 */
public class ListSummary {

    public static void main(String[] args) {
        ListNode n1 = ListUtils.makeList(1, 2, 3, 4, 5);

        ListUtils.printList(n1);

        ListNode x = reGetKthNode(n1, 2);
        System.out.println(x.val);

    }

    // 求单链表中结点的个数
    // 注意检查链表是否为空。时间复杂度为O(n)
    public static int getListLength(ListNode head) {
        // 注意头结点为空情况
        if (head == null) {
            return 0;
        }

        int len = 0;
        ListNode cur = head;
        while (cur != null) {
            len++;
            cur = cur.next;
        }
        return len;
    }

    // 翻转链表(遍历) pre,cur,post的运用
    // 注意链表为空和只有一个结点的情况。时间复杂度为O(n)
    public static ListNode reverse(ListNode head) {
        if (head == null || head.next == null)
            return head;
        ListNode pre = head;
        ListNode cur = head.next;
        while (cur != null) {
            ListNode post = cur.next;
            cur.next = pre;
            pre = cur;
            cur = post;
        }
        head.next = null;
        return pre;
    }

    // 翻转递归(递归)
    // 递归的精髓在于你就默认reverseListRec已经成功帮你解决了子问题了!但别去想如何解决的
    public static ListNode reverseListRec(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }

        ListNode reHead = reverseListRec(head.next);
        head.next.next = head; // 把head接在reHead串的最后一个后面
        head.next = null; // 防止循环链表
        return reHead;
    }

    /**
     * 查找单链表中的倒数第K个结点(k > 0) 主要思路就是使用两个指针,先让前面的指针走到正向第k个结点
     * 这样前后两个指针的距离差是k-1,之后前后两个指针一起向前走,前面的指针走到最后一个结点时,后面指针所指结点就是倒数第k个结点
     * 注意k>len的情况如何处理
     */
    public static ListNode reGetKthNode(ListNode head, int k) {
        if (k <= 0)
            return null;

        ListNode s = head;
        ListNode f = head;
        for (int i = 0; i < k - 1 && f != null; i++)
            f = f.next;

        if (f == null)// k is larger than len
            return null;

        while (f.next != null) {
            s = s.next;
            f = f.next;
        }
        return s;
    }

    // 查找单链表的中间结点
    /**
     * 此题可应用于上一题类似的思想。也是设置两个指针,只不过这里是,两个指针同时向前走,前面的指针每次走两步,后面的指针每次走一步,
     * 前面的指针走到最后一个结点时,后面的指针所指结点就是中间结点,即第(n/2+1)个结点。注意链表为空,链表结点个数为1和2的情况。时间复杂度O(n
     */
    public static ListNode getMiddleNode(ListNode head) {

        ListNode slow = head;
        ListNode fast = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }

        return slow;
    }

    /**
     * 从尾到头打印单链表
     * 对于这种颠倒顺序的问题,我们应该就会想到栈,后进先出。所以,这一题要么自己使用栈,要么让系统使用栈,也就是递归。注意链表为空的情况
     * 。时间复杂度为O(n)
     */
    public static void reversePrintListStack(ListNode head) {
        Stack<ListNode> s = new Stack<ListNode>();
        ListNode cur = head;
        while (cur != null) {
            s.push(cur);
            cur = cur.next;
        }

        while (!s.empty()) {
            cur = s.pop();
            System.out.print(cur.val + " ");
        }
        System.out.println();
    }

    /**
     * 从尾到头打印链表,使用递归(优雅!)
     */
    public static void reversePrintListRec(ListNode head) {
        if (head == null) {
            return;
        } else {
            reversePrintListRec(head.next);
            System.out.print(head.val + " ");
        }
    }

    /**
     * 已知两个单链表pHead1 和pHead2 各自有序,把它们合并成一个链表依然有序
     * 这个类似归并排序。尤其注意两个链表都为空,和其中一个为空时的情况。只需要O(1)的空间。时间复杂度为O(max(len1, len2))
     */
    public static ListNode mergeSortedList(ListNode l1, ListNode l2) {
        if (l1 == null)
            return l2;
        if (l2 == null)
            return l1;
        ListNode p1 = l1;
        ListNode p2 = l2;

        ListNode head = new ListNode(-1);
        ListNode p = head;

        while (p1 != null && p2 != null) {
            if (p1.val <= p2.val) {
                p.next = p1;
                p1 = p1.next;
            } else {
                p.next = p2;
                p2 = p2.next;
            }
            p = p.next;
        }
        if (p1 != null)
            p.next = p1;
        if (p2 != null)
            p.next = p2;

        return head.next;

    }

    /**
     * 递归合并两链表(优雅!)
     */
    public static ListNode mergeSortedListRec(ListNode head1, ListNode head2) {
        if (head1 == null) {
            return head2;
        }
        if (head2 == null) {
            return head1;
        }

        ListNode mergeHead = null;
        if (head1.val < head2.val) {
            mergeHead = head1;
            mergeHead.next = mergeSortedListRec(head1.next, head2);
        } else {
            mergeHead = head2;
            mergeHead.next = mergeSortedListRec(head1, head2.next);
        }
        return mergeHead;
    }

    /**
     * 判断一个单链表中是否有环
     * 这里也是用到两个指针。如果一个链表中有环,也就是说用一个指针去遍历,是永远走不到头的。因此,我们可以用两个指针去遍历,一个指针一次走两步
     * ,一个指针一次走一步,如果有环,两个指针肯定会在环中相遇。时间复杂度为O(n)
     */
    public static boolean hasCycle(ListNode head) {
        ListNode fast = head; // 快指针每次前进两步
        ListNode slow = head; // 慢指针每次前进一步

        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) { // 相遇,存在环
                return true;
            }
        }
        return false;
    }

    // 判断两个单链表是否相交
    /**
     * 如果两个链表相交于某一节点,那么在这个相交节点之后的所有节点都是两个链表所共有的。 也就是说,如果两个链表相交,那么最后一个节点肯定是共有的。
     * 先遍历第一个链表,记住最后一个节点,然后遍历第二个链表, 到最后一个节点时和第一个链表的最后一个节点做比较,如果相同,则相交,
     * 否则不相交。时间复杂度为O(len1+len2),因为只需要一个额外指针保存最后一个节点地址, 空间复杂度为O(1)
     */
    public static boolean isIntersect(ListNode head1, ListNode head2) {
        if (head1 == null || head2 == null) {
            return false;
        }

        ListNode tail1 = head1;
        // 找到链表1的最后一个节点
        while (tail1.next != null) {
            tail1 = tail1.next;
        }

        ListNode tail2 = head2;
        // 找到链表2的最后一个节点
        while (tail2.next != null) {
            tail2 = tail2.next;
        }

        return tail1 == tail2;
    }

    /**
     * 求两个单链表相交的第一个节点 对第一个链表遍历,计算长度len1,同时保存最后一个节点的地址。
     * 对第二个链表遍历,计算长度len2,同时检查最后一个节点是否和第一个链表的最后一个节点相同,若不相同,不相交,结束。
     * 两个链表均从头节点开始,假设len1大于len2
     * ,那么将第一个链表先遍历len1-len2个节点,此时两个链表当前节点到第一个相交节点的距离就相等了,然后一起向后遍历,直到两个节点的地址相同。
     * 时间复杂度,O(len1+len2)
     *
     * ---- len2 |__________ | --------- len1 |---|<- len1-len2
     */
    public static ListNode getFirstCommonNode(ListNode head1, ListNode head2) {
        if (head1 == null || head2 == null) {
            return null;
        }
        int len1 = 1;
        ListNode tail1 = head1;
        while (tail1.next != null) {
            tail1 = tail1.next;
            len1++;
        }

        int len2 = 1;
        ListNode tail2 = head2;
        while (tail2.next != null) {
            tail2 = tail2.next;
            len2++;
        }

        // 不相交直接返回NULL
        if (tail1 != tail2) {
            return null;
        }

        ListNode n1 = head1;
        ListNode n2 = head2;

        // 略过较长链表多余的部分
        if (len1 > len2) {
            int k = len1 - len2;
            while (k != 0) {
                n1 = n1.next;
                k--;
            }
        } else {
            int k = len2 - len1;
            while (k != 0) {
                n2 = n2.next;
                k--;
            }
        }

        // 一起向后遍历,直到找到交点
        while (n1 != n2) {
            n1 = n1.next;
            n2 = n2.next;
        }

        return n1;
    }

    /**
     * 求进入环中的第一个节点 用快慢指针做(本题用了Crack the Coding Interview的解法,因为更简洁易懂!)
     */
    public static ListNode getFirstNodeInCycle(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        boolean meet = false;

        // 1) 找到快慢指针相遇点
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) { // Collision
                meet = true;
                break;
            }
        }

        // 错误检查,这是没有环的情况
        if (!meet) {
            return null;
        }

        // 2)现在,相遇点离环的开始处的距离等于链表头到环开始处的距离,
        // 这样,我们把慢指针放在链表头,快指针保持在相遇点,然后
        // 同速度前进,再次相遇点就是环的开始处!
        slow = head;
        while (slow != fast) {
            slow = slow.next;
            fast = fast.next;
        }

        // 再次相遇点就是环的开始处
        return fast;
    }

    /**
     * 求进入环中的第一个节点 用HashMap做 一个无环的链表,它每个结点的地址都是不一样的。
     * 但如果有环,指针沿着链表移动,那这个指针最终会指向一个已经出现过的地址 以地址为哈希表的键值,每出现一个地址,就将该键值对应的实值置为true。
     * 那么当某个键值对应的实值已经为true时,说明这个地址之前已经出现过了, 直接返回它就OK了
     */
    public static ListNode getFirstNodeInCycleHashMap(ListNode head) {
        HashMap<ListNode, Boolean> map = new HashMap<ListNode, Boolean>();
        while (head != null) {
            if (map.get(head) == true) {
                return head; // 这个地址之前已经出现过了,就是环的开始处
            } else {
                map.put(head, true);
                head = head.next;
            }
        }
        return head;
    }

    /**
     * 给出一单链表头指针head和一节点指针toBeDeleted,O(1)时间复杂度删除节点tBeDeleted
     * 对于删除节点,我们普通的思路就是让该节点的前一个节点指向该节点的下一个节点
     * ,这种情况需要遍历找到该节点的前一个节点,时间复杂度为O(n)。对于链表,
     * 链表中的每个节点结构都是一样的,所以我们可以把该节点的下一个节点的数据复制到该节点
     * ,然后删除下一个节点即可。要注意最后一个节点的情况,这个时候只能用常见的方法来操作,先找到前一个节点,但总体的平均时间复杂度还是O(1)
     */
    public void delete(ListNode head, ListNode toDelete) {
        if (toDelete == null) {
            return;
        }
        if (toDelete.next != null) { // 要删除的是一个中间节点
            toDelete.val = toDelete.next.val; // 将下一个节点的数据复制到本节点!
            toDelete.next = toDelete.next.next;
        } else { // 要删除的是最后一个节点!
            if (head == toDelete) { // 链表中只有一个节点的情况
                head = null;
            } else {
                ListNode node = head;
                while (node.next != toDelete) { // 找到倒数第二个节点
                    node = node.next;
                }
                node.next = null;
            }
        }
    }

}

linked-list 汇总,布布扣,bubuko.com

时间: 2024-10-12 13:32:42

linked-list 汇总的相关文章

java神奇问题汇总

com.sun .xml.bind.v2.ContextFactory 类找不到的解决 用cxf+spring发布webservice,使用apache-cxf客户端方式调用接口访问服务器时,报javax.xml.bind.JAXBException: Provider com.sun.xml.bind.v2.ContextFactory not found 错误,异常堆栈信息如下: Caused by: javax.xml.bind.JAXBException: Provider com.su

【leetcode82】Linked List Cycle

题目描述: 判断有序list是不是环 要求: 时间复杂度o(n) 原文描述: Given a linked list, determine if it has a cycle in it. Follow up: Can you solve it without using extra space? 思路: 设置两个指针,一个快指针,每次走两步,一个慢指针,每次走一步 如果块指针==慢指针,那么包含环 注意判断空值和长度为一的情况 - 代码: /** * Definition for singly

Cracking the coding interview汇总目录

很久之前刷的CTCI的题目,都快忘记了,做个分类汇总,再重新好好复习一遍. Chapter 1 | Arrays and Strings 1.1 Implement an algorithm to determine if a string has all unique characters. What if you can not use additional data structures? 1.2 Write code to reverse a C-Style String. (C-Str

[leetcode]-Linked List Random Node

题目描述: 给定一个单向的链表,要求以相等的概率返回 要求: 时间复杂度o(n),空间复杂度o(1) 原文描述: Given a singly linked list, return a random node's value from the linked list. Each node must have the same probability of being chosen. Follow up: What if the linked list is extremely large an

Java常用英语汇总(面试必备)

Java常用英语汇总(面试必备) abstract (关键字)             抽象 ['.bstr.kt] access                            vt.访问,存取 ['.kses]‘(n.入口,使用权) algorithm                     n.算法 ['.lg.riem] annotation                     [java]代码注释 [.n.u'tei..n] anonymous                

[LeetCode] Odd Even Linked List 奇偶链表

Given a singly linked list, group all odd nodes together followed by the even nodes. Please note here we are talking about the node number and not the value in the nodes. You should try to do it in place. The program should run in O(1) space complexi

[LeetCode] Plus One Linked List 链表加一运算

Given a non-negative number represented as a singly linked list of digits, plus one to the number. The digits are stored such that the most significant digit is at the head of the list. Example: Input: 1->2->3 Output: 1->2->4 这道题给了我们一个链表,用来模拟一

[LeetCode] Delete Node in a Linked List 删除链表的节点

Write a function to delete a node (except the tail) in a singly linked list, given only access to that node. Supposed the linked list is 1 -> 2 -> 3 -> 4 and you are given the third node with value 3, the linked list should become 1 -> 2 ->

Palindrome Linked List Leetcode

Given a singly linked list, determine if it is a palindrome. Follow up:Could you do it in O(n) time and O(1) space? 这个follow up要求O(n)的时间和O(1)的空间,可以先reverse一半,然后再对比.只是reverse的时候要考虑奇数个还是偶数个.如果是奇数个的话,就跳过最中间的,从下一个开始reverse. /** * Definition for singly-li

237. Delete Node in a Linked List

1. 问题描述 Write a function to delete a node (except the tail) in a singly linked list, given only access to that node. Supposed the linked list is 1 -> 2 -> 3 -> 4 and you are given the third node with value 3, the linked list should become 1 ->