1. 程式人生 > >帶環連結串列得幾個問題

帶環連結串列得幾個問題

1、判斷一個連結串列是否有環,如果有環,則返回入環的第一個節點,否則返回null

如果一個連結串列有環,遍歷一個連結串列便永遠不會到達null,否則必定會到達null。設定兩個指標,剛開始都指向頭節點,遍歷時,一個指標每次前進一步,我們稱之為慢指標,另一個每次前進兩步,稱之為快指標,這樣,如果連結串列沒有環,快指標必然會到達null,如果有環,兩個指標必然會相交於一點。

下面再來看看如何找到入環的第一個節點,假設從頭節點到入環第一個節點的長度為m,兩個指標第一次相遇時的節點距離環入口點的長度為t,環的長度為n,那麼此時慢指標走的長度為m+t,快指標走的長度為m+t+kn(k=0,1,2...),kn項表示快指標有可能在環內走了幾圈後再與慢指標相遇,這時滿足

m+t+kn = 2(m+t)

簡化得到

m = (k-1)n + n - t

其中n-t項正表示了慢指標走完當前環還需要走的長度,從這裡就可以看出此時如果將快指標放回頭節點,然後讓它每次只走一步,那麼這兩個指標必然會相交,並且相交的交點就是入環點。

ListNode *detectCycle(ListNode *head)
{
        ListNode *fast = head;
        ListNode *slow = 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;
}

2、判斷兩個連結串列是否相交,相交則返回第一個交點

如果兩個連結串列相交,則交點後面部分兩個連結串列是相同的,當然長度也就相同,關鍵在於兩個連結串列不相交的部分,所以可以先求出兩個連結串列的長度len1和len2,如果len1大於len2,則連結串列1先開始往前走,直到從當前節點開始兩個連結串列的長度相同,然後再同時前進,直到兩個節點相同,就是第一個交點。

    int getListLength(ListNode *list)
    {
        ListNode *p = list;
        int len = 0;
        while (p)
        {
            len++;
            p = p->next;
        }

        return len;
    }

    /**
     * @param headA: the first list
     * @param headB: the second list
     * @return: a ListNode
     */
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB)
    {
        if (!headA || !headB)
        {
            return nullptr;
        }
        int lenA = getListLength(headA);
        int lenB = getListLength(headB);

        while (lenA > lenB)
        {
            headA = headA->next;
            lenA--;
        }

        while (lenA < lenB)
        {
            headB = headB->next;
            lenB--;
        }

        while (headA != headB)
        {
            headA = headA->next;
            headB = headB->next;
        }

        return headA;
    }

3、如何判斷兩個有環連結串列是否相交,相交則返回第一個相交點

這個問題可以首先利用問題1分別獲得兩個連結串列的環入口點loop1和loop2,此時如果loop1和loop2相等,則表明兩個連結串列在入環前就已經相交,這個時候可以利用問題2的方法獲得相交點,只是把loop1當作尾節點即可。 如果loop1和loop2不相等呢,這個時候有兩種情況,要麼兩個連結串列不相交,要麼兩個連結串列在環內相交。這個時候可以從loop1出發走一圈,如果在回到loop1的時候一直沒有遇到loop2,那麼表明兩個連結串列不相交,否則就相交,此時返回loop1或者loop2都可以。
ListNode* bothLoop(ListNode* head1, ListNode* loop1, ListNode* head2, ListNode* loop2)
	{
		ListNode* cur1 = nullptr;
		ListNode* cur2 = nullptr;

		if (loop1 == loop2)
		{
			cur1 = head1;
			cur2 = head2;
			int n = 0;
			while (cur1 != loop1)
			{
				n++;
				cur1 = cur1->next;
			}
			while (cur2 != loop2)
			{
				n--;
				cur2 = cur2->next;
			}
			cur1 = n > 0 ? head1 : head2;
			cur2 = cur1 == head1 ? head2 : head1;
			n = abs(n);
			while (n != 0)
			{
				n--;
				cur1 = cur1->next;
			}
			while (cur1 != cur2)
			{
				cur1 = cur1->next;
				cur2 = cur2->next;
			}
			return cur1;
		}
		else
		{
			cur1 = loop1->next;
			while (cur1 != loop1)
			{
				if (cur1 == loop2)
				{
					return loop1;
				}
				cur1 = cur1->next;
			}
			return nullptr;
		}
	}