0
点赞
收藏
分享

微信扫一扫

链表算法 篇三

单链表的中点

package com.algorithm202305.linkedlist;

/**
 * https://leetcode.cn/problems/middle-of-the-linked-list/submissions/432318762/
 * 单链表的中点
 * 问题的关键也在于我们无法直接得到单链表的长度 n,常规方法也是先遍历链表计算 n,再遍历一次得到第 n / 2 个节点,也就是中间节点。
 *
 * 如果想一次遍历就得到中间节点,也需要耍点小聪明,使用「快慢指针」的技巧:
 *
 * 我们让两个指针 slow 和 fast 分别指向链表头结点 head。
 *
 * 每当慢指针 slow 前进一步,快指针 fast 就前进两步,这样,当 fast 走到链表末尾时,slow 就指向了链表中点。
 */
public class Demo5 {
    public static class ListNode {
        int val;
        ListNode next;

        ListNode() {
        }

        ListNode(int val) {
            this.val = val;
        }

        ListNode(int val, ListNode next) {
            this.val = val;
            this.next = next;
        }

    }
    ListNode middleNode(ListNode head){
        //快慢指针指向head
        ListNode slow = head,fast = head;
        while (slow != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }
}

链表是否成环+环的起点

每当慢指针 slow 前进一步,快指针 fast 就前进两步。

如果 fast 最终遇到空指针,说明链表中没有环;如果 fast 最终和 slow 相遇,那肯定是 fast 超过了 slow 一圈,说明链表中含有环。

只需要把寻找链表中点的代码稍加修改就行了:

package com.algorithm202305.linkedlist;

/**
 * 1 判断链表是否成环(环形链表)
 * https://leetcode.cn/problems/linked-list-cycle/
 * 2 环的起点(环形链表Ⅱ)
 * https://leetcode.cn/problems/linked-list-cycle-ii/
 * 判断链表是否包含环属于经典问题了,解决方案也是用快慢指针:
 * 
 */
public class Demo6 {
    class ListNode {
        int val;
        ListNode next;
    }

    boolean hasCycle(ListNode head) {
        ListNode slow = head, fast = head;
        while (slow != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            //fast有是slow的两倍速存在环应该有相等的节点
            if (fast == slow) {
                return true;
            }
        }
        return false;
    }
    //环的起点
    public ListNode detectCycle(ListNode head) {
        ListNode fast, slow;
        fast = slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) break;
        }
        if(fast == null || fast.next == null){
            return null;
        }
        //慢指针重新指向头节点
        slow = head;
        //快指针比慢指针要比慢指针多走K步,设置起点距离相遇点是m步,那么对于k-m就是环起点
        while (slow != fast){
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }
}

我们假设快慢指针相遇时,慢指针 slow 走了 k 步,那么快指针 fast 一定走了 2k 步:

链表算法 篇三_快慢指针

fast 一定比 slow 多走了 k 步,这多走的 k 步其实就是 fast 指针在环里转圈圈,所以 k 的值就是环长度的「整数倍」。

假设相遇点距环的起点的距离为 m,那么结合上图的 slow 指针,环的起点距头结点 head 的距离为 k - m,也就是说如果从 head 前进 k - m 步就能到达环起点。

巧的是,如果从相遇点继续前进 k - m 步,也恰好到达环起点。因为结合上图的 fast 指针,从相遇点开始走k步可以转回到相遇点,那走 k - m 步肯定就走到环起点了:

链表算法 篇三_头结点_02

所以,只要我们把快慢指针中的任一个重新指向 head,然后两个指针同速前进,k - m 步后一定会相遇,相遇之处就是环的起点了。

环的相遇点和环的起点

假设我们有一个链表:1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 3,其中节点3后面的部分形成了一个环。

首先,我们使用快慢指针算法来判断链表是否有环。我们设置快指针每次走两步,慢指针每次走一步,并从起点开始遍历链表。当快指针追上慢指针时,说明链表中存在环。在本例中,快指针和慢指针在节点6处相遇。

接下来,我们需要区分出环的起点。为此,我们再次使用快慢指针算法,但是这次我们让快指针从相遇点开始走,并且让慢指针回到起点。然后,快指针每次走两步,慢指针每次走一步,直到它们再次相遇。那么它们相遇的节点就是环的起点。

在本例中,我们让慢指针回到起点1,并让快指针从相遇点6开始走。快指针第一步可以到达节点3,第二步到达节点4,第三步到达节点5,第四步又回到了节点3。而慢指针在这个过程中只走了一步,到达了节点2。然后,我们让快指针回到起点6,并且速度变成每次走一步,慢指针也继续每次走一步。当它们再次相遇时,它们相遇的节点就是环的起点,也就是节点3。

因此,在这个例子中,相遇点是节点6,而环的起点是节点3。

链表是否相交

链表算法 篇三_头结点_03

如果用两个指针 p1p2 分别在两条链表上前进,并不能同时走到公共节点,也就无法得到相交节点 c1

解决这个问题的关键是,通过某些方式,让 p1p2 能够同时到达相交节点 c1

所以,我们可以让 p1 遍历完链表 A 之后开始遍历链表 B,让 p2 遍历完链表 B 之后开始遍历链表 A,这样相当于「逻辑上」两条链表接在了一起。

如果这样进行拼接,就可以让 p1p2 同时进入公共部分,也就是同时到达相交节点 c1

链表算法 篇三_头结点_04

package com.algorithm202305.linkedlist;

/**
 * 两链表是否有相交(相交链表)
 * https://leetcode.cn/problems/intersection-of-two-linked-lists/
 */
public class Demo7 {
    class ListNode {
        int val;
        ListNode next;
    }
    ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        // p1 指向 A 链表头结点,p2 指向 B 链表头结点
        ListNode p1 = headA, p2 = headB;
        while (p1 != p2) {
            // p1 走一步,如果走到 A 链表末尾,转到 B 链表
            if (p1 == null) p1 = headB;
            else            p1 = p1.next;
            // p2 走一步,如果走到 B 链表末尾,转到 A 链表
            if (p2 == null) p2 = headA;
            else            p2 = p2.next;
        }
        return p1;
    }

}

举报

相关推荐

0 条评论