c语言使用链表 常犯的错误,C语言—链表面试题之带环问题的总结

链表带环问题

昨天在网上看到一个挺有趣的东西,链表带环问题,感觉很烧脑,但是可以锻炼我们的思想,

我就闲来无事写一个博客总结一下。

所谓链表带环其实也就是让他最后一个结点的next成员指向自己第一个元素的,好开始!

首先我们创建一个带环的链表。这个自己写一个链表的后插函数,然后找到最后一个成员

的next成员的地址,将它的内容改为你的头结点的地址,这样的一个环就成功了。

pList plist = NULL;

pNode cur = plist;

PushBack(&plist, 1);

PushBack(&plist, 2);

PushBack(&plist, 3);

PushBack(&plist, 4);

PushBack(&plist, 5);

PushBack(&plist, 6);

PushBack(&plist, 7);

PushBack(&plist, 8);

cur = plist;

while (cur->next)

{

cur = cur->next;

}

cur ->next= plist;

pList plist2 = NULL;

PushBack(&plist2, 1);

PushBack(&plist2, 2);

PushBack(&plist2, 3);

PushBack(&plist2, 4);

PushBack(&plist2, 5);

void PushBack(pList* pplist, DataType d)

{

pNode cur = *pplist;

assert(pplist != NULL);

if (NULL == *pplist)

{

pNode newnode = buy(d);

*pplist = newnode;

return;

}

pNode newnode = buy(d);

while (cur->next)

{

cur = cur->next;

}

cur->next = newnode;

return;

}

这个是我创建的两个链表,pushBack是我自己写的一个后插结点函数,因为环状的链表无法打印

他们的样子我画图显示一下。

0818b9ca8b590ca3270a3433284dd417.png

现在我们思考一个问题,如何判断一个链表是否带环呢??

第一种思路,也是我犯过的错误,就是思考着,环状的链表没有最后一个结点,但是直链表有啊,他的

最有一个节点的next成员为NULL判断这个就可以了,但是这里不可以,因为如果它真的是环状链表判断

条件就不会成立,那么就是一个死循环程序会崩溃掉。

第二种思路,这样的方法很值得我们学习,用一个快慢指针,快指针,具体实现我画个图来看

0818b9ca8b590ca3270a3433284dd417.png

具体代码实现:

pNode checkCylen(pList* pplist)

{

pNode fast = *pplist;

pNode slow = *pplist;

assert(pplist);

while (fast && fast->next)

{

fast = fast->next->next;

slow = slow->next;

if (fast == slow)

{

printf("yes\n");

return slow;

}

}

printf("no\n");

return NULL;

}

第二个问题,如果它带环,你能否求出他的长度?

上面知道的条件到这里都是我们知道的条件,长度怎么想呢?

我们知道我们只需要在环里的一个结点位置,就可以通过遍历知道找到这个结点地址并求出长度了。

上面我们知道一个快慢指针相交的节点,所以长度很容易啊。

代码实现:

int GetcircleLength(pNode meet)

{

pNode cur = meet;

int count = 0;

while (1)

{

cur = cur->next;

count++;

if (cur == meet)

{

return count;

}

}

}

这里的 pNode meet就是上面那个函数返回来的slow。

第三个问题找到入口点位置?

0818b9ca8b590ca3270a3433284dd417.png

看到这个问题我的内心是拒绝的,真的。完全没有思路。。。。。。

这个问题确实有点难度,我最后查了查还是找到答案了。

0818b9ca8b590ca3270a3433284dd417.png

代码实现:

pNode GetCycleEntryNode(pList plist, pNode meetNode)

{

pNode cur = meetNode;

pNode cur2 = plist;

//前提它已经是一个环状链表

//plist和meetNode分别是链表的头结点位置和快慢指针相遇点

if (plist == NULL)

{

return NULL;

}

if (meetNode == NULL)

{

return NULL;

}

while (1)

{

if (cur == cur2)

{

return cur;

}

cur = cur->next;

cur2 = cur2->next;

}

}

链表相交问题!!

现在链表带环的大部分问题解决了,接下来你如何判断他们是否相交。 对就是判断两个链表是否相交。

首先我们应该先知道,这里相交的所有情况。

0818b9ca8b590ca3270a3433284dd417.png

第一种情况两个直链表,这个判断条件很容易就是观察他们的最后一个结点,是否一样,若是一样的那么

他们一定相交。

第二种情况 一个直链表 一个环状链表,记住这两个不可能!! 他们不可能。

因为呢,若是你两个相交你们一定都是环状链表,或者他们都是直链表,不可能是一个直,一个环。

第三种情况也就是两个环状链表相交,这个里面又分为2种小情况,相交但入口点不同,相交入口点相同

这些情况我只写了直链表的代码,环状后面可能会补充~

代码实现:

int checkCross(pList list, pList list2)

{

pNode cur1 = list;

pNode cur2 = list2;

if (NULL == list || NULL == list2)

{

return 0;

}

if (checkCylen(list) != NULL &&checkCylen(list2) == NULL)//一个带环一个不带环

{

return NULL;

}

if (checkCylen(list) == NULL && checkCylen(list2) != NULL)//一个带环一个不带环

{

return NULL;

}

if (checkCylen(list) == NULL && checkCylen(list2) == NULL) //两个都不带环的

{

while (cur1->next)

{

cur1 = cur1->next;

}

while (cur2->next)

{

cur2 = cur2->next;

}

if (cur1 == cur2)

{

return 1;

}

return 0;

}

}

但是我现在有一个问题,如果两个不带环的链表相交求交点。

你想想应该怎么搞呢?

这里有好多方法,比如找到倒数第一个结点的位置,然后往前走,直到发现分叉口。(不要告诉我

单链表不能从后往前,人是活的,等下我会附上代码)但是这种方法函数实现太麻烦不推荐使用,

第二种方法很实用的。

这是一个删除倒数第几个结点的函数,里面的方法是可以帮助实现我上面说到的从后往前走。

pNode DelKNode(pList *pplist, int k)

{

pNode fast = *pplist;

pNode slow = *pplist;

pNode del = NULL;

assert(pplist);

if (*pplist == NULL)

{

printf("此链表为空\n");

}

while (fast->next)

{

--k;

if (k <= 0)

{

slow = slow->next;

}

fast = fast->next;

}

del = slow->next;

slow->data = slow->next->data;

slow->next = slow->next->next;

free(del);

return NULL;

}

还有一种方法,如图:

0818b9ca8b590ca3270a3433284dd417.png

这个代码实现起来很简单,现在你绝对会写,我就不写了。

大概这么多,这些问题的大致方法,已经全部写完了,自我感觉良好。

哈哈,这就是我这两天学到的知识,并且把它写出来还觉得不错诶。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值