您的位置:首页 > 职场人生

【剑指offer】面试题37-两个单链表的第一个公共结点

2017-01-22 20:37 381 查看

问题描述:

输入两个链表,找出链表的第一个公共结点。

问题分析:

方法1:粗暴的办法。

从一个链表的第一个结点开始,再另一个链表上查找是否是交点(指向结点的指针一样),这样的话,如果一个链表的长度是m,另一个链表的长度是n,则时间复杂度是O(mn)。

方法2:

我们知道链表相交,不仅仅只是结点里的data值一样,next指针也是一样的,也就是说,如果两个链表从某一个结点处开始相交,那么之后两个链表一定是相交的。即就是,链表的相交模型不会是X型,而是Y型。如图。



所以我们可以从后向前找第一个交点,但是这里是单链表,只能从后向前遍历,所以这里采用数据结构stack,将结点的信息存储在栈中,从栈顶开始比较两个结点,如果不一样,则就说明不相交,如果一样,记住当前结点,将两个栈的栈顶元素出栈,继续比较下一个结点,就这样,直到找到不一样的结点,那么上一个结点就是第一个公共结点。但是这种办法需要两个辅助栈,不是最好的办法。

方法3:

求出两个链表的长度,让长的链表先走两个链表长度之差的绝对值步,然后两个链表一起走,直到找到第一个公共结点。这就是所谓的快慢指针法。

方法4:

如果两个单链表相交,将链表的尾和其中任何一个链表的头相接,就会构成一个环,求出环的入口就是连个链表的第一个公共结点;如果链表不相交,就不会构成环。判断链表相不相交的办法,就是直接看他们的最后一个结点是否一样。这种办法之后的文章(判断链表是否带环以及环的入口)会实现,这里暂时不实现。

代码实现(包括测试代码):

#include<iostream>
using namespace std;
struct ListNode
{
int _data;
ListNode* _pNext;
ListNode(int x = 0)
:_data(x)
,_pNext(NULL)
{}
};
ListNode* Create(int arr[],int n)
{
ListNode* head = new ListNode(arr[0]);
ListNode* prev = head;
ListNode* cur = prev;
for(int i = 1; i < n; ++i)
{
cur = new ListNode(arr[i]);
prev->_pNext = cur;
prev = cur;
}
return head;
}
size_t GetLength(ListNode* head)
{
size_t len = 0;
ListNode* cur = head;
while(cur)
{
++len;
cur = cur->_pNext;
}
return len;
}
ListNode* GetFirstCommonNode(ListNode* head1,ListNode* head2)
{
size_t len1 = GetLength(head1);
size_t len2 = GetLength(head2);
ListNode* pLong = head1;
ListNode* pShort = head2;
int diff = len1 - len2;
if(diff < 0)//第一个链表比第二个链表短
{
pLong  = head2;
pShort = head1;
diff = len2 - len1;
}
//让长的链表先走diff步
for(int i = 0; i < diff; ++i)
{
pLong = pLong->_pNext;
}
//两个链表一起向后走找第一个交点
while(pLong != NULL && pShort != NULL && pLong != pShort)
{
pLong = pLong->_pNext;
pShort = pShort->_pNext;
}
if(pLong && pShort == 0)//没有找到公共结点
return NULL;
else
return pShort;
}
void Destroy(ListNode* head)
{
ListNode* cur = head;
ListNode* del = NULL;
while(cur)
{
del = cur;
cur = cur->_pNext;
delete del;
}
}
ListNode* FindNode(ListNode* head,int data)
{
ListNode* cur = head;
while(cur)
{
if(cur->_data == data)
return cur;
cur = cur->_pNext;
}
return NULL;
}
int main()
{
int arr1[] = {3,4,5,6,7,8};
int arr2[] = {8,7};
//创建链表
ListNode* head1 = Create(arr1,sizeof(arr1)/sizeof(arr1[0]));
ListNode* head2 = Create(arr2,sizeof(arr2)/sizeof(arr2[0]));
//创造交点
ListNode* Node1 = FindNode(head1,6);
ListNode* Node2 = FindNode(head2,7);
Node2->_pNext = Node1;
ListNode* ret = GetFirstCommonNode(head1,head2);
if(ret)
cout<<"交点的值是:"<<ret->_data <<endl;
else
cout<<"没有交点"<<endl;
Destroy(head1);
Node2->_pNext = NULL;
Destroy(head2);
system("pause");
return 0;
}


总结:

1、在使长的链表先走diff步的时候,使用pLong来记录长的链表,pShort记录短的链表。如果不这样的话,我们需要判断diff是否大于0,如果是,head1先走diff步,否则head2先走diff绝对值步。所以代码中的处理相当好~~

2、销毁两个链表的时候,一定要先将相交的部分进行拆开,如果不这样做的话,第一个链表已经释放的结点,第二个链表释放的时候就会报错。这是测试代码应该注意的地方~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息