判斷單鏈表是否存在環及尋找環的進入點

來源:互聯網
上載者:User

判斷鏈表是否存在環,有如下幾種解法:
1. 遍曆鏈表,將已經遍曆過的節點放在一個hash表中,如果一個節點已經存在hash表中,說明有環。時間:O(n) 空間:O(n)
2. 反轉鏈表。 時間O(n),空間O(1),使用三個指標。(ref:  http://www.cppblog.com/tx7do/archive/2009/01/06/71280.html)

    單鏈表反轉:下面給出兩種可能的實現。

    普通版:

    void reverse(node*& head)    {        if ( (head == 0) || (head->next == 0) ) return;// 邊界檢測        node* pNext = 0;        node* pPrev = head;// 儲存鏈表前端節點        node* pCur = head->next;// 擷取當前節點        while (pCur != 0)        {            pNext = pCur->next;// 將下一個節點儲存下來            pCur->next = pPrev;// 將當前節點的下一節點置為前節點            pPrev = pCur;// 將當前節點儲存為前一節點            pCur = pNext;// 將當前節點置為下一節點        }        head->next = NULL;        head = pPrev; }

    遞迴版:

    node* reverse( node* pNode, node*& head)    {        if ( (pNode == 0) || (pNode->next == 0) ) // 遞迴跳出條件        {            head = pNode; // 將鏈表切斷,否則會形成迴環            return pNode;        }        node* temp = reserve(pNode->next, head);// 遞迴        temp->next = pNode;// 將下一節點置為當前節點,既前置節點        return pNode;// 返回當前節點    }

      使用反轉鏈表的方法, 每過一個節點就把該節點的指標反向。若存在環,反轉next指標最終會走到鏈表頭部。若沒有環,反轉next指標會破壞鏈表結構(使鏈表反向), 所以為還原鏈表,需要把鏈表再反向一次。 這種方法的空間複雜度是O(1), 實事上我們使用了3個額外指標;而時間複雜度是O(n), 我們最多2次遍曆整個鏈表(當鏈表中沒有環的時候)。下面給出一個實現,但最大的問題是:若存在環,則無法還原到鏈表的原狀態

  bool reverse(Node *head) {   Node *curr = head;   Node *next = head->next;   curr->next = NULL;   while(next!=NULL) {    if(next == head) { /* go back to the head of the list, so there is a loop */      next->next = curr;      return true;    }    Node *temp = curr;    curr = next;    next = next->next;    curr->next = temp;   }   /* at the end of list, so there is no loop, let's reverse the list back */   next = curr->next;   curr ->next = NULL;   while(next!=NULL) {    Node *temp = curr;    curr = next;    next = next->next;    curr->next = temp;   }   return false;  }

3. 快慢指標。 時間O(n),空間O(1),使用兩個指標。(ref: http://blog.csdn.net/mingming_bupt/article/details/6331333)

    判斷環的存在:設定兩個指標(fast, slow),初始值都指向頭,slow每次前進一步,fast每次前進二步。如果鏈表存在環,則fast必定先進入環,而slow後進入環,兩個指標必定相遇。(當然,fast先行頭到尾部為NULL,則是無環鏈表)。

bool IsExitsLoop(slist * head){slist * slow = head , * fast = head;while ( fast && fast -> next ){slow = slow -> next;fast = fast -> next -> next;if ( slow == fast ) break ;}return ! (fast == NULL || fast -> next == NULL);} 

    尋找環的進入點: 當fast按照每次2步,slow每次一步的方式走,發現fast和slow重合,確定了單向鏈表有環路。接下來,讓fast回到鏈表的頭部,重新走,每次步長1,那麼當fast和slow再次相遇的時候,就是環路的入口了。

證明:在fast和slow第一次相遇的時候,假定slow走了n步,環路的入口是在p步,那麼

           slow走的路徑: p+c = n; c為fast和slow相交點 距離環路入口的距離

           fast走的路徑: p+c+k*L = 2*n; L為環路的周長,k是整數

          顯然,如果從p+c點開始,slow再走n步的話,還可以回到p+c這個點。

          同時,fast從頭開始走,步長為1,經過n步,也會達到p+c這點。

          顯然,在這個過程中fast和slow只有前p步驟走的路徑不同。所以當p1和p2再次重合的時候,必然是在鏈表的環路進入點上。

slist * FindLoopPort(slist * head){slist * slow = head, * fast = head;while ( fast && fast -> next ){slow = slow -> next;fast = fast -> next -> next;if ( slow == fast ) break ;}if (fast == NULL || fast -> next == NULL)return NULL;slow = head;while (slow != fast){slow = slow -> next;fast = fast -> next;}return slow;}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.