测试一个链表是否为空表 C语言,链表面试题:判断链表是否相交(c语言)

一、问题描述:

判断两个链表是否相交,若相交返回交点,否则返回空。(这里假设链表不带环)

40302b9af194cba98e02ef706617ebda.png

由上图我们可以得出链表相交的两个结论:

1.两链表相交,第一个相同的结点为链表的交点;

2.两链表相交,从交点向后的所有结点都相同(合二为一)。

二、解决思路

方法一:直观法

思路:依次判断第一个链表中的每个结点是否在第二个链表中出现。时间复杂度:O(Length(List1)*Length(List2))

链表相关定义:

typedef int DataType;

typedef struct ListNode

{

DataType data;

struct ListNode* next;

}ListNode,*pListNode;

typedef struct LinkList

{

pListNode pHead;

}LinkList,*pLinkList;

代码实现:

pListNode CrossOfList(pLinkList plist1, pLinkList plist2)

{

pListNode cur1 = NULL;

pListNode cur2 = NULL;

assert(plist1 && plist2);

cur1 = plist1->pHead;

while(cur1)

{

cur2 = plist2->pHead;

while(cur2)

{

if(cur1 == cur2)

{

return cur1;

}

cur2 = cur2->next;

}

cur1 = cur1->next;

}

return NULL;

}

方法二:构造环法

思路:找到List1的最后一个结点,将其next指向List2的表头。这样若链表相交则会构成环,不相交的话就不会构成环。时间复杂度:O(Length(h1)+Length(h2))

13006d12e8dc72f1b63843c91f088559.png

那么问题就转换为检查链表是否带环的问题:

(1)判断相交:

我们利用快慢指针的方法,定义两个指针都指向List1,然后slow指针每次走一步,fast指针每次走两步。若他们两可以相遇,则链表有环,否则fast为NULL时仍未相遇则链表不带环。

(2)找入口点:

dce92d7a8e2391158f3ac7823ea8c7d3.png

设环周长为r,slow指针走m步,则fast指针走2m步,由此可得:

这里首先要证明一下,当slow指针进入环后,fast指针与slow指针最多差r-1,fast每次比slow多走一步,最多走r-1次就可以追上slow指针,这时slow指针走了r-1步,所以当slow指针进入环之后,必定再一圈内被fast指针追上。

slow指针:  m = a+x            (1)

fast指针: 2m = a+x+nr       (2)

由(2)-(1)得:m = nr      (3)

由(3)和(1)得:a+x = nr (4)

由(4)推出:a = (n-1)r + (r-x)

通过这个式子,我们可以得到从链表开始到环入口点的距离为从交点到环入口点的距离(由图中?标出)加上环的整数倍。

通过上面的分析:我们可以在链表头设一个指针find,让find和处在交点处的slow指针每次都各走一步,当find指针与slow指针相遇,则相遇点为环的入口点,即链表的相交点。

代码实现:

pListNode CrossOfList2(pLinkList plist1, pLinkList plist2)

{

pListNode fast = NULL;

pListNode slow = NULL;

pListNode cur = NULL;

assert(plist1 && plist2);

cur = plist1->pHead;

fast = plist1->pHead;

slow = plist1->pHead;

while(cur->next) //找list1的尾结点

{

cur = cur->next;

}

cur->next = plist2->pHead; //将list1的尾连在list2的头上

while(fast && fast->next)

{

fast = fast->next->next;

slow = slow->next;

if(fast == slow) //链表有环

{

pListNode find = plist1->pHead;

while(find != slow) //找环入口点

{

find = find->next;

slow = slow->next;

}

return find;

}

}

return NULL;

}

方法三:找尾结点法

思路:若List1和List2相交,则他们的尾结点必定相同。

此方法可以判断链表是否相交,但却不能找出链表的交点。

时间复杂度:O(Length(h1)+Length(h2))

通过两个指针直接找到两个链表的尾结点,然后进行比较就可以了。

这种方法比较简单,这里关于此方法就不做过多介绍了。

三、测试:

void Test()

{

LinkList list1;

LinkList list2;

pListNode cur = NULL;

pListNode ret = NULL;

InitLinkList(&list1);

InitLinkList(&list2);

PushBack(&list1, 1);

PushBack(&list1, 2);

PushBack(&list1, 3);

PushBack(&list1, 4);

PushBack(&list1, 5);

PrintLinkList(&list1);

PushBack(&list2, 11);

PushBack(&list2, 22);

PushBack(&list2, 33);

//构建相交链表cur = list2.pHead;

while(cur->next)

{

cur = cur->next;

}

cur->next = Find(&list1, 2);

PrintLinkList(&list2);

ret = CrossOfList2(&list1, &list2);

if(ret == NULL)

{

printf("No Cross\n");

}

else

{

printf("CrossPoint:%d\n", ret->data);

}

//DestoryLinkList(&list1);

//DestoryLinkList(&list2);

}

谢谢阅读,如有问题欢迎提出!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值