每日一题——力扣206. 反转链表(举一反三、思想解读)


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

个人主页:用哲学编程-CSDN博客
专栏:每日一题——举一反三
题目链接

目录

菜鸡写法​编辑

代码点评

代码分析

时间复杂度

空间复杂度

专业点评

另一种方法​编辑

代码点评

代码逻辑

时间复杂度

空间复杂度

专业点评

我要更强

代码点评

代码逻辑

时间复杂度

空间复杂度

点评

这种方法用到哪些哲学和编程思想

1. 迭代思想

2. 逐步构建

3. 指针操作

4. 空间效率

5. 不变性和可变性

6. 递归与迭代的对比

7. 抽象和具体化

举一反三

1. 理解数据结构的性质

2. 分步骤解决问题

3. 空间复杂度意识

4. 循环和迭代

5. 修改现有结构 vs 新建结构

6. 不变量的使用

7. 对可变性的控制

8. 边界案例和错误处理



菜鸡写法

// 定义了一个用于反转单链表的函数。接受一个指向链表头节点的指针。
struct ListNode* reverseList(struct ListNode* head) {// 如果链表为空或只有一个节点,直接返回头指针,因为无需反转。if(head==NULL||head->next==NULL)return head;// 定义三个指针用于遍历链表和反转操作。struct ListNode* tmp1=NULL;struct ListNode* tmp2=NULL;struct ListNode* newHead;// 查找链表的尾节点,将其作为新的头节点(但这个过程实际上并不改变链表结构)。for(newHead=head;newHead->next!=NULL;newHead=newHead->next) {// 空循环体,仅用于遍历到链表末尾。}// 开始反转链表的过程。for(tmp1=head,tmp2=newHead;tmp2->next!=head;) {// 查找倒数第二个节点。for(tmp1=head;tmp1->next->next!=NULL;tmp1=tmp1->next) {// 空循环体,仅用于遍历。}// 再次遍历到链表末尾的节点,这一步似乎是多余的,因为tmp2已经是最后一个节点。for(tmp2=newHead;tmp2->next!=NULL;tmp2=tmp2->next) {// 空循环体。}// 将倒数第二个节点连接到最后一个节点。tmp2->next = tmp1;// 并将倒数第二个节点的next设置为NULL,断开与前一个节点的连接。tmp1->next = NULL;}// 返回新的头节点,即原链表的尾节点。return newHead;
}

代码点评

这段代码的目标是反转一个单链表。然而,它采用了一种非常不典型且低效的方法来实现这一目标。让我们从几个方面来进行分析。

代码分析

首先,代码的基本逻辑是试图通过找到链表的最后一个节点来开始反转过程,然后逐步将每个节点移动到这个位置。这种方法虽然在逻辑上可行,但远非最优解。

时间复杂度

时间复杂度是衡量算法运行时间长短的一个指标。对于这段代码,我们注意到它包含多层嵌套循环。最外层循环的目的是为了重复反转操作,而内层的两个循环则是寻找链表的倒数第二个节点(tmp1)和最后一个节点(tmp2)。这种多层嵌套循环的使用导致了非常高的时间复杂度。

  • 对于一个包含 n 个节点的链表,最外层循环需要执行 n 次,因为每次循环只能将一个节点移动到正确的位置。
  • 内层循环每次都会遍历整个链表来查找倒数第二个节点,因此,它们的时间复杂度也是 O(n)。

因此,总的时间复杂度为 O(n^2),这是因为对于每个节点,算法都需要遍历整个链表来找到它应该在反转后的位置。这种方法显然不是反转链表的最佳方案。

空间复杂度

空间复杂度衡量的是算法在执行过程中需要的额外空间。这段代码的空间复杂度相对较低,为 O(1)。这是因为无论链表的大小如何,它只需要维护几个额外的指针(tmp1、tmp2 和 newHead),这些指针的数量不随链表大小变化而变化。

专业点评

从专业角度来看,这段代码虽然能够实现链表反转的功能,但其方法过于复杂且效率低下。在实际应用中,我们通常采用更高效且更直观的算法,例如迭代法或递归法,这些方法的时间复杂度为 O(n),空间复杂度为 O(1)(迭代法)或 O(n)(递归法,因为递归调用栈的使用)。

