每日一题——力扣141. 环形链表(举一反三+思想解读+逐步优化)


一个认为一切根源都是“自己不够强”的INTJ

个人主页:用哲学编程-CSDN博客
专栏:每日一题——举一反三
Python编程学习
Python内置函数

Python-3.12.0文档解读

目录

我的写法

专业点评

时间复杂度分析

空间复杂度分析

总结

我要更强

方法2:使用哈希表

方法3:修改节点结构(不推荐)

方法4:反转链表(不推荐)

结论

哲学和编程思想

快慢指针 (Floyd's Cycle-Finding Algorithm)

哈希表法

修改节点结构法

反转链表法

举一反三

技巧1:双指针技术

技巧2:哈希表技术

技巧3:状态标记法

技巧4:反转操作

技巧5:递归与迭代

技巧6:分治法

技巧7:贪心算法

技巧8:动态规划

技巧9:回溯法

技巧10:模拟法


题目链接


我的写法

/** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */bool hasCycle(struct ListNode *head) {// 定义两个指针,slow 和 quick,初始都指向链表的头节点struct ListNode *slow = head;struct ListNode *quick = head;// 如果链表为空或只有一个节点,则不可能存在环,返回 falseif (!head || !head->next) {return false;}// 使用 for 循环来遍历链表,循环条件是 slow 和 quick 及其下一个节点都不为 NULLfor (; slow && slow->next && quick && quick->next && quick->next->next;) {// slow 指针每次移动一步slow = slow->next;// quick 指针每次移动两步quick = quick->next->next;// 如果 slow 和 quick 指针相遇,则说明链表中存在环if (slow == quick) {return true;}}// 如果循环结束后没有相遇,说明链表中不存在环,返回 falsereturn false;
}

 

这段代码实现了一个检测单链表是否存在环的功能,采用的是经典的快慢指针(Tortoise and Hare)算法。下面是对这段代码的专业点评及其时间复杂度和空间复杂度分析。

专业点评

  1. 代码结构清晰:
    • 代码结构合理,使用变量命名清晰,便于理解。
    • 初始检查部分有效地处理了特殊情况(空链表或只有一个节点的链表)。
  2. 循环条件:
    • 循环条件健全,确保在链表遍历过程中不会发生空指针引用错误。
    • for 循环中使用多个条件确保 slow 和 quick 及其下一个节点都有效。
  3. 快慢指针逻辑:
    • 快指针每次移动两步,慢指针每次移动一步,有效地检测链表中的环。
    • 如果存在环,快慢指针必定会相遇。
  4. 代码简洁性:
  • 代码简洁,逻辑明确,没有冗余的操作。

时间复杂度分析

  • 最坏情况:在没有环的情况下,快指针和慢指针遍历链表的每个节点。
    • 在这种情况下,快指针遍历节点数大约为 n/2,慢指针遍历节点数为 n,其中 n 是链表的节点数。
    • 因此,时间复杂度为 O(n)。
  • 最优情况:在存在环的情况下,快慢指针会在环内相遇。
  • 最多需要 O(n) 的时间,因为快指针和慢指针最终会在环内相遇。

综合来看,时间复杂度为 O(n)。

空间复杂度分析

  • 该算法只使用了两个额外的指针变量 slow 和 quick,不依赖于链表的大小。
  • 因此,空间复杂度为 O(1)。

总结

  • 优点:
    • 时间复杂度为 O(n),在所有链表节点遍历完毕前即可得出结果。
    • 空间复杂度为 O(1),只用到了常数额外空间。
    • 代码简洁清晰,易于理解。
  • 缺点:
  • 无明显缺点,该算法是检测链表环的经典方法,几乎是最优解。

改进建议

  • 从代码功能和效率来看,这段代码已经相当高效且简洁,没有明显需要改进的地方。如果需要进一步优化,可以考虑在注释方面增加更详细的解释,以便于其他开发者更好地理解代码逻辑。

代码

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/
bool hasCycle(struct ListNode *head) {// 定义两个指针,slow 和 quick,初始都指向链表的头节点struct ListNode *slow = head;struct ListNode *quick = head;// 如果链表为空或只有一个节点,则不可能存在环,返回 falseif (!head || !head->next) {return false;}// 使用 for 循环来遍历链表,循环条件是 slow 和 quick 及其下一个节点都不为 NULLfor (; slow && slow->next && quick && quick->next && quick->next->next;) {// slow 指针每次移动一步slow = slow->next;// quick 指针每次移动两步quick = quick->next->next;// 如果 slow 和 quick 指针相遇,则说明链表中存在环if (slow == quick) {return true;}}// 如果循环结束后没有相遇,说明链表中不存在环,返回 falsereturn false;
}

这段代码实现了高效的环检测功能,是生产环境中常用的方法之一。


我要更强

在链表检测环的问题中,代码已经使用了快慢指针(Floyd’s Cycle-Finding Algorithm),这已经是时间复杂度 (O(n)) 和空间复杂度 (O(1)) 的最优解。因此,从理论上讲,无法进一步优化这两个指标。

不过,为了完整性和拓展视野,提供几个不同的方法,即使它们在时间和空间复杂度上未必能优于快慢指针法。下面将详细介绍这些方法并提供相应的代码和注释。

方法2:使用哈希表

利用哈希表记录每个访问过的节点,如果再次遇到已经访问过的节点,则说明存在环。时间复杂度为 (O(n)),空间复杂度为 (O(n))。

#include <stdbool.h>
#include <stdlib.h>// Definition for singly-linked list.
struct ListNode {int val;struct ListNode *next;
};// Definition of the hash table node.
struct HashNode {struct ListNode *node;struct HashNode *next;
};// Simple hash function for the hash table.
unsigned int hash(struct ListNode *node) {return ((unsigned long)node) % 1024;
}bool hasCycle(struct ListNode *head) {if (!head) return false;struct HashNode *hashTable[1024] = { NULL };while (head) {unsigned int hashIndex = hash(head);struct HashNode *entry = hashTable[hashIndex];while (entry) {if (entry->node == head) {return true; // Cycle detected}entry = entry->next;}struct HashNode *newEntry = (struct HashNode *)malloc(sizeof(struct HashNode));newEntry->node = head;newEntry->next = hashTable[hashIndex];hashTable[hashIndex] = newEntry;head = head->next;}return false; // No cycle detected
}

方法3:修改节点结构(不推荐)

这种方法临时修改节点结构,标记访问过的节点。虽然时间复杂度和空间复杂度均为 (O(n)),但破坏了链表的原始结构。

// Definition for singly-linked list.
struct ListNode {int val;struct ListNode *next;
};bool hasCycle(struct ListNode *head) {while (head) {// 如果当前节点的值已经被标记,则说明存在环if (head->val == -1) {return true;}// 标记当前节点head->val = -1;head = head->next;}return false; // 如果遍历完成后没有发现环
}

方法4:反转链表(不推荐)

这种方法通过反转链表来检测环,但会破坏链表的结构,且复杂度较高。

// Definition for singly-linked list.
struct ListNode {int val;struct ListNode *next;
};struct ListNode* reverseList(struct ListNode* head) {struct ListNode* prev = NULL;struct ListNode* curr = head;while (curr) {struct ListNode* nextTemp = curr->next;curr->next = prev;prev = curr;curr = nextTemp;}return prev;
}bool hasCycle(struct ListNode* head) {if (!head || !head->next) {return false;}struct ListNode* reversedHead = reverseList(head);// 如果反转后的链表头指针等于原链表头指针,说明有环return reversedHead == head;
}

结论

在检测链表环的问题上,快慢指针算法已经是最优解,具有 (O(n)) 时间复杂度和 (O(1)) 空间复杂度。其他方法虽然也能实现环检测,但在性能和实用性上不如快慢指针高效。


哲学和编程思想

在解决链表环检测问题的过程中,可以看到一些重要的哲学和编程思想。这些思想不但帮助我们理解和解决问题,还可以提升编写高效、可靠代码的能力。

快慢指针 (Floyd's Cycle-Finding Algorithm)

哲学思想:

  1. 对称性与均衡:通过两个不同步的指针(快指针和慢指针),我们可以同时观察链表的不同部分。这个方法体现了对称性和均衡的哲学思想。
  2. 渐进性:慢指针逐步遍历链表每一步,而快指针以更快的速度前进。这个思想类似于在哲学中逐步接近真理的过程。

编程思想:

  1. 双指针技术:利用两个指针以不同速度遍历数据结构。
  2. 空间效率:仅使用固定数量的额外空间,使算法在空间复杂度上最优。

哈希表法

哲学思想:

  1. 记忆与记录:通过记录已经访问过的节点,我们可以检测到重复。这个方法类似于人类记忆和识别重复事件的过程。
  2. 关联性:哈希表的使用体现了关联性哲学,通过快速定位和匹配记录来检测环的存在。

编程思想:

  1. 哈希表:利用哈希表的快速查找特性来记录和检测节点。
  2. 时间效率:虽然增加了空间复杂度,但提高了时间效率。

修改节点结构法

哲学思想:

  1. 变更与保留:通过临时变更节点的值或结构来标记已访问的节点,体现了变更与保留的哲学思想。
  2. 牺牲与利益:在一定程度上牺牲链表的完整性来换取检测环的简便性。

编程思想:

  1. 状态标记:通过修改数据结构的状态来记录访问情况。
  2. 时间效率:虽然这种方法简单直接,但不推荐因为它破坏了原始数据结构。

反转链表法

哲学思想:

  1. 反转与复原:通过反转链表实现检测环,体现了反转与复原的哲学思想。
  2. 对立统一:链表的反转与检测过程展现了对立统一的哲学观点。

编程思想:

  1. 反转操作:利用链表反转的技巧来检测环。
  2. 结构变化:尽管能实现目标,但会破坏链表结构,因此在实际应用中不推荐。

总结

这些方法展示了不同的哲学和编程思想,包括对称性、记忆与记录、变更与保留、反转与复原等。在解决问题时,理解这些思想不仅帮助选择适当的算法,还可以提高编程能力和对问题的深刻理解。


举一反三

为了帮助你在编程中举一反三,下面提供了一些基于哲学和编程思想的技巧。这些技巧将帮助你在不同情境下应用这些思想:

技巧1:双指针技术

哲学思想:对称性与均衡

应用场景:

  • 排序数组中的问题,如两数之和、三数之和。
  • 快慢指针用于链表问题,如检测链表环、找到中间节点。
  • 滑动窗口技术用于子数组问题,如最大子数组和、最小覆盖子串。

思维方式:

  • 考虑从两端同时向中间推进。
  • 或者从头和尾同时进行遍历,找到符合条件的值。

技巧2:哈希表技术

哲学思想:记忆与记录

应用场景:

  • 查找问题,如查找数组中是否存在重复元素。
  • 计数问题,如字符频率统计、两数组交集。
  • 映射问题,如从一个集合映射到另一个集合。

思维方式:

  • 利用快速查找的特性,记录已经访问过的元素及其信息。
  • 适用于需要频繁查找和更新数据的问题。

技巧3:状态标记法

哲学思想:变更与保留

应用场景:

  • 动态规划问题,如最小路径和、背包问题。
  • 图的遍历问题,如深度优先搜索(DFS)、广度优先搜索(BFS)。
  • 状态转移问题,如记忆化搜索、状态压缩。

思维方式:

  • 通过记录状态来避免重复计算。
  • 使用数组或其他数据结构保存中间结果。

技巧4:反转操作

哲学思想:反转与复原

应用场景:

  • 数据结构的反转,如反转链表、反转字符串。
  • 双端队列问题,如使用双指针反转数组的一部分。
  • 操作顺序颠倒问题,如栈的应用。

思维方式:

  • 考虑如何从尾到头进行操作,或从两端向中间靠拢。
  • 适用于需要反转或改变顺序的问题。

技巧5:递归与迭代

哲学思想:自相似性

应用场景:

  • 分治算法,如快速排序、归并排序。
  • 树的遍历,如前序、中序、后序遍历。
  • 数学问题,如斐波那契数列、组合数计算。

思维方式:

  • 递归方法通常用于自然分治的问题。
  • 确保理解递归的基线条件和递归条件。
  • 考虑如何将递归转换为迭代来提高效率。

技巧6:分治法

哲学思想:整体与部分

应用场景:

  • 排序算法,如快速排序、归并排序。
  • 搜索算法,如二分搜索、最近点对问题。
  • 动态规划问题中的优化,如矩阵链乘法。

思维方式:

  • 将问题分解为更小的子问题,解决每个子问题后合并结果。
  • 适用于可以自然分割的问题。

技巧7:贪心算法

哲学思想:局部最优与全局最优

应用场景:

  • 路径问题,如最短路径、最小生成树。
  • 资源分配问题,如活动选择、背包问题。
  • 排序问题,如区间调度、会议室安排。

思维方式:

  • 在每一步选择局部最优解,希望通过一系列局部最优解达到全局最优。
  • 适用于贪心选择性质和最优子结构的问题。

技巧8:动态规划

哲学思想:阶段性最优

应用场景:

  • 序列问题,如最长上升子序列、最长公共子序列。
  • 数组问题,如最大子数组和、矩阵路径问题。
  • 计数问题,如硬币兑换、字符串匹配。

思维方式:

  • 分阶段求解问题,每个阶段的解依赖于上一个阶段的解。
  • 使用状态转移方程和初始条件来解决问题。

技巧9:回溯法

哲学思想:试探与回溯

应用场景:

  • 组合问题,如全排列、组合数。
  • 棋盘问题,如八皇后、数独。
  • 路径问题,如迷宫求解、图的 Hamiltonian 路径。

思维方式:

  • 通过试探法尝试所有可能的解,当发现当前试探不成功时,进行回溯。
  • 适用于需要穷举所有解或找到一个可行解的问题。

技巧10:模拟法

哲学思想:逐步逼近

应用场景:

  • 操作模拟,如旋转矩阵、文字处理。
  • 数学问题,如大数计算、数列生成。
  • 游戏问题,如棋盘游戏、博弈问题。

思维方式:

  • 逐步模拟问题的步骤,接近最终结果。
  • 考虑如何将实际问题的步骤逐步实现。

通过理解和应用这些技巧,可以在不同类型的编程问题中举一反三,从而提高解决问题的效率和灵活性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/844898.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

概率论与数理统计,重要知识点——全部公式总结

二、一维随机变量及其分布 五个分布参考另外一篇文章 四、随机变量的数字特征 大数定理以及中心极限定理 六、数理统计

WSDM2024推荐系统和LLM相关论文整理(二)

WSDM 2024接收的论文已经公布&#xff0c;全部收录的论文可前往地址 网址&#xff1a;https://www.wsdm-conference.org/2024/accepted-papers/ 其中推荐系统相关论文三十余篇&#xff0c;下文列举了部分论文的标题以及摘要&#xff0c;更多内容欢迎关注公众号【深度学习推荐算…

Stable Diffusion AI绘画:从创意词汇到艺术图画的魔法之旅

文章目录 一、Stable Diffusion的工作原理二、从提示词到模型出图的过程三、Stable Diffusion在艺术创作中的应用《Stable Diffusion AI绘画从提示词到模型出图》内容简介作者简介楚天 目录前言/序言本书特色特别提示 获取方式 在科技的飞速发展中&#xff0c;Stable Diffusion…

Java-----Comparable接口和Comparator接口

在Java中&#xff0c;我们会经常使用到自定义类&#xff0c;那我们如何进行自定义类的比较呢? 1.Comparable接口 普通数据的比较 int a10;int b91;System.out.println(a<b); 那自定义类型可不可以这样比较呢&#xff1f;看一下代码 我们发现会报错&#xff0c;因为自定义…

LNMP分布式搭建

一、准备三台主机 192.168.100.11 mysql 192.168.100.12 nginx 192.168.100.13 php 二、关闭防火墙及安全策略 systemctl stop firewalld setenforce 0 三、安装nginx&#xff08;192.168.100.11&#xff09; 1、添加nginx源 vim /etc/yum.repos.d/ng…

618必备好物选购清单有哪些?五款精品好物分享

618将近&#xff0c;很多好物都会在这段时间搞活动&#xff0c;许多朋友会在这个时候置办或者置换家居&#xff0c;那么&#xff0c;2024年的618有哪些值得入手的好物呢&#xff1f;今天&#xff0c;将与大家分享一些精选好物&#xff0c;这些好物在618入手绝对是个不错的选择。…

Unity中模拟生成正态分布的一种方式

using System; using System.Collections; using System.Collections.Generic; using Unity.Mathematics; using UnityEngine;public class MathFunction : MonoBehaviour {private void Start(){//key 范围 0-99 表示 0% 到 99%Dictionary<int,uint> m new Dictionary&…

【Linux】文件系统和软硬链接

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前正在学习c和算法 ✈️专栏&#xff1a;Linux &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章有啥瑕疵&#xff0c;希望大佬指点一二 如果文章对…

redux相关源码

1、createStore 实现 2、connct实现 3、bindActionCreators实现 4、Provider实现 5. thunk

html+css web前端 多边形

<!DOCTYPE html><html><head><meta charset"UTF-8"><title>多边形</title><style type"text/css">#pentagon_6_1 {position: absolute;top: 0px;height: 0; width: 100; border-left: 100px solid rgb(255, 255…

【记忆化搜索 】2312. 卖木头块

本文涉及知识点 记忆化搜索 LeetCode2312. 卖木头块 给你两个整数 m 和 n &#xff0c;分别表示一块矩形木块的高和宽。同时给你一个二维整数数组 prices &#xff0c;其中 prices[i] [hi, wi, pricei] 表示你可以以 pricei 元的价格卖一块高为 hi 宽为 wi 的矩形木块。 每…

网络四层、七层协议

一、OSI七层模型 物理层&#xff1a;建立、维护、断开物理连接。 数据链路层&#xff1a;逻辑连接、寻找硬件地址——地址解析协议&#xff1a;ARP、PARP 反向地址转换协议 网络层&#xff1a;寻找逻辑地址&#xff0c;实现不同网络之间的路径选择——ICMP(互联网控制信息协议…

Vue前端平台的搭建

文章目录 前端平台搭建(`Vue2.6`,App:`HBulderX`)创建`Vue2.6`项目下载相应插件方便开发路由配置对连接后端进行一些配置(`main.js`文件)导入ElementUI组件[组件 | Element](https://element.eleme.cn/#/zh-CN/component/icon)同步与异步`axios`异步请求框架前端平台搭建(Vue2.…

YoloV8改进策略:卷积篇|基于PConv的二次创新|附结构图|性能和精度得到大幅度提高(独家原创)

摘要 在PConv的基础上做了二次创新,创新后的模型不仅在精度和速度上有了质的提升,还可以支持Stride为2的降采样。 改进方法简单高效,需要发论文的同学不要错过! 论文指导 PConv在论文中的描述 论文: 下面我们展示了可以通过利用特征图的冗余来进一步优化成本。如图3所…

windows 在cmd 使用cd命令无法进入指定目录解决方法

目录预览 一、问题描述二、原因分析三、解决方案四、参考链接 一、问题描述 使用cmd命令想要快速进入某个目录&#xff0c;发现没有跳转&#xff0c;如下&#xff1a; 二、原因分析 cmd 切换目录跨磁盘的话&#xff0c;需要先进行磁盘的转换&#xff0c;也就是要进入到另外一…

使用pkg打包了一个使用了sqlite3的nodejs项目,启动后闪退

从截图来看&#xff0c;问题出在 sqlite3 模块上。说明在打包过程中&#xff0c;sqlite3 模块的 .node 文件没有正确加载。 紧急解决方法&#xff1a; 其实就是exe文件还需要node_modules中的sqlite3 依赖&#xff0c;我们直接在系统顶级放一个node_modules&#xff0c;且其中只…

极简朋友圈rmoments多用户版

什么是 moments &#xff1f; moments 是仿照微信朋友圈开发的个人极简朋友圈。但目前不支持多用户。 什么是 rmoments &#xff1f; rmoments 复刻自 moments&#xff0c;除了支持全部原生功能外&#xff0c;最大的特点是支持多用户。 原版 moments 目前不支持多用户。rmoment…

✅order by 是怎么实现的?

首先排序功能由 ORDER BY 实现&#xff0c;具体排列顺序取决于优化器的选择。若优化器认为索引排序更有效率&#xff0c;则使用索引排序&#xff1b;反之&#xff0c;则使用 filesort&#xff08;执行计划中额外信息提示&#xff1a;使用 filesort&#xff09;。然而&#xff0…

如何制定精益生产团队的目标和考核机制?

当下&#xff0c;精益生产已经成为企业追求高效运营和持续竞争优势的重要策略。而要实现精益生产的最大化效益&#xff0c;制定科学合理的团队目标与考核机制显得尤为重要。本文&#xff0c;深圳天行健企业管理咨询公司将分享如何制定精益生产团队的目标和考核机制&#xff0c;…

【Test 19】 数据结构 快速排序详解!

文章目录 1. 快速排序的非递归版本2. 快速排序2.1 hoare 版本一2.2 挖坑法 &#x1f427;版本二2.3 前后指针 版本三2.4 调用以上的三个版本的快排 3. 快速排序的优化 1. 快速排序的非递归版本 &#x1f192;&#x1f427;关键思路&#xff1a; &#x1f34e;① 参数中的begin…