数据结构之单向链表环

一个单向数据链表,如果有形成环,如何判断是否有环,且环的启示位置在哪呢?
笨拙的方法就是申请一个很大的指针数据,用来存储已经遇到的指针地址,并且用来判断是否有重复,不过仔细想想得动态申请多大一块呢?oh, god!真的是很笨的方法。
下面说一个比较有效的方法,算法复杂度不高(O(n))、空间复杂度(O(1))也很低的方法:

跑步

在理解这个算法之前,先和大家讨论一个关于跑步的问题,大家应该都知道:

两个人在环形操场跑步,一个比较快,一个跑的慢一点。同时出发,当两者相遇的时候,一定是快者比慢者多跑了一圈了。

有了这个概念,我们接下来讨论下,快慢指针。

快慢指针

下面我们继续讨论快慢指针:
顾名思义,一个指针跑的快,一个指针跑的慢。两个指针,一个每次移动两位记为pf2, 一个每次移动一位记为pf1.
当pf1 总共走了x 部之后,pf1 、pf2相遇了。环之前的长度为s, 相遇点P。

这里写图片描述

由图可以得到此时的关系

总长度:M;
环长度: N;
pf1 路程: x = s + N - L;
pf2 路程: 2x = s + rN + N - L ;
因为不确定N 到底有多大,所以不能确定在相遇之前,pf2 已经绕了几圈了。
update:
s + rN + N - L = 2 * (s + N -L)
⇒ s = rN + L

结合该公式和图,大家应该可以明白,当两个人速度一样,一个从起始点出发,一个从P点出发,最终的相遇点,一定是环的入口处。
所以我们的程序就知道该如何写了,下面是小弟所写的代码,供大家参考。

创建有环单链表

static int create_link(linklist *header)
{
    int count = 0;
    linklist * pf1 = header;
    linklist * pf = NULL;
    do 
    {
        linklist * pf2 = (linklist *)malloc(sizeof(linklist));
        if (pf2 == NULL)
            return 1;

        pf1->next = pf2;
        pf1 = pf2;
        count ++;
        if (count == 6)
            pf = pf2;
    } while(count < 14);

    pf1->next = pf;
    return 0;
}

查找环入口

static int find_cycle(linklist *header)
{
    linklist *pf2 = header;
    linklist *pf1 = header;
    int count = 0;
    do {
        if (pf2->next == NULL)
            return -1;
        else if (pf2->next == NULL)
            return -1;
        else {
            pf2 = pf2->next->next;
            pf1 = pf1->next;
        }
        if (pf1 == pf2)
            break;
    } while(true);

    pf2 = header;
    do {
        if (pf1 == pf2)
            return count;

        pf1 = pf1->next;
        pf2 = pf2->next;
        count ++;

    } while(true);
}

main 函数和相关define

#include <stdio.h>
#include <malloc.h>

typedef struct link{
    link * next;
} linklist;

int main(void) { 
    int ret = 0;
    linklist * header = (linklist *)malloc(sizeof(linklist));
    if (header == NULL)
        return -1;

    ret = create_link(header);
    if (ret)
        return -1;

    ret = find_cycle(header);
    if (-1 == ret) {
        printf("can not find the cycle: %d", ret);
        return ret;
    } else
        printf("%d", ret);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值