迭代法通过遍历链表一次,逐个调整节点的指向,就可以完成链表的反转,而无需如此复杂的多次遍历和无效操作。因此,尽管这段代码在逻辑上是正确的,它并不是解决问题的最佳方案。在编写代码时,寻找更高效的算法总是非常重要的,这不仅可以提升代码的运行速度,还可以提高代码的可读性和可维护性。


另一种方法

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/struct ListNode* reverseList(struct ListNode* head){int arr[5001]={0},i;struct ListNode* cur=head;for(i=0;cur;cur=cur->next,i++){arr[i]=cur->val;}i--;for(cur=head;cur;cur=cur->next,i--){cur->val=arr[i];}return head;
}

代码点评
 

这段代码提供了一种通过使用数组作为中介来反转单链表的方法。整体上,这种方法虽然相对直观,但并不是最优解。下面是对这段代码的专业点评,包括时间复杂度和空间复杂度的分析。

代码逻辑

  1. **遍历链表并存储值:**初始遍历链表,将每个节点的值存储到一个固定大小的数组arr中。数组大小被设定为5001,这意味着代码假设链表的最大长度不会超过5000个节点。
  2. **反向赋值:**第二次遍历链表时,按反向顺序从数组arr中读取值,并重新赋值给各个链表节点。

时间复杂度

  • 第一次遍历链表有一个时间复杂度为O(n),其中n是链表中的节点数量,因为它需要遍历整个链表一次。
  • 第二次遍历同样有一个时间复杂度为O(n),因为它再次遍历整个链表来重新赋值。
  • 因此,总体时间复杂度为O(n) + O(n) = O(2n),简化后为O(n)。

空间复杂度

  • 数组arr的空间复杂度为O(m),其中m是数组的固定大小,这里是5001。尽管这个空间用于存储链表的值,但由于其大小固定,我们可以认为这里的空间复杂度是O(1),意味着与链表的长度无关。
  • 没有使用递归调用或动态分配额外的数据结构,所以除了数组arr以外,没有使用额外的空间。

专业点评

  1. 假定限制:使用固定大小的数组(5001个元素)来存储链表的值是这段代码的一个主要限制。这意味着它只能正确处理最多5000个节点的链表。对于更大的链表,这段代码将无法工作。
  2. 效率和优化:虽然这种方法的时间复杂度为O(n),与一些更直接的链表反转方法相同,但它通过增加一个额外的数组来存储链表的值,这增加了额外的空间复杂度。更优的链表反转方法(例如迭代或递归反转)可以直接在链表上操作,而无需额外的存储空间。
  3. 实用性:这段代码在实际应用中的实用性受到限制,因为它假设了链表的最大长度。在处理未知长度的链表时,更灵活的方法是直接操作链表的指针。

总的来说,这段代码为链表反转问题提供了一个有趣的解决方案,但它不是最优的。对于实际应用,推荐使用直接在链表上操作的方法,这样可以避免固定长度数组带来的限制,并且减少空间复杂度。

我要更强
 

struct ListNode* reverseList(struct ListNode* head) {struct ListNode* prev = NULL;struct ListNode* curr = head;while (curr) {struct ListNode* next = curr->next;curr->next = prev;prev = curr;curr = next;}return prev;
}

代码点评
 

这段代码是一个用来反转单链表的经典迭代方法。它通过重新指定节点间的链接关系来实现链表的反向,不需要额外的数组或数据结构来存储链表的节点值。下面是对这段代码的专业点评,包括时间复杂度和空间复杂度的分析。

代码逻辑

  1. **初始化指针:**使用三个指针prev、current和next来跟踪链表的节点。prev初始化为NULL,因为反转后的链表的第一个节点将指向NULL。
  2. **遍历链表:**遍历整个链表,对于每个节点,暂时保存它的下一个节点(next),然后将它的next指针指向prev节点,从而实现反转。之后,prev和current都向前移动一个节点。
  3. **更新头指针:**遍历完成后,prev将指向原链表的最后一个节点,这时候它成了反转后的链表的头节点。更新head指针指向prev。

时间复杂度

时间复杂度是O(n),其中n是链表中的节点数。这是因为代码需要遍历链表一次,每个节点仅被访问一次。

空间复杂度

空间复杂度是O(1),因为反转过程中仅使用了有限的几个指针变量(prev、current和next),并且这个额外空间的需求不随输入链表的大小变化而变化。

点评

