LeetCode 141 Linked List Cycle(循环链表)(HashSet/Linked List)

简介: 版权声明:转载请联系本人,感谢配合!本站地址:http://blog.csdn.net/nomasp https://blog.csdn.net/NoMasp/article/details/51598735 翻译给定一个链表,判断是否有一个循环在其中。
版权声明:转载请联系本人,感谢配合!本站地址:http://blog.csdn.net/nomasp https://blog.csdn.net/NoMasp/article/details/51598735

翻译

给定一个链表,判断是否有一个循环在其中。

跟进:
你可以不用额外的空间来解决这个问题吗?

原文

Given a linked list, determine if it has a cycle in it.

Follow up:
Can you solve it without using extra space?

分析

解决方案 1 哈希表

有个效率不太高的方法,那就是在遍历这个链表的时候看看是否有某个节点曾近访问过。

这就用到了哈希的思想,不论是用map,还是set,甚至是vector,都是可以的。

下面的代码以set为例,如果visited中还没有包含这个节点,就将它添加到visited中。否则,就表示它已经访问过了,这也意味着这里存在一个循环。

bool hasCycle(ListNode *head) {
    set<ListNode*> visited;
    set<ListNode*>::iterator iter;    
    while (head != NULL) {
        iter = visited.find(head);
        if (iter == visited.end()) {
            visited.insert(head);
        } else {
            return true;
        }
        head = head->next;
    }
    return false;
}
updated at 2016/09/20
    public boolean hasCycle(ListNode head) {
        HashSet<ListNode> visited = new HashSet<>();
        while (head != null) {
            if (visited.contains(head)) {
                return true;
            } else {
                visited.add(head);
            }
            head = head.next;
        }
        return false;
    }

哈希表方法的效率:

时间复杂度:O(n)

空间复杂度:O(n)

但是题目的有个更高的要求,那就是不用额外的空间,那么接下来我们就来看另一种方法。

解决方案 2 双指针

想象1,在一条直线的公路上,在速度保持稳定不变的情况下,有着更快的汽车和慢些的自行车,汽车在前,自行车在后,他们肯定是不可能的汇合的。

ListNode *bicycle = head;
ListNode *car = head->next;
while (bicycle != car) { 
// other code
}

之所以要一前一后,只是为了排除一开始就是汇合的情况。当然,如果一开始是同一起点,那就要在开始while循环之前,先让他们动起来。

ListNode *bicycle = head;
ListNode *car = head;
bicycle = bicycle->next;
car = car->next->next;
while (bicycle != car) {
// other code
}

想象2,如果是在一个包含循环的公路上,也就是一直往前行驶着,但终究会回到之前走过的某个路段上。至于在这种公路上,他们为什么会汇合。我们在汇合的那一刻,把时间禁止,再回退。假设此时自行车离刚才设定的汇合点距离是S,汽车离该汇合点距离是2S,但自行车行驶这距离S的路段需要10分钟,而汽车行驶这距离2S的路段也是10分钟,那么10分钟他们不就相遇了吗?(别问我为什么汽车的速度只比自行车快1倍……

ListNode *bicycle = head;
ListNode *car = head->next;
while (bicycle != car) {
   if (car == NULL || car->next == NULL)
      return false;
   bicycle = bicycle->next;
   car = car->next->next;
}

这个双指针法的效率:

空间复杂度:

如果没有循环,那么结束的条件是汽车走完全程,这就取决于路程的长短了。所以是O(n)
如果有循环,需要分开来看:设非循环的长度为N,循环的长度为M,非循环的路程显然都只用走一次,循环的路程可能要走K次。因此也就是O(N) + O(kM),虽然并不能知道k是多少,但走的总路程肯定还是可以用tn来计。t为常亮,n为N和M之和。所以时间复杂度仍然是O(n)

空间复杂度:

这个就很明显的,就是O(1),原文只使用了2个指针节点,也就是个数为常量。

代码

C Plus Plus

bool hasCycle(ListNode *head) {
    if (head == NULL || head->next == NULL)
        return false;
    ListNode *bicycle = head;
    ListNode *car = head->next;
    while (bicycle != car) {
        if (car == NULL || car->next == NULL)
            return false;
        bicycle = bicycle->next;
        car = car->next->next;
    }
    return true;
}

Java

update 2016/09/20
    public boolean hasCycle(ListNode head) {
        if (head == null || head.next == null)
            return false;
        ListNode bicycle = head;
        ListNode car = head.next;
        while (bicycle != car) {
            if (car == null || car.next == null)
                return false;
            bicycle = bicycle.next;
            car = car.next.next;
        }
        return true;
    }
目录
相关文章
【力扣】-- 移除链表元素
【力扣】-- 移除链表元素
170 1
|
9月前
|
算法 Go
【LeetCode 热题100】23:合并 K 个升序链表(详细解析)(Go语言版)
本文详细解析了 LeetCode 热题 23——合并 K 个升序链表的两种解法:优先队列(最小堆)和分治合并。题目要求将多个已排序链表合并为一个升序链表。最小堆方法通过维护节点优先级快速选择最小值,;分治合并则采用归并思想两两合并链表。文章提供了 Go 语言实现代码,并对比分析两种方法的适用场景,帮助读者深入理解链表操作与算法设计。
326 10
LeetCode第二十四题(两两交换链表中的节点)
这篇文章介绍了LeetCode第24题的解法,即如何通过使用三个指针(preNode, curNode, curNextNode)来两两交换链表中的节点,并提供了详细的代码实现。
161 0
LeetCode第二十四题(两两交换链表中的节点)
|
索引
力扣(LeetCode)数据结构练习题(3)------链表
力扣(LeetCode)数据结构练习题(3)------链表
282 0
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
291 6
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
183 6
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
396 2
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
308 3
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
机器学习/深度学习 人工智能 自然语言处理
280页PDF,全方位评估OpenAI o1,Leetcode刷题准确率竟这么高
【10月更文挑战第24天】近年来,OpenAI的o1模型在大型语言模型(LLMs)中脱颖而出,展现出卓越的推理能力和知识整合能力。基于Transformer架构,o1模型采用了链式思维和强化学习等先进技术,显著提升了其在编程竞赛、医学影像报告生成、数学问题解决、自然语言推理和芯片设计等领域的表现。本文将全面评估o1模型的性能及其对AI研究和应用的潜在影响。
377 1
|
索引 Python
【Leetcode刷题Python】从列表list中创建一颗二叉树
本文介绍了如何使用Python递归函数从列表中创建二叉树,其中每个节点的左右子节点索引分别是当前节点索引的2倍加1和2倍加2。
391 7