每日一题3:判断单链表是否相交

由于单链表的特殊性,如果某一链表与另一链表相交,那么从相交的节点开始,以后两个链表的每个节点都相同,因此判断两个链表是否相交,只需判断每条链表的最后一个节点是否相同即可!

#include "stdafx.h"
#include <iostream>
using namespace std;

struct list_node
{
    int data;
    list_node* next;
};

list_node* CreateList(int datas[],int n)
{
    if(n < 1) return NULL;
    list_node* head = new list_node;
    head->data = datas[0];
    head->next = NULL;
    list_node* p = head;
    for (int i = 1; i < n; ++i)
    {
        list_node* new_node = new list_node;
        new_node->data = datas[i];
        new_node->next = NULL;
        p->next = new_node;
        p = new_node;
    }
    return head;
}

void DisplayList(list_node* head)
{
    list_node* p = head;
    while(p)
    {
        cout<<p->data<<' ';
        p = p->next;
    }
    cout<<endl;
}

void FreeList(list_node* head)
{
    while(head)
    {
        list_node* p = head->next;
        delete head;
        head = p;
    }
}

bool IsInterseced(list_node* list1,list_node* list2)
{
    list_node* p1 = list1,*p2 = list2;
    while(p1->next)
    {
        p1 = p1->next;
    }
    while(p2->next)
    {
        p2 = p2->next;
    }

    return p1 == p2;
}


int _tmain(int argc, _TCHAR* argv[])
{
    int datas[] = {3,2,0,3,6,8,34,23,9,5,4};
    list_node* list1 = CreateList(datas,11);
    list_node* list2 = CreateList(datas,7);
    DisplayList(list1);
    DisplayList(list2);
    //两条链表没有相交时,可以安全释放每一条链表
    //FreeList(list1);
    //FreeList(list2);
    cout<<IsInterseced(list1,list2)<<endl;
    list_node* list2_tail = list2;
    for (int i = 0; i < 6; ++i)
    {
        list2_tail = list2_tail->next;
    }
    //让第二个链表的最后一个节点的next指针指向第
    //一个链表的第三个节点,如此则两个链表相交
    list2_tail->next = list1->next->next;

    cout<<IsInterseced(list1,list2)<<endl;
    //两条链表相交时,安全释放每一条链表需要做更多的工作,
    //否则,程序一定会崩溃
    //...
    return 0;
}

程序运行截图
接下来,需要求取从哪一个节点开始相交。由于两个链表相交,要么两条链表的长度一样,要么一长一段(将公共部分也算入总长度中),所以我的思路是先将较长的哪一条链表从头开始跳过其比另一链表多出来的节点的个数,到达节点a,此时长链表如果从节点a算起,短链表从头算起,则两条链表的长度一样,这样,只需检查每个对应的节点是否相同,第一相同的节点即为开始相交的节点。修改IsInterSection函数如下:

bool IsInterseced(list_node* list1,list_node* list2,list_node** intersection_node)
{
    *intersection_node = NULL;
    if(list1 == NULL || list2 == NULL) return false;
    list_node* p1 = list1,*p2 = list2;

    int node_count1 = 1;
    while(p1->next)
    {
        ++node_count1;
        p1 = p1->next;
    }

    int node_count2 = 1;
    while(p2->next)
    {
        ++ node_count2;
        p2 = p2->next;
    }
    if(p1 != p2) return false;

    int D_value = 0;

    if(node_count1 >= node_count2)
    {
        p1 = list1;
        p2 = list2;
        D_value = node_count1 - node_count2;
    }
    else
    {
        p1 = list2;
        p2 = list1;
        D_value = node_count2 - node_count1;
    }

    for (int i = 0; i < D_value; ++i)
    {
        p1 = p1->next;
    }
    while(p1 != p2)
    {
        p1 = p1->next;
        p2 = p2->next;
    }

    *intersection_node = p1;

    return true;
}


int _tmain(int argc, _TCHAR* argv[])
{
    int datas[] = {3,2,0,3,6,8,34,23,9,5,4};
    list_node* list1 = CreateList(datas,11);
    list_node* list2 = CreateList(datas,7);
    DisplayList(list1);
    DisplayList(list2);
    //两条链表没有相交时,可以安全释放每一条链表
    //FreeList(list1);
    //FreeList(list2);

    list_node* intersection_node = NULL;
    cout<<IsInterseced(list1,list2,&intersection_node)<<endl;
    list_node* list2_tail = list2;
    for (int i = 0; i < 6; ++i)
    {
        list2_tail = list2_tail->next;
    }
    //让第二个链表的最后一个节点的next指针指向第
    //一个链表的第三个节点,如此则两个链表相交
    list2_tail->next = list1->next->next;
    cout<<IsInterseced(list1,list2,&intersection_node)<<endl;
    cout<<intersection_node->data<<endl;
    //两条链表相交时,安全释放每一条链表需要做更多的工作,
    //否则,程序一定会崩溃
    //...
    return 0;
}

这里写图片描述
从程序运行截图可以看到,相交的节点的数值是0,两个链表中只有一个0,且在第三个节点上,从而验证了算法。
如果已经知道了两条链表各自的长度,是否可以省去前面的两次对链表的遍历呢?不能,相交后两条链表长度的差值未必就是原来两条链表长度的差值,本例中原本的链表长度差值为4,而相交后差值为5.因此,在我看来,开始的两次遍历不可避免,整个算法的复杂度为O(m + n),m与n为原始链表的长度。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值