这种迭代方法是反转单链表的一种非常高效且空间节省的方法。与使用数组或其他数据结构存储节点值相比,这种直接操作链表指针的方法不仅节省了空间,而且简化了逻辑,提高了效率。由于它的时间复杂度是线性的,且空间复杂度是常数级别的,这使得它在实际应用中非常受欢迎,尤其是在空间资源受限的环境中。

此外,这种方法的优点还包括其简洁性和直观性,使得它容易理解和实现。不依赖于链表的大小或是其他外部数据结构,使得它在多种编程语境中都非常适用。因此,这是一种在实际编程工作中推荐使用的链表反转技术。


这种方法用到哪些哲学和编程思想

反转链表的迭代方法体现了几个重要的哲学和编程思想:

1. 迭代思想

迭代是一种通过重复执行一系列步骤直到满足某个条件来解决问题的方法。在这个链表反转的例子中,迭代思想体现在使用一个循环(while循环)来遍历链表,直到所有节点都被处理完毕。

2. 逐步构建

这种方法通过逐步改变链表节点的next指针来构建反转后的链表。这种逐步构建的思想是编程中解决复杂问题的一种常见策略,它将一个复杂的问题分解成一系列简单的步骤,每一步都依赖于前一步的结果。

3. 指针操作

链表反转方法的核心在于对指针的精确操作。指针是编程中用来直接访问和操作内存中数据的重要工具。在这个例子中,通过改变节点的next指针,实现了链表结构的改变。

4. 空间效率

这种方法的空间复杂度是O(1),意味着它只使用了常数级别的额外空间。这与许多算法和数据结构中追求空间效率的哲学相一致,特别是在资源受限的环境中,如嵌入式系统或高性能计算。

5. 不变性和可变性

在编程中,不变性指的是对象在创建后其状态不能被改变,而可变性则允许对象的状态在创建后被改变。在这个链表反转的例子中,我们利用了链表的可变性,通过改变节点的next指针来反转链表。

6. 递归与迭代的对比

虽然这个例子使用的是迭代方法,但反转链表也可以使用递归方法来实现。递归和迭代是解决问题的两种不同方法,它们各自有优缺点。在这个例子中,迭代方法避免了递归可能带来的栈溢出风险,同时也减少了额外的空间使用。

7. 抽象和具体化

链表反转的迭代方法体现了从抽象到具体化的过程。首先,我们有一个抽象的问题:反转链表。然后,我们通过具体的代码实现来解决这个问题。这种从抽象到具体的过程是编程中的一个核心思想,它帮助我们将复杂的问题分解成可管理的代码块。

总的来说,链表反转的迭代方法是一个很好的例子,展示了如何将编程思想和哲学应用于解决实际问题。它强调了迭代、逐步构建、指针操作、空间效率、不变性与可变性、递归与迭代的对比,以及抽象和具体化的重要性。


举一反三

基于单链表反转的迭代方法,以下是一些编程技巧和原则,你可以举一反三地应用到其他编程任务中:

1. 理解数据结构的性质

在反转链表之前,了解链表的性质是至关重要的。同样地,在处理任何数据结构时,首先要深入理解它的性质和运作方式。例如,数组支持随机访问,而链表支持高效的元素插入和删除。这种理解会帮助你选择正确的算法和编程策略。

2. 分步骤解决问题

反转链表的过程是一步一步完成的,每次迭代只处理一个节点。类似地,面对复杂问题时,将其拆分成一系列小任务,并逐个解决。这样可以简化问题,并保持代码的清晰和可管理性。

3. 空间复杂度意识

使用有限的几个指针变量就完成了链表的反转,这展示了在可能的情况下应该优化空间使用的重要性。在其他算法设计中,同样要考虑如何使用最小的额外空间,特别是对于大数据量或内存限制的应用场景。

4. 循环和迭代

迭代是解决问题的核心工具,不仅适用于链表操作,也适用于数组处理、树遍历等。熟练使用forwhile循环可以帮助你编写出处理各种数据结构的有效算法。

5. 修改现有结构 vs 新建结构

在链表反转案例中,我们通过修改现有的链表结构来实现目标,而没有新建一个链表。当需要优化性能时,考虑直接修改现有结构而不是创建新的数据结构可以节省空间和时间。

6. 不变量的使用

在反转链表的过程中,我们维护了节点之间的连接关系,即使是在修改指针时。在其他问题中,识别和保持不变量可以帮助你在变化的过程中保持数据完整性和一致性。

