单链表面试题~带环链表的入口点

昨天做了一个小总结,发现单链表这块关于带环链表漏掉了,今天就来总结一下。

要寻找一个单链表的环的入口点,首先得判断一个链表是否带环,那么又怎样判断一个单链表是否带环呢?

首先我们得普及一下环有什么特性,我们知道无环的单链表有首节点和尾节点,那么带环的单链表呢?显然还是有首节点的,但是没有尾节点吧。

如图所示:
这里写图片描述

带环的单链表既然没有尾结点,那么要是遍历的话,就会无限循环,我们就没法通过这样来判断这是带环的,那么我们就要想一个办法让其停下来。

我们知道,运动会三千米赛跑的时候,第一名总会超过最后一名一圈,他们都是从同一个起点开始跑的,最后跑的快的追上了跑的慢的。这就是圈的特性,那么我们是不是也可以使用这一点来判断单链表是否带环。

这里写图片描述

我们定义一个快的指针,让其一步走两个节点,再定义一个慢的节点,一步走一个节点,这样,如果链表带环,那么总有一个地方让这两个指针是相遇的;如果不带环那么快的会先到尾结点。

那么接下来就来实现吧:

typedef struct LinkNode  //节点
{  
    DataType data;  
    struct LinkNode *next;  
}LinkNode,*pLinkNode; 

typedef struct LinkList  //链表
{  
    LinkNode *pHead;  
}LinkList,*pLinkList;  

pLinkNode isCircle(pLinkList plist)  //判断是不是带环链表
{  
    assert(plist);  
    if (NULL == plist->pHead)  
    {  
        printf("链表为空\n");  
        return NULL;  
    }  
    pLinkNode fast = plist->pHead;  
    pLinkNode slow = plist->pHead;  
    while (fast && fast->next)  //如果快的指针先到尾结点,那么不是。(奇偶)
    {  
        fast = fast->next->next;  
        slow = slow->next;  
        if (fast == slow)  //相遇,那么是带环的链表
            return fast;  
    }  
    return NULL;  
}  

判断是不是带环的链表很简单,只要理解了这个过程,没啥问题,那么知道了链表是带环的链表,如何来找带环链表的入口呢?

这涉及不太高深的数学问题,作为数学专业的我,还是很乐意讲解的,首先来看下图:

这里写图片描述

我们先将一些未知量设出来,这样就可以进行运算了:

首先可以列出一个简单的等式:a+r=L ①

那么再根据两个指针来列一个等式: a+n*r+x=(a+x)*2 ②

解释一下第二个式子:左半部分是快的指针相遇前走的路程首先走了一段 a 之后,在进入环中走了 n 圈,又走了x段路程和慢的指针相遇;慢的指针路程因为它的速度是快的指针的一半,所以时间相同的情况下,快的指针走过的路程当然是慢的指针走过的路程的二倍。

然后联合①、②得:a=n*r-x

这样假设n为1时,即——a=r-x,我们要是再定义一个指针,从表头开始遍历,同时,让慢的指针从相遇点开始,那么当他们相遇的时候,就是入口点的地方。

下面我们来看代码:

pLinkNode firstCrossNode(pLinkList plist)  
{  
    assert(plist);  
    if (NULL == plist->pHead)  
    {  
        printf("链表是空\n");  
        return NULL;  
    } 

    pLinkNode ret = isCircle(plist);  //接收相遇点的指针

    if (ret == NULL)  
    {  
        printf("链表不带环\n");  
        return NULL;  
    }   

    pLinkNode start = plist->pHead;  //从表头开始
    pLinkNode slow = ret;  //从相遇点开始
    while (start)  
    {  
        start = start->next;  
        slow = slow->next;  
        if (start == slow)  
            return start;  //返回入口点的指针
    }  
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值