测试一个链表是否为空表 C语言,【C语言】单链表相关面试题(二)

这篇博客介绍了链表的四个核心操作:1. 删除链表倒数第k个节点;2. 合并两个有序链表;3. 判断链表是否带环并找到环的长度和入口点;4. 判断两条链表是否相交并找到交点。通过设置快慢指针,实现高效解决问题的算法。
摘要由CSDN通过智能技术生成

上篇博客介绍了查询链表中间结点,删除非尾结点,逆序链表三个问题。

有兴趣可点击链接查看:单链表相关面试题(一)

此篇博客将介绍:

1.删除倒数第k个结点(k>1)

2.合并两个有序链表

3.判断链表是否带环,若带环求环的长度,找出环的入口点

4.判断两条链表是否相交,相交则求出交点。

一、删除倒数第k个结点

1.算法思路:

仍然通过设置快慢指针的方法解决,快指针每走一步,让k--,当k<0的时候,让慢指针也开始走,这样,当快指针走到链表尾部的时候,慢指针刚好指向倒数第k个结点。最后删除慢指针指向的那个结点。简单来说就是,让快指针先走k部,慢指针再开始走,这样快慢指针之间总是相差k步,所以,快指针指向空时,慢指针离空k个结点,即慢指针指向倒数第k个结点。

2.代码实现:

void DelKNode(pLinkList pList, int k)

{

assert(pList);

assert(k > 1);

if (pList->pHead == NULL)

{

return;

}

pLinkNode fast = pList->pHead;

pLinkNode slow = pList->pHead;

while (fast)

{

k--;

if (k < 0)

{

slow = slow->next;

}

fast = fast->next;

}

if (k<=0) //为了防止给进去的k不符合要求,超过链表的总长

{

pLinkNode del = NULL;

del = slow->next;

slow->data = del->data;

slow->next = del->next;

free(del);

del = NULL;

}

}

3.测试

test9()

{

LinkList list;

InitLinkList(&list);

PushBack(&list, 1);

PushBack(&list, 2);

PushBack(&list, 3);

PushBack(&list, 4);

PushBack(&list, 5);

PushBack(&list, 6); //构建链表1->2->3->4->5->6

PrintList(&list);

DelKNode(&list, 5); //删除倒数第5个结点

PrintList(&list);

}

4.测试结果

0818b9ca8b590ca3270a3433284dd417.png

二、合并两个有序链表

1.算法思路: 假设顺序是由小到大,先考虑异常情况,当其中一条链表为空则返回另外一条链表。当两条链表都不为空的时候,设置新的头结点,比较两条链表第一个结点的数据域大小,让新的头结点指向数据域小的结点,且用来遍历此条链表的结点指针向后移。接下来循环比较两条链表里的数据域,让新链表的尾结点的指针域指向较小的结点。直到其中一条链表指向空,则让新的链表的尾指针的指针域指向用来另外一条链表的当前指针。可能文字描述不太能懂,我们画图说明一下。

0818b9ca8b590ca3270a3433284dd417.png

0818b9ca8b590ca3270a3433284dd417.png

0818b9ca8b590ca3270a3433284dd417.png

2.代码实现:

pLinkNode merge(pLinkNode list1, pLinkNode list2)

{

pLinkNode Newhead = NULL;

pLinkNode Tail = NULL;

pLinkNode cur1 = list1;

pLinkNode cur2 = list2;

if (list1 == NULL)

{

return list2;

}

else if (list2 == NULL)

{

return list2;

}

else

{

if (cur1->data > cur2->data)

{

Newhead = cur2;

cur2 = cur2->next;

}

else

{

Newhead = cur1;

cur1 = cur1->next;

}

Tail = Newhead;

while (cur1&&cur2)

{

if (cur1->data > cur2->data)

{

Tail->next = cur2;

cur2 = cur2->next;

Tail = Tail->next;

}

else

{

Tail->next = cur1;

cur1 = cur1->next;

Tail = Tail->next;

}

}

if (cur1)

{

Tail->next = cur1;

}

else if (cur2)

{

Tail->next = cur2;

}

}

return Newhead;

}

