[LeetCode]环链表相关的题目和算法

这篇文章讨论一下与链表的环相关的题目,我目前遇到的一共有3种题目。

1.判断一个链表是否有环(LeetCode相关题目:https://leetcode.com/problems/linked-list-cycle/description/)

设置两个指针,初始值都指向头,一快一慢,slow每次前进一步,fast每次前进两步,假如链表存在环,两个指针必定会相遇。假如fast走到尾部,则为无环链表。

代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        ListNode *fast = head;
        ListNode *slow = head;
        while (fast && fast->next) {
            fast = fast->next->next;
            slow = slow->next;
            if (slow == fast) {
                return true;
            }
        }
        return false;
    }
};

 

 2.判断一个有环链表中环的位置(LeetCode相关题目:https://leetcode.com/problems/linked-list-cycle-ii/description/)

也是利用到一快一慢的两个指针,借用网上一个图:

两个指针顺时针走动,fast一次走两步,slow一次走一步,假设两个指针第一次在Z相遇,slow走过的距离为a+b,fast走过的距离为a+b+c+b,2(a + b) == a + b + c + b,得出a == c,因此此时将fast指针放回起点,将两次走一步改为一次走一步。此时,fast和slow同时开始走,由于他们的路程和速度都一样,因此他们会在环的起点相遇。

代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *slow = head;
        ListNode *fast = head;
        while (true) {
            if (fast == NULL || fast->next == NULL) return NULL;
            else {
                fast = fast->next->next;
                slow = slow->next;
                if (fast == slow) break;
            }
        }
        fast = head;
        while (fast != slow) {
            slow = slow->next;
            fast = fast->next;
        }
        return slow;
    }
};

 

3.求两个链表的交点(LeetCode相关题目:https://leetcode.com/problems/intersection-of-two-linked-lists/description/)

尽管看上去和链表的环没啥关系,但是这个题目可以用这种方法来做。我们可以先将其中一个环的头尾相连,然后按照上一题的做法,就可以找到交点的位置了。不过要记得将一切复原,也就是将环解开。

代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if (headA == NULL || headB == NULL) return NULL;
        ListNode *copyA = headA;
        ListNode *copyB = headB;
        while (headA->next != NULL) {
            headA = headA->next;
        }
        ListNode *recover = headA; 
        headA->next = copyA;
        ListNode *fast = headB;
        ListNode *slow = headB;
        while (true) {
            if (!fast || !fast->next) {
                recover->next = NULL;
                headA = copyA;
                headB = copyB;
                return NULL;
            }
            fast = fast->next->next;
            slow = slow->next;
            if (fast == slow) break;
        }
        fast = headB;
        while (fast != slow) {
            slow = slow->next;
            fast = fast->next;
        }
        recover->next = NULL;
        headA = copyA;
        headB = copyB;
        return slow;
    }
};

 

4.其他的。。。

我能想到的其他题目类型:

求环的长度:在2的前提下,从环的起点开始,一个指针不动,一个指针走,统计走的长度,两个指针重新相遇时的长度就是环的长度

将环解开:原理其实一样

因此,这类题目的关键是利用一快一慢两个指针,然后要懂得找出环的起点的方法,其他变形的问题也就迎刃而解了。

posted @ 2017-12-28 21:40  fengzw  阅读(401)  评论(0编辑  收藏  举报