题目:
输入两个链表,找出他们的第一个公共节点。链表节点定义如下:
struct ListNode {
int val;
ListNode *next;
};
分析:
解法一
在第一个链表上顺序遍历每个节点,每遍历到一个节点,就在第二个链表上顺序遍历每个节点,如果在第二个链表上有一个节点和第一个链表上的节点一样,那么这个节点就是我们要找的公共节点如果第一个链表长度为m,第二个链表长度为n,那么显然该方法的时间复杂度为O(mn),一般情况下暴力法都不会是最好的解决办法。
解法二:
我们试着分析有公共节点的两个链表有哪些特点。两个链表如果有公共节点,那么这两个链表从某个节点开始他们的pNext都指向一个节点,由于是单向链表,每个节点只有一个pNext,从第一个公共链表开始,他们之后的所有的节点都是重合的,不可能再出现分叉。则如果两个链表有公共节点,从两个链表的尾部开始向前比较,那么最后一个相同的节点就是我们要找的节点,可问题是单向链表,我们只能从头节点开始遍历按顺序最后到达尾结点,可是我们却想最先开始比较尾结点,这种顺序结构跟我们学过的栈相同,我们把两个链表分别放到两个栈里,这样两个链表的尾结点就出现在栈顶,比较栈顶节点是否相同,如果相等,则弹出栈顶比较下一个栈顶,直到找到最后一个相同的节点。空间复杂度为O(m+n),时间复杂度也是O(m+n)。跟法一相比相当于用空间换取时间。
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
stack<ListNode*>sta1;
stack<ListNode*>sta2;
ListNode *p1 = headA;
ListNode *p2 = headB;
while(p1 != NULL){
sta1.push(p1);
p1 = p1->next;
}
while(p2 != NULL){
sta2.push(p2);
p2 = p2->next;
}
while(!sta1.empty() && !sta2.empty()){
if(sta1.top() == sta2.top()){
p1 = sta1.top();
sta1.pop();
sta2.pop();
}
else
break;
}
return p1;
}
解法三:我们先遍历两个链表得到他们各自的长度,链表1比链表2长x, 那我们再次遍历两个链表的时候先让链表1的指针走x步,然后两个链表的指针再一起走,当两个指针指向的节点相等的时候,我们就找到了公共节点。时间复杂度O(m+n)。
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *p1 = headA;
ListNode *p2 = headB;
int count1 = 0;
int count2 = 0;
int x = 0;
while(p1 != NULL){
count1++;
p1 = p1->next;
}
while(p2 != NULL){
count2++;
p2 = p2->next;
}
p1 = headA;
p2 = headB;
if(count1 >= count2){
while(x != count1 - count2){
p1 = p1->next;
x++;
}
while(p1 != p2){
p1 = p1->next;
p2 = p2->next;
}
}
else {
while(x != count2 - count1){
p2 = p2->next;
x++;
}
while(p1 != p2) {
p1 = p1->next;
p2 = p2->next;
}
}
return p1;
}
但是其实这种解法还有另一种思路,不用先遍历得到两个链表的长度,直接让两个指针从各自链表的头开始遍历,如果其中一个链表的指针为空了,那就让它接着从另一个链表的头开始走,直到两个指针指向的节点相等。即a+c+b=b+c+a。
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *p1 = headA;
ListNode *p2 = headB;
while(p1 != p2)
{
if(p1 == NULL)
{
p1 = headB;
}
else
{
p1 = p1->next;
}
if(p2 == NULL)
{
p2 = headA;
}
else
{
p2 = p2->next;
}
}
return p1;
}