【LeetCode】每日一题:排序链表

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。

解题思路

主要是归并排序,即分治思想,两个数组都有序了之后再双指针合并。这个过程当中,链表排序涉及到:

1、如何寻找到中点来二分:快慢指针找到终点,这个过程道中涉及链表的分治问题时的边界问题,需要考虑好head.next=tail是一个元素的边界而不是两个。

2、在一个元素返回递归时,需要注意要将其变为独立结点,即next应改为空,只有这样才不会在合并的过程中发生乱指的问题

这是一个综合了快慢指针,链表合并,归并问题的综合型问题

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:def sortListNode(head, tail):if not head:return headif head.next == tail:head.next = Nonereturn headfast = slow = headwhile fast != tail:fast = fast.nextslow = slow.nextif fast != tail:fast = fast.nextmid = slowreturn merge(sortListNode(head, mid), sortListNode(mid, tail))def merge(a, b):res = ListNode()temp, list1, list2 = res, a, bwhile list1 and list2:if list1.val < list2.val:temp.next = list1temp = temp.nextlist1 = list1.nextelse:temp.next = list2temp = temp.nextlist2 = list2.next  temp.next = list1 if list1 else list2return res.nextreturn sortListNode(head, None)

官方题解

官方题解中还给出了一种自顶向上的方法,这种方法可以使得其只需要常数级的空间复杂度,需要通过记录链表长度来实现。

class Solution:def sortList(self, head: ListNode) -> ListNode:def merge(head1: ListNode, head2: ListNode) -> ListNode:dummyHead = ListNode(0)temp, temp1, temp2 = dummyHead, head1, head2while temp1 and temp2:if temp1.val <= temp2.val:temp.next = temp1temp1 = temp1.nextelse:temp.next = temp2temp2 = temp2.nexttemp = temp.nextif temp1:temp.next = temp1elif temp2:temp.next = temp2return dummyHead.nextif not head:return headlength = 0node = headwhile node:length += 1node = node.nextdummyHead = ListNode(0, head)subLength = 1while subLength < length:prev, curr = dummyHead, dummyHead.nextwhile curr:head1 = currfor i in range(1, subLength):if curr.next:curr = curr.nextelse:breakhead2 = curr.nextcurr.next = Nonecurr = head2for i in range(1, subLength):if curr and curr.next:curr = curr.nextelse:breaksucc = Noneif curr:succ = curr.nextcurr.next = Nonemerged = merge(head1, head2)prev.next = mergedwhile prev.next:prev = prev.nextcurr = succsubLength <<= 1return dummyHead.next

Q&A

Q: 为什么head.next == tail是一个元素的情况,我感觉像是两个。代码是对的但是我没有理解

A: 举例说明
假设链表有三个节点 A -> B -> C:

初始调用:sortFunc(A, None)
快慢指针遍历找到中点,链表被分为两部分:A 和 B -> C
递归调用:sortFunc(A, A) 和 sortFunc(B, None)
分割链表到单个节点的过程:
对于 sortFunc(A, A):
head 是 A,tail 也是 A
根据条件 if head.next == tail,实际上是 A.next == A,条件不成立,但由于链表只有一个元素,所以 head.next 被设为 None,返回 A
对于 sortFunc(B, None):
head 是 B,tail 是 None
快慢指针找到中点,分割为 sortFunc(B, B) 和 sortFunc(C, None)
sortFunc(B, B) 返回 B,sortFunc(C, None) 返回 C
核心理解:
当 head.next == tail 时,说明从 head 到 tail 只有一个元素。此时,将 head.next 设为 None,并返回 head,相当于链表只包含 head 这个单一节点。
由于 tail 是函数中的虚拟边界,不包含在链表中,所以实际处理时,head.next == tail 代表的是只有一个节点。
总结
head.next == tail 的判断是为了处理链表分割到只剩一个元素的情况。
这种判断方式有效地确保递归基于链表的分割能够正确终止,并处理只有一个元素的子链表。
通过上述解释,相信你能够理解为什么 head.next == tail 实际上处理的是一个元素的情况,而不是两个元素。

Q: 嗯我现在理解了,那么为什么一个元素的时候需要把head.next 改为None呢?

A: 将 head.next 设置为 None 的主要原因是为了确保在递归过程中,每个单独的节点成为一个独立的链表节点。这有助于在归并两个子链表时避免不必要的连接,从而保证链表的正确排序。具体原因如下:

保证链表分割正确
在归并排序中,链表被不断地分割直到每个子链表只包含一个元素。如果不将 head.next 设置为 None,那么在分割后的子链表中,原链表的 next 连接仍然存在,这可能导致在后续的合并过程中出现错误的连接。