7. 对可变性的控制

在修改数据时,要小心对数据结构中的元素进行操作,确保不会不小心破坏结构。在编程中,正确管理可变状态和不变状态对于预防错误和保证程序的可靠性至关重要。

8. 边界案例和错误处理

当你反转链表时,需要考虑空链表或只有一个节点的链表等边界情况。同样地,在设计算法和编写代码时,总是要考虑和处理边界情况和可能的错误,确保代码的健壮性。

将这些技巧和原则应用到其他编程任务中,可以帮助你更有效地解决问题,写出更清晰、更高效、更健壮的代码。

以上是本节全部内容,感谢阅读!!!

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

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

相关文章

基于火山引擎云搜索的混合搜索实战

在搜索应用中,传统的 Keyword Search 一直是主要的搜索方法,它适合精确匹配查询的场景,能够提供低延迟和良好的结果可解释性,但是 Keyword Search 并没有考虑上下文信息,可能产生不相关的结果。最近几年,基…

第三十二天 | 46.全排列 47.全排列||

终于进入排列!(之前都是组合) 排列和组合的区别:在数学上的区别都懂,主要是看在代码实现上有什么区别 题目:46.全排列 树型结构比较简单 用used标记某一元素是否使用过。在组合问题中,其实是…

【Amplify_自己写的shadr遇到没有阴影的解决方案】

Amplify 自己写的shadr遇到没有阴影的解决方案 2020-01-21 16:04 本来我有个百试很灵的投射阴影脚本。 这次不灵光了!地形内建材质,这个不支持投射的阴影~~奇了怪了。 可以采用引用的方式UsePass加入阴影部分代码,具体操作如下&#xff1…

.NET 4.8和.NET 8.0的区别和联系、以及查看本地计算机的.NET版本

文章目录 .NET 4.8和.NET 8.0的区别查看本地计算机的.NET版本 .NET 4.8和.NET 8.0的区别 .NET 8.0 和 .NET 4.8 之间的区别主要体现在它们的发展背景、目标平台、架构设计和功能特性上。下面是它们之间的一些主要区别: 发展背景: .NET 4.8 是.NET Fram…

WSL2-Ubuntu(深度学习环境搭建)

1.在Windows的WSL2上安装Ubuntu 流程可参考:https://www.bilibili.com/video/BV1mX4y177dJ 注意:中间可能需要使用命令wsl --update更新一下wsl。 2.WSL数据迁移 按照下面流程:开始菜单->设置->应用->安装的应用->搜索“ubun…

LAMP与动静态网站介绍

黄金架构LAMP Httpd PHP MySQL 三这如何工作 为什么说LAMP是黄金架构呢? 在互联网刚刚新起时,很多门户网站第一代架构都是采用LAMP,比如新浪,一些电商的互联网公司的站点,他们在网站第一代架构出行,用的都…

JETBRAINS IDES 分享一个2099通用试用码!DataGrip 2024 版 ,支持一键升级

文章目录 废话不多说上教程:(动画教程 图文教程)一、动画教程激活 与 升级(至最新版本) 二、图文教程 (推荐)Stage 1.下载安装 toolbox-app(全家桶管理工具)Stage 2 : 下…

VUE 滚动到指定区域scrollIntoView

背景&#xff1a;当前 VUE 页面数据量很大&#xff0c;右侧出现滚动条, 进入该页面&#xff0c;页面定位到指定区域&#xff1b; 项目要求&#xff1a; 进入页面&#xff0c;定位到指定行&#xff08;红色标记&#xff09; 直接看效果&#xff1a; 代码demo&#xff1a; <…

Unity设计模式之工厂模式

什么是工厂模式&#xff1f; 工厂是一种创建型设计模式。通俗来讲就是提供一种封装对象创建的方式&#xff0c;将对象的创建和使用区分开。就是Unity里面通常用到的创建和管理对象。 工厂模式有什么优点&#xff1f; 1、封装对象的创建方式&#xff0c;使其更加灵活、易于管理…

中美在AI大模型的商业化和产业化的优势和面临的挑战有哪些?

中美在AI大模型的商业化和产业化的优势和面临的挑战有哪些&#xff1f; 中美在AI大模型的商业化和产业化方面各有优势&#xff0c;但也面临不少挑战。 一、优势 1、技术领先 美国在AI大模型领域拥有深厚的技术储备和布局&#xff0c;如OpenAI的GPT系列、谷歌的BERT等&#…

