☘️C语言の单链表是否有环问题☘️
Posted 谁吃薄荷糖
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了☘️C语言の单链表是否有环问题☘️相关的知识,希望对你有一定的参考价值。
问题:
给定一个单链表,只给出头指针,如何判断是否存在环?
思路:
使用快慢指针法。
使用两个指针(快指针(fast)、慢指针(slow))从链表头开始遍历此单链表。快指针每次走两步,慢指针每次走一步。
如果有环:快指针和慢指针会相遇;
如果无环:快指针走到单链表尾部,遇到NULL退出。
类比思维:
就是小学数学经常考的龟🐢兔🐰
赛跑追击相遇问题:
小兔子🐰速度快,小乌龟🐢速度为慢。它们从起点一起出发,赛道如果有环,他们一定都会在环里不停地跑,它们总归相遇;如果赛道没有环,小兔子🐰肯定很早就达到终点,比赛结束啦。
代码:
以下图单链表为例
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
int value;
struct node *next;
}LinkNode, *Linklist;
Linklist createList(); ///< 创建单链表
int deleteList(Linklist head, LinkNode* cyclestartnode); ///< 删除单链表
LinkNode* judgeCycle(Linklist head); ///< 判断单链表是否有环
///< 法一
int getLenHead2CycleStart(Linklist head, LinkNode *cycleMeetNode); ///< 获取头结点到环入口结点长度
LinkNode* CycleStart(Linklist head, int len); ///< 根据头结点到环入口结点长度获取环入口结点
///< 法二
LinkNode* getCycleStartNode(Linklist head); ///< 获取环入口结点
int main()
{
Linklist list = NULL; ///< 已知单链表
LinkNode *cycleMeetNode = NULL; ///< 快慢指针相遇结点
LinkNode *cycleStartNode = NULL; ///< 环入口结点
int Len = 0;
int cycleLength = 0;
list = createList();
cycleMeetNode = judgeCycle(list);
if (cycleMeetNode == NULL)
{
printf("No Cycle\\n");
}
else
{
printf("Have Cycle\\n");
///< 获取环起始点
///< 法一
//Len = getLenHead2CycleStart(list, cycleMeetNode);
// cycleStartNode = CycleStart(list, Len);
///< 法二
cycleStartNode = getCycleStartNode(list);
}
deleteList(list, cycleStartNode); ///< 删除单链表, 有环时需要注意环起始点
return 0;
}
/// 创建链表(链表长度,环节点起始位置)
Linklist createList()
{
Linklist head = NULL;
LinkNode *preNode = head;
LinkNode *tailNode = NULL;
int i = 0;
for (i = 0; i < 7; i++)
{
LinkNode *pLinkNode = (LinkNode*)malloc(sizeof(LinkNode));
pLinkNode->value = i;
pLinkNode->next = NULL;
if (preNode == NULL)
{
head = pLinkNode;
preNode = head;
}
else
{
preNode->next = pLinkNode;
preNode = pLinkNode;
}
if (3 == i)
{
tailNode = pLinkNode;
}
}
preNode->next = tailNode;
return head;
}
///< 删除链表
int deleteList(Linklist head, LinkNode* cyclestartnode)
{
int isCycleStartFlag = 0; ///< 环起始点只能被释放空间一次 isCycleStartFlag:0 表示没有被释放过 ,1 表示已经被释放过
LinkNode *nextnode = NULL;
while (head != NULL)
{
nextnode = head->next;
///< 如果是环起始结点
if (head == cyclestartnode)
{
if (1 == isCycleStartFlag)
{
break; ///< 如果第二次遇到环起始点,则停止。此时链表已经删除完毕
}
else
{
isCycleStartFlag = 1; ///< 记录:已经释放过了
}
}
free(head);
head = nextnode;
}
return 0;
}
///< 判断链表是否有环,如有返回相遇结点,无则返回NULL
LinkNode* judgeCycle(Linklist head)
{
LinkNode *fastNode = head;
LinkNode *slowNode = head;
if (head == NULL)
{
return NULL;
}
while (1)
{
if (slowNode->next != NULL && fastNode->next != NULL && fastNode->next->next != NULL)
{
slowNode = slowNode->next; ///< 慢指针:一步
fastNode = fastNode->next->next; ///< 快指针:两步
}
else
{
return NULL;
}
if (fastNode == slowNode)
{
return fastNode;
}
}
return NULL;
}
///< 法一
///< 获取链表头到环连接点的长度
int getLenHead2CycleStart(Linklist head, LinkNode *cycleMeetNode)
{
int lenA = 0;
LinkNode *aNode = head; ///< 从头结点开始
LinkNode *bNode = cycleMeetNode; ///< 从相遇点开始
while (1)
{
aNode = aNode->next;
bNode = bNode->next;
lenA++;
if (aNode == bNode)
break;
}
return lenA;
}
///< 获取环入口结点
LinkNode* CycleStart(Linklist head, int len)
{
if (!head || len <= 0)
{
return NULL;
}
int i = 0;
LinkNode* tmp = head;
for (; i < len; ++i)
{
if (tmp != NULL)
{
tmp = tmp->next;
}
}
return (i == len) ? tmp : NULL;
}
///< 法二
///< 获取环入口结点
LinkNode* getCycleStartNode(Linklist head)
{
LinkNode *fastNode = head;
LinkNode *slowNode = head;
int isHaveCycle = 0;
if (NULL == head)
{
return NULL;
}
///< 快慢指针法判断是否有环,有环则找出相遇点
while (slowNode->next && fastNode->next && fastNode->next->next)
{
slowNode = slowNode->next;
fastNode = fastNode->next->next;
if (slowNode == fastNode)
{
isHaveCycle = 1;
break;
}
}
if (1 == isHaveCycle)
{
slowNode = head;
///< 一个从头结点出发,一个从相遇点触发,再次相遇则为环入口节点
while (slowNode != fastNode)
{
slowNode = slowNode->next;
fastNode = fastNode->next;
}
return slowNode;
}
else
{
return NULL;
}
}
打印信息:
Have Cycle
引经据典
http://blog.sina.com.cn/s/blog_725dd1010100tqwp.html
http://www.cnblogs.com/xudong-bupt/p/3667729.html
以上是关于☘️C语言の单链表是否有环问题☘️的主要内容,如果未能解决你的问题,请参考以下文章
数据结构单链表的介绍和基本操作(C语言实现)保姆级别详细教学