※剑指offer系列29:两个链表的第一个公共结点
这个题我拿到的第一个想法确实是遍历两个链表,先遍历第一个链表,在遍历内部再遍历第二个链表。这样两个for循环就可以计算出一样的结点了。剑指offer这一章讲的是算法的效率问题,所以我想这个题一定有更快捷的方法。但是我想不出什么快速的办法,o(╥﹏╥)o看了剑指offer上的解法,主要其实就一句话。两个存在共同结点的链表在共同结点后的结点全是共同结点,因为我们计算的链表是单链表。所以这两个链表的拓扑形状应该是Y型,而不是X型。开始我自己想的时候就是想成了X型,因此想了半天没想到什么方案。有了这个思路之后,想到更快捷的方法就是顺其自然的事情了。
剑指offer上提供了两种做法,首先是从后往前输出,因为链表的后半部分是一样的,但是前面却长短不一。这个剑指offer上的图画的很清楚。可是链表是一个从前向后的结构,没法从后向前输出,这个数据的特点“先进后出”,于是想到了栈。用栈做辅助结构,将链表的数据放进去,然后两个栈同时弹出相同数据,一直到找到最后一个相同的结点。这个思路听起来不错,但是最后剑指offer没有用它,我说一下原因。首先它需要两个辅助的栈的结构,增加了空间的开销来换时间的减少。实在不能说是上策。其次这个思路等于把链表里的数据放进栈里去操作,而这个题目最后返回的是链表的结点,也就是说在栈中找到了第一个相同的结点之后还要再去链表里找到,所以并不简便。
第二种做法是先求出两个链表的长度,然后根据长度把更长的链表截成和短的一样长,然后从两个链表连个指针一起走,直到遇见相同的结点。这个结点无论是思路还是做法都很新颖简便,所以答案也采用了这种方法。
#include<iostream> using namespace std; struct ListNode { int val; struct ListNode *next; /* ListNode(int x) : val(x), next(NULL) { } */ }; class Solution { public: ListNode* FindFirstCommonNode(ListNode* pHead1, ListNode* pHead2) { int len1 = getlen(pHead1); int len2 = getlen(pHead2); int gap=0; //这段不能写成if,else。因为if-else中是程序的一个小子块,这样内部定义的变量外面不能用 ListNode* plong = pHead1;//默认第一个长 ListNode* pshort = pHead2; gap = len1 - len2; if(len2>len1)//如果第二个长,修改值 { ListNode* plong = pHead2; ListNode* pshort = pHead1; gap = len2 - len1; } for (int i = 0; i < gap; i++)//将两个链表拉到一样长 { plong = plong->next; } while (plong != NULL&&pshort != NULL&&plong != pshort)//寻找一样的结点 { plong = plong->next; pshort = pshort->next; } ListNode* commonnode = plong; return commonnode; } int getlen(ListNode* ln) { if (ln == NULL) return 0; int count = 0; while (ln) { count++; ln = ln->next; } return count; } }; int main() { Solution so; ListNode common[2]; common[0].val = 6; common[0].next = &common[1]; common[1].val = 7; common[1].next = NULL; ListNode left[3]; left[0].val = 1; left[0].next = &left[1]; left[1].val = 2; left[1].next = &left[2]; left[2].val = 3; left[2].next = &common[0]; ListNode right[2]; right[0].val = 4; right[0].next = &right[1]; right[1].val = 5; right[1].next = &common[0]; Solution solu; ListNode *node = solu.FindFirstCommonNode(left, right); while (node != NULL) { cout << node->val << " "; node = node->next; } cout << endl; return 0; }
在LeetCode上写的时候,这个题目要求不能修改原来的链表结构,所以衍生出了另一个解法。使用两个指针分别从两个链表的头结点开始查找那个相同的节点,由于两个链表的长度不同所以会有一个链表走完了,另一个链表还有元素。此时将这个走完了的链表的指针指向另一个长链表的头部,而让长链表结束后指向短链表的头部,相当于将这个两个链表走了2次,以均衡两个链表的长度差,这样,就可以在相同的节点相遇。代码如下,没有想到我会继续更新这个系列,最近在不遗余力的找工作,代码语言也变成了python。
class Solution: def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode: a = headA b = headB while a != b: a = a.next if a else headB b = b.next if b else headA return a
时隔三年重新做这道题,还是有新的思路出现。