【前段】开发五子棋小游戏全流程

使用前端技术开发五子棋小游戏 在这篇博文中,我们将详细介绍如何使用HTML、CSS和JavaScript开发一个简单的五子棋小游戏。我们将展示如何初始化棋盘、处理用户交互以及实现胜负判定。特别是,我们将着重介绍胜负判定的逻辑实现。 完整代码我放在了这里:github 项目结构 项…

EE trade:现货黄金交易有哪些优势

现货黄金交易具有多种优势&#xff0c;使其成为许多投资者青睐的投资选择&#xff1a; 流动性高&#xff1a;黄金市场是全球最大的金融交易市场之一&#xff0c;确保了黄金拥有极高的流动性。这意味着投资者可以随时买入或卖出黄金&#xff0c;几乎不必担心大额交易会对市场价…

锁策略详解:互斥锁、读写锁、乐观锁与悲观锁、轻量级锁与重量级锁、自旋锁、偏向锁、可重入锁与不可重入锁、公平锁与非公平锁

目录 一.锁策略 二.互斥锁 三.读写锁 四.乐观锁与悲观锁 五.重量级锁与轻量级锁 六.自旋锁 七.偏向锁 八.公平锁与非公平锁 九.可重入锁与不可重入锁 一.锁策略 锁策略指的是在多线程编程中用于管理共享资源访问的规则和技术。它们确保在任何给定时间只有一个线程可以…

自然资源-国土空间体系下详细规划转型建设-学习安徽模式

自然资源-国土空间体系下详细规划转型建设-学习安徽模式 为了全面贯彻落实党的二十大精神&#xff0c;深化“多规合一”改革成果&#xff0c;提高城市规划、建设、治理水平&#xff0c;促进城乡高质量发展&#xff0c;2023年3月&#xff0c;自然资源部印发《关于加强国土空间详…

IntelliJ IDEA 配置JDK

IntelliJ IDEA-之配置JDK 我们的开发神器IDEA安装好了之后&#xff0c;在实际开发中&#xff0c;我们如何去配置好JDK的版本呢&#xff1f; 注意&#xff1a;需要保证JDK在已经成功安装的情况下&#xff0c;再进行IDEA的配置 现在就行动&#xff0c;让IntelliJ IDEA成为你征…

【工具】macOS、window11访问limux共享目录\共享磁盘,samba服务安装使用

一、samba服务安装 Samba是一个免费的开源软件实现&#xff0c;使得非Windows操作系统能够与Windows系统进行文件和打印服务共享。它实现了SMB/CIFS协议&#xff0c;并且能够在Linux、Unix、BSD等多种系统上运行。 安装 samba&#xff1a; sudo yum install samba配置 samba…

后端之路第一站——Maven

前提&#xff1a;得会基础java 前言&#xff1a;不知道出于什么原因&#xff0c;可能是喜欢犯贱吧&#xff0c;本人从大一到大二都一直在专研前端开发&#xff0c;一点也没接触过后端&#xff0c;但是突然抽风想学后端了&#xff0c;想试着自己全栈搞一下项目&#xff0c;于是在…

机器人工具箱学习(三)

一、动力学方程 机器人的动力学公式描述如下&#xff1a; 式中&#xff0c; τ \boldsymbol{\tau} τ表示关节驱动力矩矢量&#xff1b; q , q ˙ , q \boldsymbol{q} ,\; \dot{\boldsymbol { q }} ,\; \ddot{\boldsymbol { q }} q,q˙​,q​分别为广义的关节位置、速度和加速…

【Python】图形用户界面设计

1、设计并编写一个窗口程序,该窗口只有一个按钮,当用户单击时可在后台输出hello world. import tkinter as tk def on_button_click():print("hello world") # 创建主窗口 root tk.Tk() root.title("Hello World Button") # 设置窗口大小 root.geometry…

Android手动下载Gradle的使用方法

导入新项目通常会自动下载gradle版本&#xff0c;这种方式很慢而且经常下载失败&#xff0c;按照提示手动下载的gradle应该放在那里&#xff0c;如何使用&#xff0c;本篇文章为你提供一种亲测有效的方法&#xff1a; 在Android Studio打开Setting搜索Gradle找到Gradle的存放目…