1. 程式人生 > >LeetCode:160. Intersection of Two Linked Lists

LeetCode:160. Intersection of Two Linked Lists

題目是:

Write a program to find the node at which the intersection of two
singly linked lists begins.

也就是兩個連結串列是從哪一節點開始相交的。

這題不難,但是很有意思,有些解法會讓你感到“哦,還能這樣!”

1.解法一
分別遍歷兩條連結串列,將元素分別放到兩個堆疊。然後比較堆疊頂的兩個元素,如果相同,則都彈出;如果不同,說明之前彈出的元素是要求的。這個方法是最簡單暴力的:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        stack<ListNode*> s1, s2;
        while(headA) {
            s1.push(headA);
            headA = headA->next;
        }
        while(headB) {
            s2.push(headB);
            headB = headB->next;
        }
        ListNode* p = nullptr;
        while (!s1.empty() && !s2.empty() && s1.top() == s2.top()) {
            p = s1.top();
            s1.pop();
            s2.pop();
        }
        return p;
    }
};

2.解法二
這次我們分別遍歷兩條連結串列,求出兩條連結串列的長度。看哪一條長,比如說L1比L2長2,那麼我們先從L1的頭節點前進兩個節點,然後L1從這個節點出發,L2從頭節點出發,每次前進一個。如果它們相交,那麼必然會同時到達那個交點:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        auto len1 = Length(headA);
        auto len2 = Length(headB);
        if (len1 == 0 || len2 == 0)
            return NULL;
        if (len1 > len2)
            headA = moveForward(headA, len1-len2);
        if (len1 < len2)
            headB = moveForward(headB, len2-len1);
        while(headA && headB && headA->val != headB->val) {
            headA = headA->next;
            headB = headB->next;
        }
        return headA;
    }
private:
    int Length(ListNode *head) {
        int len = 0;
        while(head) {
            ++len;
            head = head->next;
        }
        return len;
    }
    ListNode *moveForward(ListNode *head, int k) {
        assert(k > 0);
        while(k-- > 0)
            head = head->next;
        return head;
    }
};

3.解法三
這是最有意思的一個解法。
我們考慮一下解法二,我們是先求出連結串列的長度。但實際上我們關心那個長度的值嗎?我們不關心,我們只是想要確保兩個指標同時到達相交點罷了。
考慮這樣一種方法:我們同時開始遍歷兩個連結串列。一旦一個連結串列到達尾部,那麼我們就把指標指向另一個連結串列的開始節點,繼續遍歷。這樣的話,如果有相交點,那麼會同時到達;如果沒有相交點,那會同時到達連結串列尾端。
程式碼:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if (!headA || !headB)
            return NULL;
        auto p1 = headA, p2 = headB;
        while (p1 != p2) {
            p1 = p1? p1->next : headB;
            p2 = p2? p2->next : headA;            
        }
        return p1;
    }
};

是不是很有意思?
本質上和方法二是同一種思想,都是為了消除連結串列間的長度差,只不過這裡更巧妙一點而已。

到此為止,做過的連結串列題目的較為常用的方法有:
1.一快一慢兩個指標
2.一個先將長度差走完,然後一起走(這裡的方法二)
3.一個連結串列走完再從另一個連結串列開始走(這裡的方法三)