3.测试

test10()

{

LinkList list; //构建两个链表

LinkList list1;

LinkList list2;

pLinkNode ret = NULL;

pLinkNode l1 = NULL;

pLinkNode l2 = NULL;

InitLinkList(&list);

InitLinkList(&list1);

InitLinkList(&list2);

PushBack(&list, 1);

PushBack(&list, 3);

PushBack(&list, 5);

PushBack(&list, 7);

PushBack(&list1, 2);

PushBack(&list1, 4);

PushBack(&list1, 6);

PrintList(&list);

PrintList(&list1);

l1 = list.pHead;

l2 = list1.pHead;

list2.pHead= merge(l1, l2);//接收新的头结点

PrintList(&list2);

DestroyList(&list);

DestroyList(&list1);

DestroyList(&list2);

}

4.测试结果

0818b9ca8b590ca3270a3433284dd417.png

三、判断链表是否带环,若带环求环的长度,找出环的入口点

1.算法思路: 设置快慢指针,快指针每次走两步,慢指针每次走一步,若链表带环,则快慢指针一定会在环上相遇,我们返回相遇点,若不带环,则快指针或者其指针域会为空,此时我们返回空。

如何计算环的长度:从相遇点遍历一遍,通过计数器获得环的长度。

找环的入口点:

0818b9ca8b590ca3270a3433284dd417.png

2.代码实现:

pLinkNode CheckCycle(pLinkList pList);  //判断链表是否带环

int GetCircleLength(pLinkNode meet);  //若带环求环的长度

pLinkNode FindEntryNode(pLinkList pList, pLinkNode meet);//找出环的入口点

pLinkNode CheckCycle(pLinkList pList)

{

assert(pList);

pLinkNode fast = pList->pHead;

pLinkNode slow = pList->pHead;

while(fast&&fast->next)

{

slow = slow->next;

fast = fast->next->next;

if (fast == slow)

{

return slow;

}

}

return NULL;

}

int GetCircleLength(pLinkNode meet)

{

assert(meet);

int count = 1;

pLinkNode cur = meet->next;

while (cur != meet)

{

count++;

cur = cur->next;

}

return count;

}

pLinkNode FindEntryNode(pLinkList pList, pLinkNode meet)

{

assert(pList);

pLinkNode cur = pList->pHead;

while (cur != meet)

{

cur = cur->next;

meet = meet->next;

}

return cur;

}

3.测试

void test11()

{

LinkList list;

int length=0;

pLinkNode ret = NULL;

pLinkNode meet = NULL;

pLinkNode entry= NULL;

InitLinkList(&list);

PushBack(&list, 1);

PushBack(&list, 2);

PushBack(&list, 3);

PushBack(&list, 4);

PushBack(&list, 5);

PushBack(&list, 6);

ret = Find(&list, 6); //构造环

ret->next = Find(&list, 3); //尾指针域指向3,则环的入口点应该为3

meet=CheckCycle(&list);

if (ret = NULL)

{

printf("链表不带环");

}

else

{

length = GetCircleLength(meet);

printf("环长%d\n", length);

entry = FindEntryNode(&list, meet);

printf("入口点为:%d", entry->data);

}

DestroyList(&list);

}

4.测试结果

0818b9ca8b590ca3270a3433284dd417.png

四、判断两条链表是否相交,相交则求出交点。

1.算法思路: 链表是否相交分为以下几种情况来讨论:

1>链表不带环,不带环时相交,只要遍历两条链表,判断两条链表的尾结点是否相同,相同则说明相交,不同则不相交。相交时,通过计数器的方法求出两条链表的长度,让长的那条链表先走长度的差值个结点,然后指向两条链表指针同时向后遍历,指针相等的时候就是交点。

2>一条带环一条不带环,此种情况一定不相交。

