题目
在本题中, 单链表可能有环, 也可能无环。 给定两个单链表的头节点 head1和head2, 这两个链表可能相交, 也可能不相交。 请实现一个函数, 如果两个链表相交, 请返回相交的第一个节点; 如果不相交, 返回null 即可。 要求: 如果链表1的长度为N, 链表2的长度为M, 时间复杂度请达到 O(N+M), 额外空间复杂度请达到O(1)
分析
这道题涵盖了链表相交的一系列问题 也是左老师直播课的内容,我们可以拆分成几个问题来分步处理,
- 首先判断单链表是否有环,这里我们给该函数增加一个功能,如果有环则返还该链表的入口结点,没有则返回NULL.
- 判断两个无环链表是否相交,如果相交则返回相交节点,否则返回NULL。
- 判断两个有环链表是否相交,如果相交则返回相交节点,否则返回NULL。
- 如果一个链表有环,一个无环则则不相交返回NULL;
问题一
采用快指针慢指针的方法,具体操作是,定义一个快指针和一个慢指针,从头结点开始,快指针一次走两步,慢指针一次走一步,(如果走到某一步快指针为NULL,说明它到头了,进而说明它没形成环,返回NULL)如果快指针和 慢指针相遇了,说明是有环的,此时让快指针从新回到头结点,并且调整步伐为每一一步,和慢指针同步,当它们再次相遇时,所处的位置就是入环结点,这就是个套路,知道这么用就好了。这部分代码如下:
Node*getNode(Node*head)
{
if (head == NULL)
return NULL;
Node*slow = head->next;
Node*fast = head->next->next;
while (fast != slow) {
if (fast->next == NULL || fast->next->next == NULL)
{
return NULL;
}
fast = fast->next->next;
slow = slow->next;
}
fast = head;
while (fast != slow)
{
fast = fast->next;
slow = slow->next;
}
return fast;
}
问题二
两个无环链表相交,结构就是一个Y字形,我们将两个链表各自遍历一遍,求出长度相减,就知道两个链表的相差长度,让长链表独自走相差出来的长度的距离,然后此时两个链表在同一起跑线了,同时往下走,直到结点相等,也就是他们首次相遇的地方。
Node*noLoop(Node*head1, Node*head2)
{
int n = 0;
Node*n1 = head1;
Node*n2 = head2;
while (n1 != NULL)
{
n++;
n1 = n1->next;
}
while (n2 != NULL)
{
n--;
n2 = n2->next;
}
n1 = n > 0 ? head1 : head2;
n2 = n1 == head1 ? head2 : head1;
n = abs(n);
while (n!=0)
{
n--;
n1 = n1->next;
}
while (n1 != n2)
{
n1 = n1->next;
n2 = n2->next;
}
return n1;
}
问题三
两个有环链表 一共三种形态:
可以通过入口结点是否相等当做边界条件来分析,当loop1=loop2时是图2的情况,跟两个无环链表相交的情况基本一致,有一点注意就是遍历链表求长度时只需要求到入口结点的长度,将下面的环砍掉,剩下的就和问题二一致。
当loop2不等于loop1时有可能是图一也有能是图二,这时让loop1结点往下遍历,并每次查看是否能等于loop2如果有等于说明是图3这时返回loop1或者loop2都可以,如果走了一圈还没碰到lloop2,说明是图1没有相交返回NULL;
Node*loop(Node*head1, Node*loop1, Node*head2, Node*loop2)
{
Node *n1 = NULL;
Node *n2 = NULL;
if (loop1 == loop2)
{
int n = 0;
n1 = head1;
n2 = head2;
while (n1 != loop1)
{
n++;
n1 = n1->next;
}
while (n2 != loop2)
{
n--;
n2 = n2->next;
}
n1 = n > 0 ? head1 : head2;
n2 = n1 == head1 ? head2 : head1;
n = abs(n);
while (n!=0)
{
n--;
n1 = n1->next;
}
while (n1 != n2)
{
n1 = n1->next;
n2 = n2->next;
}
return n1;
}
else {
n1 = loop1->next;
while (n1 != loop1)
{
if (n1 == loop2)
return loop1;
n1 = n1->next;
}
return NULL;
}
}
总体代码如下
// 两个单链表相交的一系列问题.cpp : 定义控制台应用程序的入口点。
//
//【题目】 在本题中, 单链表可能有环, 也可能无环。 给定两个
//单链表的头节点 head1和head2, 这两个链表可能相交, 也可能
//不相交。 请实现一个函数, 如果两个链表相交, 请返回相交的
//第一个节点; 如果不相交, 返回null 即可。 要求: 如果链表1
//的长度为N, 链表2的长度为M, 时间复杂度请达到 O(N + M), 额外
//空间复杂度请达到O(1)
#include "stdafx.h"
#include<iostream>
using namespace std;
struct Node
{
int val;
struct Node*next;
Node(int data) :val(data),next(nullptr){}
};
Node*getNode(Node*head)
{
if (head == NULL)
return NULL;
Node*slow = head->next;
Node*fast = head->next->next;
while (fast != slow) {
if (fast->next == NULL || fast->next->next == NULL)
{
return NULL;
}
fast = fast->next->next;
slow = slow->next;
}
fast = head;
while (fast != slow)
{
fast = fast->next;
slow = slow->next;
}
return fast;
}
Node*noLoop(Node*head1, Node*head2)
{
int n = 0;
Node*n1 = head1;
Node*n2 = head2;
while (n1 != NULL)
{
n++;
n1 = n1->next;
}
while (n2 != NULL)
{
n--;
n2 = n2->next;
}
n1 = n > 0 ? head1 : head2;
n2 = n1 == head1 ? head2 : head1;
n = abs(n);
while (n!=0)
{
n--;
n1 = n1->next;
}
while (n1 != n2)
{
n1 = n1->next;
n2 = n2->next;
}
return n1;
}
Node*loop(Node*head1, Node*loop1, Node*head2, Node*loop2)
{
Node *n1 = NULL;
Node *n2 = NULL;
if (loop1 == loop2)
{
int n = 0;
n1 = head1;
n2 = head2;
while (n1 != loop1)
{
n++;
n1 = n1->next;
}
while (n2 != loop2)
{
n--;
n2 = n2->next;
}
n1 = n > 0 ? head1 : head2;
n2 = n1 == head1 ? head2 : head1;
n = abs(n);
while (n!=0)
{
n--;
n1 = n1->next;
}
while (n1 != n2)
{
n1 = n1->next;
n2 = n2->next;
}
return n1;
}
else {
n1 = loop1->next;
while (n1 != loop1)
{
if (n1 == loop2)
return loop1;
n1 = n1->next;
}
return NULL;
}
}
Node*getIntersectNode(Node*head1, Node*head2)
{
if (head1 == NULL || head2 == NULL)
return NULL;
Node*Loop1 = getNode(head1);
Node*Loop2 = getNode(head2);
if (Loop1 == NULL&&Loop2 == NULL)
{
return noLoop(head1, head2);
}
else if (Loop1 != NULL&&Loop2 != NULL)
{
return loop(head1, Loop1, head2, Loop2);
}
return NULL;
}
int main()
{
Node *head1 = new Node(1);
head1->next = new Node(2);
head1->next->next = new Node(3);
head1->next->next->next = new Node(4);
head1->next->next->next->next = new Node(5);
Node *head2 = new Node(0);
head2->next = new Node(9);
head2->next->next = new Node(8);
head2->next->next->next = head1->next->next->next;
int a = getIntersectNode(head1, head2)->val;
cout << a << endl;
head1 = new Node(1);
head1->next = new Node(2);
head1->next->next = new Node(3);
head1->next->next->next = new Node(4);
head1->next->next->next->next = new Node(5);
head1->next->next->next->next->next = new Node(6);
head1->next->next->next->next->next->next = new Node(7);
head1->next->next->next->next->next->next = head1->next->next->next;//7->4
head2 = new Node(0);
head2->next = new Node(9);
head2->next->next = new Node(8);
head2->next->next->next = head1->next; // 8->2
/*int f = getNode(head2)->val;
cout << f << " " << endl;*/
int b = getIntersectNode(head1, head2)->val;
cout << b<< endl;
head2 = new Node(0);
head2->next = new Node(9);
head2->next->next = new Node(8);
head2->next->next->next = head1->next->next->next->next->next;
int c = getIntersectNode(head1, head2)->val;
cout << c << endl;
return 0;
}