Linked List Cycle系列

首先讨论下有环单链表相关问题

1. 判断单链表是否有环

使用slow,fast指针从头开始扫描链表,slow指针每次走一步,fast指针每次走两步,如果链表有环,那么fast指针一定会追上slow指针,否则,fast指针会遇到null

2. 求有环单链表的环长


设环长为R,当fast,slow指针相遇是在环中某一点c处时,继续让slow每次走一步,fast每次走两步,同时开始记录slow指针走的步数,等fast,slow指针再次相遇时,有

slow指针走了  len

fast指针走了  2len

由于fast与slow同起点出发并追上了slow,他们所走的路长之差就恰好为R,即 R = 2len - len = len,即环的长度。

3. 求有环单链表的环连接点起始位置

依然使用fast,slow指针,fast指针每次走两步,slow指针每次走一步,它们第一次相遇时,令fast指针指向链表开头,每次走一步,slow指针依然每次走一步,fast,slow指针再次相遇时的节点就是环连接的起始节点。

证明如下:

第一次相遇时:

slow指针走的步长为    Sab + Sbc

fast指针走的步长为     Sab + Sbc + nR,其中 n = 1,2,3 ......

fast指针的速度是slow指针速度的两倍,故有   

 2(Sab + Sbc)=  Sab + Sbc + nR,得 Sab   =  nR - Sbc = (n-1)R + Scb

4. 求有环单链表的链表长

由2知道环长,由3知道Sab,那么单链表的长度也就知道了。

leetcode相关题目如下:

 

Linked List Cycle

 

 

Given a linked list, determine if it has a cycle in it.

Follow up:
Can you solve it without using extra space?

 

方法和原理见1,代码如下:
/**
 * 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) {
        if( !head ) return false;
        ListNode* fast = head;
        ListNode* slow = head;
        while( fast && fast->next ) {
            fast = fast->next->next;
            slow = slow->next;
            if( fast == slow )
                return true;
        }
        return false;
    }
};

Linked List Cycle II

 

 

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.

Follow up:
Can you solve it without using extra space?

 

方法和原理见3,代码如下:
/**
 * 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) {
        if( !head ) return NULL;
        ListNode* fast = head;
        ListNode* slow = head;
        while( fast && fast->next ) {
            fast = fast->next->next;
            slow = slow->next;
            if( fast == slow )      //第一次相遇,退出循环
                break;
        }
        if( !fast || !fast->next ) return NULL;     //如果没有相遇,则说明没环
        fast = head;
        while( fast != slow ) {     //fast每次走一步,再次相遇时则是环起点
            fast = fast->next;
            slow = slow->next;
        }
        return fast;
    }
};
当然也可以用hash表来做,比较简单,直接建立hash set存储访问过的节点地址,代码如下:
/**
 * 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) {
        if( !head ) return NULL;
        unordered_set<ListNode*> ust;
        ListNode* pcur = head;
        while( pcur ) {
            if( ust.find(pcur) == ust.end() ) {     //如果没有找到,则将节点地址放入hash表中
                ust.insert(pcur);
                pcur = pcur->next;
            }
            else        //在hash表中找到了,说明是环起始节点
                break;
        }
        return pcur;    //如果是单链表,那么pcur是null,否则就是环起始节点
    }
};




 

posted on 2014-08-14 15:49  bug睡的略爽  阅读(163)  评论(0编辑  收藏  举报

导航