LeetCode | 141.环形链表

时间:2022-07-23
本文章向大家介绍LeetCode | 141.环形链表,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

这次来写一下 LeetCode 的第 141 题,环形链表。

题目描述

题目直接从 LeetCode 上截图过来,题目如下:

上面的题就是 环形链表 题目的截图,同时 LeetCode 给出了一个函数的定义,然后要求实现 环形链表 的函数体。函数定义如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) {
}

从上面的定义可以看出,是一个单链表。单链表只能顺着节点的指针依次找下去,而不能往回找。

问题分析

这个题目是要判断链表中是否存在环,从题目的图中可以看出,链表的最后一个节点的指针可能会指回它前面的某个节点,从而使链表形成一个环。那到链表中是否存在环,需要程序进行判断。

首先说说没有环的情况,没有环的情况是比较简单的,就是正常的遍历链表,只要链表的某个节点的 next 指针指向了 NULL,那么就说明到达链表的结尾,也就不存在环了。但是,问题既然这样给出了,那么判断链表是否有环才是关键。

关于判断链表是否有环这个问题可以有两种解决方法。

第一种方法是把所经过的每个节点的地址记录下来,然后每次移动指针后判断当前地址是否被记录过,如果记录过,则说明该链表是个环。如下图所示。

上面图中,当前指针指向第一个链表节点,那么就把当前节点的地址记录在地址列表中。然后当 cur 指针依次经过 0x0002、0x0003 和 0x0004 指针后情况是如下图所示。

经过这么一轮的遍历下来,我们已经可以用人眼看出来,已经到了最后一个节点,再遍历下去就形成环了。当指针 cur 指向地址 0x0002 时,这个地址在 地址列表 中是可以找到的,那么就说明该链表是有环的。

这种方法要使用另外一种数据结构,然后通过查表的方法来判断链表是否有环。这样的方式会增加额外的空间。

另一种方式是通过 快慢指针 来判断链表中是否存在环。首先需要定义两个指针,一个快指针(fast)和一个慢指针(slow),快指针每次跳一个节点进行链表的遍历,慢指针则逐个节点进行遍历。在链表中有环的情况下,快指针和慢指针是会相遇的。想想钟表的时针和分针分别一快一慢的前进着,然后还会经常相遇,因为钟表是个环。看下图。

通过上面的图可以看出,通过 快慢指针 可以判断出链表是否有环。

代码实现

判断链表是否有环,我在实现时使用了第二种方法,因为第一种方式需要使用另外的数据结构进行记录并查表。而 快慢指针 这种方式显得简单粗暴。虽然上面又是解释又是画图,其实代码反倒不多,代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) {
    if (!head) {
        return false;
    }

    struct ListNode *fast = head->next;
    struct ListNode *slow = head;

    while (slow && fast && fast->next) {
        if (slow == fast) {
            return true;
        }

        slow = slow->next;
        fast = fast->next->next;
    }

    return false;
}

代码不需要进行注释,因为已经很简单了。

提交结果

在写完 hasCycle 函数体后,点击右下角的 “执行代码”,然后观察 “输出” 和 “预期结果” 是否一致,一致的话就点击 “提交” 按钮。点击 “提交” 按钮后,系统会使用更多的测试用例来测试我们写的函数体,如果所有的测试用例都通过了,那么就会给出 “通过” 的字样,如果没有通过,会给出失败的那一组测试用例,我们继续修改代码。我们以上代码 “提交” 以后的截图如下:

这个题目感觉没有坑在里面,还是比较容易完成的。