LeetCode | 141.环形链表(C语言版)

简介: LeetCode | 141.环形链表(C语言版)

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

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

相关文章
|
1月前
【力扣】-- 移除链表元素
【力扣】-- 移除链表元素
35 1
|
20天前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
48 4
|
1月前
|
程序员 C语言
【C语言】LeetCode(力扣)上经典题目
【C语言】LeetCode(力扣)上经典题目
|
1月前
|
存储 缓存 C语言
C语言:链表和数组有什么区别
C语言中,链表和数组是两种常用的数据结构。数组是一种线性结构,元素在内存中连续存储,通过下标访问,适合随机访问且大小固定的情况。链表由一系列不连续的节点组成,每个节点存储数据和指向下一个节点的指针,适用于频繁插入和删除操作的场景,链表的大小可以动态变化。
|
1月前
|
C语言
无头链表再封装方式实现 (C语言描述)
如何在C语言中实现无头链表的再封装,包括创建节点和链表、插入和删除操作、查找和打印链表以及销毁链表的函数。
26 0
|
1月前
|
C语言
C语言链式结构之有头单链表再封装写法
本文介绍了如何使用C语言对有头单链表进行封装,包括节点的创建、链表的初始化、数据的插入和删除,以及链表的打印等功能。
16 1
|
20天前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
39 0
|
1月前
|
索引
力扣(LeetCode)数据结构练习题(3)------链表
力扣(LeetCode)数据结构练习题(3)------链表
78 0
|
1月前
|
C语言
无头链表二级指针方式实现(C语言描述)
本文介绍了如何在C语言中使用二级指针实现无头链表,并提供了创建节点、插入、删除、查找、销毁链表等操作的函数实现,以及一个示例程序来演示这些操作。
24 0
|
5月前
|
存储 SQL 算法
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表