☘️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语言实现单链表基本操作

☀️C语言函数传参の结构体数组篇☀️

☀️C语言函数传参の结构体数组篇☀️

数据结构单链表的介绍和基本操作(C语言实现)保姆级别详细教学

❤️[数据结构]动图详解链表(单链表/双链表……)(动图+实例)建议收藏!❤️

☠️️社死现场の老板来了☠️️小伙,搞C语言嵌入式开发这么久了,还不知道u8u16u32s8s16s32是什么意思啊?