避免循环引用
如果不将 head.next 设置为 None,链表的节点可能会保持原来的连接状态。在递归调用返回时,如果节点之间存在原来的连接,可能会导致链表的某些部分形成循环引用,最终导致程序进入死循环。

简化合并过程
在合并两个有序链表时,假设每个子链表的头节点都是独立的,简化了合并过程。如果 head.next 没有设置为 None,在合并时需要处理更多的连接关系,增加了代码的复杂性和出错的可能性。

举例说明
假设链表为 A -> B -> C,在分割过程中:

初始调用:sortFunc(A, None)
分割为:sortFunc(A, A) 和 sortFunc(B, None)
在处理 sortFunc(A, A) 时,如果不将 head.next 设置为 None,A 仍然指向 B。
这样在后续的合并过程中,可能会错误地将 B 连接到 A 上,导致排序失败。
通过将 head.next 设置为 None,确保 A 成为独立的节点,没有任何连接,合并时只需要简单地比较和连接节点即可。

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

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

相关文章

php composer 报错

引用文章&#xff1a; Composer设置国内镜像_composer 国内源-CSDN博客 php composer.phar require --prefer-dist yiidoc/yii2-redactor "*" A connection timeout was encountered. If you intend to run Composer without connecting to the internet, run the …

【Docker】rancher 管理平台搭建

目录 1. 所有节点安装docker 2. 所有节点配置/etc/sysconfig/docker 文件修改如下配置 3. 配置证书 4. 镜像仓库导入镜像 5. 创建镜像仓库 5.1 查询上传的 image id 5.2 镜像打标签 5.3 镜像上推 6. server 节点 7. client 节点 8. 在 server 节点启动 9. 查看运行…

SHELL/作业/2024/6/25

终端输入两个数&#xff0c;判断两数是否相等&#xff0c;如果不相等&#xff0c;判断大小关系 #!/bin/basha$1b$2 if [ $a -eq $b ]then echo "ab"elif [ $a -gt $b ]thenecho "a>b"elseecho "a<b"fi2.已知网址www.hqyj.com…

vivado AUTOPIPELINE_MODULE、AUTOPIPELINE_INCLUDE

自动管道模块 AUTOPIPELINE_MODULE属性为所有组建立一个单独的名称空间 在整个子层次结构中定义的名称。它必须设置在包括的层次结构上 GROUP和AUTOPIPELINE_ LIMIT标记的网络。它也必须使用 当在设计中多次实例化具有自动流水线特性的模块时。 参见Vivado Design Suite用户指南…

算法训练营day20--235. 二叉搜索树的最近公共祖先+701.二叉搜索树中的插入操作 +450.删除二叉搜索树中的节点

一、235. 二叉搜索树的最近公共祖先 题目链接&#xff1a;https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/ 文章讲解&#xff1a;https://programmercarl.com/0235.%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E7%9A%84%E6%9C%80%E8%BF%91…

PointCloudLib-开发环境搭建-C++

简介 PCL(Point Cloud Library)是在吸收了前人点云相关研究基础上建立起来的大型跨平台开源C++编程库,它实现了大量点云相关的通用算法和高效数据结构,涉及到点云获取、滤波、分割、配准、检索、特征提取、识别、追踪、

Linux源码阅读笔记04-实时调度类及SMP和NUMA

Linux进程分类 实时进程普通进程 如果系统中有一个实时进程并且可执行&#xff0c;调度器总是会选择他&#xff0c;除非有另外一个优先级高的实时进程。SCHED_FIFO&#xff1a;没有时间片&#xff0c;被调度器选择之后&#xff0c;可以运行任意长的时间。SCHED_RR&#xff1a;有…

c++: 理解编译器在背后所做的工作-工具篇

理解C模板以及编译器的优化是深入掌握C编程的重要部分。有一些其他工具和技术可以帮助你更好地理解编译器在背后所做的工作&#xff0c;特别是优化方面。以下是一些有用的工具和技术&#xff1a; 1. Compiler Explorer (Godbolt) Compiler Explorer 是一个非常流行的在线工具…

MybatisPlus 的入门与实践:IService接口 实现 CRUD,简化数据库操作

引言 在MybatisPlus框架中&#xff0c;IService接口扮演着重要的角色。作为一个通用的服务接口&#xff0c;IService定义了一系列数据库操作方法&#xff0c;包括查询、插入、更新、删除等。这些方法的定义使得在服务层进行数据库操作变得更为便捷和高效。 IService 概述 My…

Attention系列总结-粘贴自知乎

