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

问题描述:

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


问题分析:

方法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、销毁两个链表的时候,一定要先将相交的部分进行拆开,如果不这样做的话,第一个链表已经释放的结点,第二个链表释放的时候就会报错。这是测试代码应该注意的地方~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值