3>两条链表都带环。调用之前的判断链表是否带环函数,判断两条链表的相遇点是否在一个环上,若不在,则不相交,在一个环上则说明相交。相交时,从环上的任意一点断开。参照不带环相交的情况求出相交点。

2.代码实现:

pLinkNode CheckCross(pLinkList list1, pLinkList list2)

{

int count1 = 1;

int count2 = 1;

pLinkNode tmp1 = CheckCycle(list1);//调用现有函数检测链表是否带环

pLinkNode tmp2 = CheckCycle(list2);

if (tmp1 == NULL && tmp2 == NULL) //两条链表都不带环

{

pLinkNode cur1 = list1->pHead;

pLinkNode cur2 = list2->pHead;

pLinkNode pcur1 = list1->pHead;

pLinkNode pcur2 = list2->pHead;

while (cur1->next)

{

cur1 = cur1->next;

count1++;

}

while (cur2->next)

{

cur2 = cur2->next;

count2++;

}

if (cur1 == cur2) //链表相交,求交点

{

int ret = count1 - count2;

if (ret > 0)

{

while (ret--)

{

pcur1 = pcur1->next;

}

}

else

{

ret = (int)fabs(ret);

while (ret--)

{

pcur2 = pcur2->next;

}

}

while (pcur1 != pcur2)

{

pcur1 = pcur1->next; //1 2 3 4 5 6 // 2 4 6 8 10 2 3 4 5 6

pcur2 = pcur2->next;

}

return pcur1;

}

else

return NULL;

}

else if (tmp1 != NULL && tmp2 != NULL) //两条链表都带环

{

pLinkNode cur1 = list1->pHead;

pLinkNode cur2 = list2->pHead;

int length1 = GetCircleLength(tmp1);

int length2 = GetCircleLength(tmp2);

if (length1 != length2)

{

return NULL;

}

else

{

pLinkNode pcur1 = list1->pHead;

pLinkNode pcur2 = list2->pHead;

while (--length1)

{

if (tmp1 == tmp2) //链表相交

{

tmp2->next = NULL;

int count1 = 0;

int count2 = 0;

while (cur1)

{

cur1 = cur1->next;

count1++;

}

while (cur2)

{

cur2 = cur2->next;

count2++;

}

int ret = count1 - count2;

if (ret > 0)

{

while (ret--)

{

pcur1 = pcur1->next;

}

while (pcur1 != pcur2)

{

pcur1 = pcur1->next;

pcur2 = pcur2->next;

}

return pcur1;

}

else

{

ret = (int)fabs(ret);

while (ret--)

{

pcur2 = pcur2->next;

}

while (pcur1 != pcur2)

{

pcur1 = pcur1->next;

pcur2 = pcur2->next;

}

return pcur1;

}

break;

}

tmp1 = tmp1->next;

}

if (length1 == 0)

{

return NULL;

}

}

}

return NULL;

}

3.测试

void test12()

{

LinkList list;

LinkList list1;

pLinkNode ret = NULL;

pLinkNode ret1 = NULL;

pLinkNode meet = NULL;

InitLinkList(&list);

InitLinkList(&list1);

PushBack(&list, 1);

PushBack(&list, 2);

PushBack(&list, 3);

PushBack(&list, 4);

PushBack(&list, 5);

PushBack(&list, 6);

PushBack(&list1, 2);

PushBack(&list1, 4);

PushBack(&list1, 6);

PushBack(&list1, 8);

PushBack(&list1, 10);

ret = Find(&list, 6);

ret->next = Find(&list, 3); //构建环

ret1 = Find(&list1, 10);

ret1->next = Find(&list, 2);//设置相交

meet = CheckCross(&list, &list1);

if (meet != NULL)

{

printf("交点是%d", meet->data);

}

else

{

printf("不相交");

}

}

4.测试结果

0818b9ca8b590ca3270a3433284dd417.png

本文测试用例都调用了链表基础操作里的某些函数。若对此有疑惑的亲可参考博文:

单链表的基本操作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值