每日一算法题-判断链表中是否有环

141 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情

一、题目

image.png

struct ListNode {
    int val;
    ListNode *next = nullptr;
    inline ListNode(int x, ListNode* next = nullptr) : val(x), next(next) { }
};

bool hasClosedLoop(ListNode* pHead) {
}

int main(int, char**)
{
    ListNode p1(3);
    ListNode p2(2);
    ListNode p3(0);
    ListNode p4(-4);

    p1.next = &p2;
    p2.next = &p3;
    p3.next = &p4;
    p4.next = &p2;


    cout << (hasClosedLoop(&p1) ? "有闭环" : "没有闭环") << endl;

    return 0;
}

二、分析

根据题意,要想知道是否有闭环,就得判断是否有后面的节点指向前面的节点。
但是链表没有状态量,如何在遍历的时候判断是否是前面的节点呢?\

假设存在闭环,那在遍历的时候,是永远也到不了尾结点的,而是会回到结环的开头。
假设不存在闭环,在遍历的时候,总是会到尾结点的。
从这个区别考虑,假设在操场上,两个人同时跑步,如果有终点,那跑得快的只会跑一次就能到达终点,如果没有终点,跑得快的肯定会重新回到开头追上另一个人。

既然如此,那我们模拟两个遍历,其中一个快一点,一个慢一点,就能像跑步一样,检测到是否有终点了。

三、模拟

  1. 创建两个遍历节点 节点一 跑得快A
    节点二 跑得慢B

  2. 让两个节点跑起来 跑得快A 等于 跑得快A的下一个节点的下一个节点
    跑得慢B 等于 跑得慢B的下一个节点

  3. 判断跑得快是否追上跑得慢 跑的快A 是否等于 跑得慢B

  4. 迭代

四、实现

bool hasClosedLoop(ListNode* pHead) {
    ListNode* rabbit = pHead;
    ListNode* tortoise = pHead;

    while(rabbit != nullptr && rabbit->next != nullptr){
        rabbit = rabbit->next->next;
        tortoise = tortoise->next;
        if(rabbit == tortoise){
            return true;
        }
    }
    return false;
}

五、结言

通过龟兔操场赛跑可以很容易想到这道题的解决办法,在这次的实现中,兔子的速度只是乌龟的两倍,也可以设置快一点,只需要多加一层判空和是否追上即可,程序运行效率也更高。

创作不易,留个赞再走吧!如果对文章内容有任何指正,欢迎评论!