题目是:
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.一个链表走完再从另一个链表开始走(这里的方法三)