1. 梦想做个翟老师&#xff1a;阿里&#xff1a;Behavior Sequence Transformer 解读48 赞同 7 评论文章 优点:捕捉用户行为历史序列中的顺序信息。w2v也是捕捉用户序列信息的,本质差异在于啥&#xff1f; 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff0…

请解释Java中的策略模式,并举例说明其应用场景和实现方式。请解释Java中的模板方法模式,并讨论其在实际项目中的应用。

请解释Java中的策略模式&#xff0c;并举例说明其应用场景和实现方式。 策略模式&#xff08;Strategy Pattern&#xff09; 策略模式是一种行为设计模式&#xff0c;它使你能够定义一系列算法&#xff0c;并将每一个算法封装起来&#xff0c;使它们可以互相替换。策略模式使…

Java Collection接口及方法

Java Collection接口及方法 文章目录 Java Collection接口及方法常用方法添加判断删除其他 集合与数组的相互转换将集合转化为数组将数组转化为集合 使用要求Iterator(迭代器)接口Iterator接口如何使用迭代器遍历元素错误的遍历增强for循环的使用(JDK5.0新特性) Java 集合框架概…

昇思25天学习打卡营第2天|onereal》

今天学习内容是了解华为昇思平台。虽然打了卡&#xff0c;但是我的jupyter里面并没有播放按钮&#xff0c;所以还是无法运行代码。反映给昇思吴彦祖小哥了&#xff0c;他说需要专家帮我解决。 我还是要自我表扬一下&#xff0c;不懂就问&#xff0c;切莫不懂装懂&#xff0c;那…

基于51单片机的RFID门禁系统-LCD12864显示

一.硬件方案 本RFID系统设计可分为硬件部分和软件部分。硬件部分以MFRC522射频识别模块为核心&#xff0c;结合主控模块STC89C52设计系统的外围硬件电路&#xff0c;实现对射频卡的控制与MCU之间的互通。软件部分采用C语言进行系统的下位机程序的开发&#xff0c;完成与IC卡之…

Windows 根据github上的环境需求,安装一个虚拟环境,安装cuda和torch

比如我们在github上看到一个关于运行环境的需求 Installation xxx系统Python 3.xxx CUDA 9.2PyTorch 1.9.0xxxxxx 最主要的就是cuda和torch&#xff0c;这两个会卡很多环境的安装。 我们重新走一遍环境安装。 首先创建一个虚拟环境 conda create -n 环境名字 python3.xxx…

【算法学习】判断点在多边形内外的算法以及确定内外两点连线与边界的交点

1.前言&#xff1a; 在GIS开发中&#xff0c;经常会遇到确定一个坐标点是否在一块区域的内部这一问题。 如果这个问题不是一个单纯的数学问题&#xff0c;例如&#xff1a;在判断DEM、二维图像像素点、3D点云点等含有自身特征信息的这些点是否在一个区域范围内部的时候&#x…

Kafka Stream 流处理设计概述

Kafka Stream 流处理设计概述 Kafka 流处理是指使用 Kafka 及其生态系统中的组件来处理实时数据流。Kafka Streams 是 Kafka 官方 提供的流处理库,它简化了构建流处理应用程序的过程,并与 Kafka 无缝集成。以下是 Kafka 流处理的设 计原理和相关概念。 1. Kafka 流处理基本…

第9章 EM算法:例题及课后习题

1 概要 1&#xff0e;EM算法是含有隐变量的概率模型极大似然估计或极大后验概率估计的迭代算法。含有隐变量的概率模型的数据表示为 P ( Y , Z ∣ θ ) P(Y,Z|\theta) P(Y,Z∣θ)。这里&#xff0c; Y Y Y是观测变量的数据&#xff0c; Z Z Z是隐变量的数据&#xff0c; θ \t…

浅谈Python中kwargs、动态属性和元类

有些Python的开源库会用到类的动态属性&#xff0c;其中关键字参数是主要实现方式之一。结合使用__init__方法和**kwargs&#xff0c;可以定义一个支持任意属性的类模板。此外&#xff0c;使用元类可以定义一个更加规范的类模版&#xff0c;为类型提供统一管理和规范定义。 这…

XGBoost算法深度解析:原理、实现与应用

摘要 XGBoost&#xff08;eXtreme Gradient Boosting&#xff09;是一种高效的机器学习算法&#xff0c;以其出色的预测性能和计算效率在众多数据科学竞赛和实际应用中取得了巨大成功。本文将深入探讨XGBoost算法的基本原理、实现机制、优化技巧以及在不同领域的应用案例。 1…