鏈表常見算法題四:鏈表相交及成環問題 返回兩個鏈表相交節點 判斷鏈表是否成環 返回鏈表的入環節點

題目描述:編寫一個程序,找到兩個單鏈表相交的起始節點。

如下面的兩個鏈表:
在這裏插入圖片描述
在節點 c1 開始相交。

示例 1:
在這裏插入圖片描述
輸入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
輸出:Reference of the node with value = 8

輸入解釋:相交節點的值爲 8 (注意,如果兩個列表相交則不能爲 0)。從各自的表頭開始算起,鏈表 A 爲 [4,1,8,4,5],鏈表 B 爲 [5,0,1,8,4,5]。在 A 中,相交節點前有 2 個節點;在 B 中,相交節點前有 3 個節點。

示例 2:
在這裏插入圖片描述
輸入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
輸出:Reference of the node with value = 2

輸入解釋:相交節點的值爲 2 (注意,如果兩個列表相交則不能爲 0)。從各自的表頭開始算起,鏈表 A 爲 [0,9,1,2,4],鏈表 B 爲 [3,2,4]。在 A 中,相交節點前有 3 個節點;在 B 中,相交節點前有 1 個節點。

示例 3:
在這裏插入圖片描述
輸入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
輸出:null

輸入解釋:從各自的表頭開始算起,鏈表 A 爲 [2,6,4],鏈表 B 爲 [1,5]。由於這兩個鏈表不相交,所以 intersectVal 必須爲 0,而 skipA 和 skipB 可以是任意值。
解釋:這兩個鏈表不相交,因此返回 null。

注意:
如果兩個鏈表沒有交點,返回 null.
在返回結果後,兩個鏈表仍須保持原有的結構。
可假定整個鏈表結構中沒有循環。
程序儘量滿足 O(n) 時間複雜度,且僅用 O(1) 內存。
來源:力扣(LeetCode)

解題思路:

我們不確定兩個鏈表的交點,只有先遍歷出兩個鏈表的長度, 當兩個鏈表的長度已知時,我們可以採用右對齊,就是將遍歷鏈表的兩個指針從距末尾相同位置開始向前遍歷,然後每次判斷兩指針的指向是否相等,相等就說明相交,並相等處爲交點;否則,向前遍歷,直到兩指針均走到鏈表末尾還沒有相遇,說明鏈表不相交!

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    if(headA==NULL||headB==NULL)
    {
        return NULL;
    }
    int sizeA=0;
    int sizeB=0;
    struct ListNode*curA=headA;
    struct ListNode*curB=headB;
    while(curA!=NULL||curB!=NULL)
    {
        if(curA!=NULL)
        {
            sizeA++;
            curA=curA->next;
        }
        if(curB!=NULL)
        {
            sizeB++;
            curB=curB->next;
        }
    }
    curA=headA;
    curB=headB;
    if(sizeA>sizeB)//當A鏈表長度大於B鏈表長度
    {
        while(sizeA>sizeB)
        {
            curA=curA->next;
            sizeA--;
        }
    }
    else
    {
        while(sizeA<sizeB)
        {
            curB=curB->next;
            sizeB--;
        }        
    }
    //此時curA和curB距鏈表尾部的距離相同
    while(curA!=NULL&&curB!=NULL)
    {
        if(curA==curB)
        {
            return curA;
        } 
        curA=curA->next;
        curB=curB->next;
    }
    return NULL;
}

題目描述:給定一個鏈表,判斷鏈表中是否有環

爲了表示給定鏈表中的環,我們使用整數 pos 來表示鏈表尾連接到鏈表中的位置(索引從 0 開始)。 如果 pos 是 -1,則在該鏈表中沒有環。

示例 1:

輸入:head = [3,2,0,-4], pos = 1
輸出:true

在這裏插入圖片描述
解釋:鏈表中有一個環,其尾部連接到第二個節點。

示例 2:

輸入:head = [1,2], pos = 0
輸出:true

在這裏插入圖片描述
解釋:鏈表中有一個環,其尾部連接到第一個節點。

示例 3:

輸入:head = [1], pos = -1
輸出:false

在這裏插入圖片描述
解釋:鏈表中沒有環。
來源:力扣(LeetCode)

解題思路:
我在常見鏈表常見算法題三中使用了快慢指針法,這道題同樣採用快慢指針法,鏈表成環,快慢指針會相遇,(可以想象成跑道,一人二倍速,一人原速,很像高中物理的追擊相遇問題,兩人勢必會在跑道上相遇,不過二倍速的人比原速的多跑一圈的距離。)
快指針二倍速遍歷,慢指針一步步向前遍歷,
當快指針和慢指針指向同一節點時,說明快指針已經經過一圈追到慢指針;
當快指針遇到NULL(鏈表尾部)時,說明不成環,直接返回

代碼實現:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) {
    if(head==NULL)
    {
        return false;
    }
    struct ListNode*fast=head;
    struct ListNode*low=head;
    while(fast->next!=NULL&&fast->next->next!=NULL)
    {
        low=low->next;
        fast=fast->next->next;
        if(low==fast)//滿足條件說明快慢指針相遇,有環
        {
            return true;
        }
    }
    return false;//退出while循環時說明鏈表遍歷完,fast遇到NULL,則不成環
}

題目描述:給定一個鏈表,返回鏈表開始入環的第一個節點。 如果鏈表無環,則返回 null

爲了表示給定鏈表中的環,我們使用整數 pos 來表示鏈表尾連接到鏈表中的位置(索引從 0 開始)。 如果 pos 是 -1,則在該鏈表中沒有環。

說明:不允許修改給定的鏈表。

示例 1:
在這裏插入圖片描述
輸入:head = [3,2,0,-4], pos = 1
輸出:tail connects to node index 1

解釋:鏈表中有一個環,其尾部連接到第二個節點。

示例 2:
在這裏插入圖片描述
輸入:head = [1,2], pos = 0
輸出:tail connects to node index 0

解釋:鏈表中有一個環,其尾部連接到第一個節點。

示例 3:
在這裏插入圖片描述
輸入:head = [1], pos = -1
輸出:no cycle

解釋:鏈表中沒有環。

進階:
你是否可以不用額外空間解決此題?

來源:力扣(LeetCode)
代碼實現:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *detectCycle(struct ListNode *head) {
    if(head==NULL)
    {
        return NULL;
    }
    struct ListNode *fast=head;
    struct ListNode *low=head;
    while(fast!=NULL&&fast->next!=NULL)
    {
        low=low->next;
        fast=fast->next->next;
        if(low==fast)
        {
            break;
        }
    }
    if(fast==NULL||fast->next==NULL)
    {
        return NULL;//不是環形,返回
    }
    low=head;//low從頭開始,fast此時和low均和入環節點距離相同
    while(low!=fast)
    {
        low=low->next;
        fast=fast->next;
    }
    return low;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章