链表带环问题
昨天在网上看到一个挺有趣的东西,链表带环问题,感觉很烧脑,但是可以锻炼我们的思想,
我就闲来无事写一个博客总结一下。
所谓链表带环其实也就是让他最后一个结点的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是我自己写的一个后插结点函数,因为环状的链表无法打印
他们的样子我画图显示一下。
现在我们思考一个问题,如何判断一个链表是否带环呢??
第一种思路,也是我犯过的错误,就是思考着,环状的链表没有最后一个结点,但是直链表有啊,他的
最有一个节点的next成员为NULL判断这个就可以了,但是这里不可以,因为如果它真的是环状链表判断
条件就不会成立,那么就是一个死循环程序会崩溃掉。
第二种思路,这样的方法很值得我们学习,用一个快慢指针,快指针,具体实现我画个图来看
具体代码实现:
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。
第三个问题找到入口点位置?
看到这个问题我的内心是拒绝的,真的。完全没有思路。。。。。。
这个问题确实有点难度,我最后查了查还是找到答案了。
代码实现:
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;
}
}
链表相交问题!!
现在链表带环的大部分问题解决了,接下来你如何判断他们是否相交。 对就是判断两个链表是否相交。
首先我们应该先知道,这里相交的所有情况。
第一种情况两个直链表,这个判断条件很容易就是观察他们的最后一个结点,是否一样,若是一样的那么
他们一定相交。
第二种情况 一个直链表 一个环状链表,记住这两个不可能!! 他们不可能。
因为呢,若是你两个相交你们一定都是环状链表,或者他们都是直链表,不可能是一个直,一个环。
第三种情况也就是两个环状链表相交,这个里面又分为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;
}
还有一种方法,如图:
这个代码实现起来很简单,现在你绝对会写,我就不写了。
大概这么多,这些问题的大致方法,已经全部写完了,自我感觉良好。
哈哈,这就是我这两天学到的知识,并且把它写出来还觉得不错诶。