目录

1. 重排链表

1.1 题目解析

1.2 解法

1.3 代码实现

2. 合并 K 个升序链表

2.1 题目解析

2.2 解法

2.3 代码实现


1. 重排链表

143. 重排链表 - 力扣(LeetCode)

给定一个单链表 L 的头节点 head ,单链表 L 表示为:

L0 → L1 → … → Ln - 1 → Ln

请将其重新排列后变为:

L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …

不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例 1:

输入:head = [1,2,3,4]
输出:[1,4,2,3]

示例 2:

输入:head = [1,2,3,4,5]
输出:[1,5,2,4,3]

提示:

  • 链表的长度范围为 [1, 5 * 104]
  • 1 <= node.val <= 1000

1.1 题目解析

题目本质
把链表“重排”为首尾交替:L0, L1, …, Ln → L0, Ln, L1, Ln-1, …。本质是按位置重新连接指针,不能改节点值、不能新建整条链,只能原地改 .next。

常规解法
最直观:把所有节点放进数组,然后双指针从两端往中间,按顺序重连。

问题分析
数组法需要 O(n) 额外空间,不满足“尽量原地”的典型考点;且频繁重连要小心断链与成环。期望是 O(1) 额外空间、O(n) 时间 的指针操作。

思路转折
要首尾交替,其实就是:
1)找到中点,把链表一分为二;
2)反转后半段,使其顺序变为 Ln, Ln-1, …;
3)交替合并前半(L0, L1, …)和反转后的后半(Ln, …)。
这样天然实现“首尾交替”。中点建议取左中点((while (fast.next != null && fast.next.next != null)),保证后半长度 ≤ 前半,合并时只需“把后半用尽”为止,循环条件写 while (p2 != null) 最稳。

1.2 解法

算法思想
设链表为 head:

  • 慢快指针找左中点 slow;

  • 断开:second = slow.next; slow.next = null;

  • 反转 second 得 revSecond;

  • 两指针 p1 = head, p2 = revSecond,交替接:

    n1 = p1.next; n2 = p2.next;
    p1.next = p2; p2.next = n1;
    p1 = n1; p2 = n2;
    
  • 当 p2 用尽,结束(奇数长度时前半多一个尾结点,正好留在末尾)。

i)左中点:while (fast.next != null && fast.next.next != null) { slow=slow.next; fast=fast.next.next; }

ii)断开:second = slow.next; slow.next = null;

iii)反转后半:三指针 prev/curr/next 原地反转,返回新头 prev。

iiii)交替合并:每轮先保存 n1 = p1.next, n2 = p2.next,再改指针,最后推进到 n1/n2。

iiiii)结束:函数为 void,就地修改,无需返回。

易错点

  • 早退条件:if (head == null || head.next == null) return;(不是 &&)。

  • 快慢指针写法:为得到左中点,条件用 fast.next != null && fast.next.next != null。

  • 断开前半尾:slow.next = null; 必须断开,否则可能成环。

  • 反转必须先存 next:next:next = curr.next; 之后再改 curr.next = prev,否则断路找不到后继。

  • 合并时别用错指针:保存/连接用 p1/p2 的 next,不是 head/second 的 next。

  • 循环条件:while (p2 != null),因为后半长度 ≤ 前半,合并完后半即完成。

  • 不创建新节点:只能改 .next,不能重新 new 组成整条链。

1.3 代码实现

/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public void reorderList(ListNode head) {// 0) 早退:空链或单节点,无需处理if (head == null || head.next == null) return;// 1) 快慢指针找“左中点” slowListNode slow = head, fast = head;while (fast.next != null && fast.next.next != null) {slow = slow.next;fast = fast.next.next;}// 2) 断开并反转后半段ListNode second = slow.next;slow.next = null;                 // 断开前半尾second = reverse(second);         // 原地反转后半// 3) 交替合并:前半(head)与后半(second, 已反转)ListNode p1 = head, p2 = second;while (p2 != null) {ListNode n1 = p1.next, n2 = p2.next; // 先保存后继,避免断路p1.next = p2;p2.next = n1;p1 = n1;p2 = n2;}// 就地修改,void 无需返回}// 三指针原地反转private ListNode reverse(ListNode head) {ListNode prev = null, curr = head;while (curr != null) {ListNode next = curr.next; // 保存旧路口curr.next = prev;          // 反转指向prev = curr;               // prev 前进curr = next;               // curr 走向旧路口}return prev; // 新头}
}

复杂度分析

  • 时间复杂度:O(n)(找中点 O(n/2) + 反转 O(n/2) + 合并 O(n))。

  • 空间复杂度:O(1)(只用常数级指针变量,原地修改)。

2. 合并 K 个升序链表

23. 合并 K 个升序链表 - 力扣(LeetCode)

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[1->4->5,1->3->4,2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

示例 2:

输入:lists = []
输出:[]

示例 3:

输入:lists = [[]]
输出:[]

提示:

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i] 按 升序 排列
  • lists[i].length 的总和不超过 10^4

2.1 题目解析

题目本质
把 k 条已升序的链表合成 1 条升序链表。抽象成:从 k 个“有序流”的当前表头中,持续选出最小元素并接到结果尾部,直到所有流耗尽。

常规解法
每一步在 k 个表头里线性找最小,接到结果链表尾部。

问题分析
线性扫描每步 O(k),总共有 N 次选择(N 为所有节点总数),总时间 O(N·k)。当 k 很大(最多 10^4)时会明显超时。

思路转折
要把“每步找最小”从 O(k) 降到 O(log k)

  • 路线 A:小根堆装 k 个表头,poll/offer 均 O(log k),整体 O(N log k)

  • 路线 B:分治两两合并(像归并排序):每层线性合并,总层数 log k,整体 O(N log k)
    建议:

  • 代码短、直观:小根堆

  • 不想引入堆、掌握分治:两两合并

2.2 解法

2.2.1 分治两两合并
把区间 [0..k-1] 的链表递归二分为 [l..m] 和 [m+1..r],分别合并成两条有序链,再调用“合并两条有序链表”得到当前区间的答案。
递推:mergeRange(l,r) = mergeTwo( mergeRange(l,m), mergeRange(m+1,r) )。

i)边界:lists == null或lists.length == 0 返回 null;l==r 返回 lists[l](可为 null)。

ii)递归:中点 m = l + (r-l)/2,分别合并左右。

iii)合并两条链:用局部 dummy 与 tail,每次接更小的那个节点;某一条耗尽后把另一条一次性挂尾

易错点

  • dummy/tail 必须是合并函数的局部变量,不要复用全局,否则容易把不同子问题的结果“接成环”,遍历超时。

  • 合并时推进顺序:tail.next = a/b → 移动 a/b = a/b.next → tail = tail.next。

  • 收尾:不要循环一个个接剩余部分,直接 tail.next = (a!=null ? a : b)。

  • 输入可能包含空链:初始化和递归都要兼容 null。

2.2.2 小根堆
维护一个小根堆保存“每条链当前表头”,每次弹出最小节点接入结果;若该节点有 next,把 next 放回

易错点

  • Java 用 PriorityQueue<ListNode>,比较器使用 Integer.compare(a.val, b.val),避免 b - a 溢出。

  • 只把表头入堆,而不是把整条链全部入堆。

2.3 代码实现

方案一:分治两两合并

/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode mergeKLists(ListNode[] lists) {if (lists == null || lists.length == 0) return null;return mergeRange(lists, 0, lists.length - 1);}private ListNode mergeRange(ListNode[] lists, int l, int r) {if (l == r) return lists[l];                 // 可能为 null,无妨int m = l + (r - l) / 2;ListNode a = mergeRange(lists, l, m);ListNode b = mergeRange(lists, m + 1, r);return mergeTwo(a, b);}private ListNode mergeTwo(ListNode a, ListNode b) {ListNode dummy = new ListNode(0);ListNode tail  = dummy;while (a != null && b != null) {if (a.val <= b.val) {tail.next = a; a = a.next;} else {tail.next = b; b = b.next;}tail = tail.next;}tail.next = (a != null) ? a : b;             // 一次性挂尾return dummy.next;}
}

复杂度分析

  • 时间:O(N log k)(每层线性合并,总层数约 log k)。

  • 空间:O(log k)(递归栈;若考虑输出链表为必需空间则不计入额外空间)。

方案二:小根堆

import java.util.PriorityQueue;class Solution {public ListNode mergeKLists(ListNode[] lists) {if (lists == null || lists.length == 0) return null;PriorityQueue<ListNode> pq = new PriorityQueue<>((x, y) -> Integer.compare(x.val, y.val));for (ListNode h : lists) if (h != null) pq.offer(h);ListNode dummy = new ListNode(0), tail = dummy;while (!pq.isEmpty()) {ListNode node = pq.poll();tail.next = node; tail = node;if (node.next != null) pq.offer(node.next);}return dummy.next;}
}

复杂度分析

  • 时间:O(N log k)。

  • 空间:O(k)(堆大小)。

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

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

相关文章

算法模板(Java版)_前缀和与差分

ZZHow(ZZHow1024) &#x1f4a1; 差分是前缀和的逆运算。 前缀和 &#x1f4a1; 前缀和作用&#xff1a;快速求出 [l, r] 区间的和。 一维前缀和 例题&#xff1a;AcWing 795. 前缀和 import java.util.Scanner;public class Main {public static void main(String[] args)…

openssl使用SM2进行数据加密和数据解密

一、准备工作 1. 安装依赖 sudo apt-get update sudo apt-get install libssl-dev2. 确认 OpenSSL 版本 openssl version如果是 1.1.1 或 3.0&#xff0c;就支持 SM2/SM3/SM4。二、C 语言示例代码 这个程序会&#xff1a; 生成 SM2 密钥对使用公钥加密一段明文使用私钥解密恢复…

用滑动窗口与线性回归将音频信号转换为“Token”序列:一种简单的音频特征编码方法

在深度学习和语音处理领域&#xff0c;如何将原始音频信号有效地表示为离散的“Token”序列&#xff0c;是语音识别、音频生成等任务中的关键问题。常见的方法如Mel频谱图向量量化&#xff08;VQ&#xff09;、wav2vec等已经非常成熟&#xff0c;但这些模型通常依赖复杂的神经网…

Vue开发准备

vs code VSCode的下载地址https://code.visualstudio.com/Download Node.js node.js的下载地址 https://nodejs.org/zh-cn/download 注意&#xff1a;nodejs安装路径不要和vscode安装到同一个文件夹&#xff0c;两个应用分别装到两个不同的文件夹 npm config set cache &q…

QT6(QFileSystemModel和QTreeView)

QT6QFileSystemModel和QTreeView QFileSystemModel为本机的文件系统提供一个模型&#xff0c;QFileSystemModelt和QTreeView结合使用&#xff0c;可以用目录树的形式显示本机的文件系统&#xff0c;如同Windows的资源管理器一样使用QFileSystemModel提供的接口函数&#xff0c;…

【开题答辩全过程】以 基于Spring Boot的房屋租赁系统的设计与实现为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人&#xff0c;语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…

构建下一代智能金融基础设施

1. 行业背景&#xff1a;从数字支付到可编程金融的范式跃迁全球数字支付市场正以万亿美元的规模持续扩张&#xff0c;但其底层系统仍受限于传统金融的清算、结算延迟和高昂的中间成本。尽管互联网技术提升了支付的便捷性&#xff0c;但其核心仍是中心化账户体系的延伸。Web3 技…

【C++】深入解析C++嵌套依赖类型与typename关键字

什么是嵌套依赖类型&#xff1f;嵌套依赖类型&#xff08;Nested Dependent Type&#xff09;是指在一个模板中&#xff0c;一个类型名称依赖于模板参数&#xff0c;并且是该模板参数内部的嵌套类型。具体来说&#xff0c;当一个类型满足以下两个条件时&#xff0c;它就是嵌套依…

管网信息化监测主要的内容

管网信息化监测是指通过现代信息技术手段对管网系统进行实时监控和数据采集的管理方式。其背景源于城市化进程加快以及基础设施建设规模不断扩大&#xff0c;传统的管网管理模式已无法满足现代化需求。管网信息化监测主要内容包括以下几个方面&#xff1a;█管网运行状态监测&a…

数据泄露代价千万,PII 保护你真的做对了吗?

一、PII—数据隐私的核心概念解析 在大多数数据隐私法律中,可识别个人信息(PII, Personally Identifiable Information)是指任何可以用来识别个人身份的信息。然而,PII 的定义并非由单一法律统一规定,不同国家和地区的法律对其定义略有差异: 各国对 PII 的定义 美国 20…

【数据结构】八大排序之快速排序:分而治之的艺术

文章目录快速排序1.hoare版本算法优化三数取中法小区间优化完整代码如下算法分析时间复杂度空间复杂度2.前后指针法排序过程3.非递归&#xff08;栈模拟&#xff09;实现思路总结快速排序 快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法&#xff0c;其基本思想为…

在ROS中获取并发布UBS式传感器的温湿度

哈喽大家好&#xff0c;我是钢板兽&#xff01; 今天更新一篇和ROS相关的文章&#xff0c;有个项目需求是在ROS中获取并发布UBS式传感器的温湿度&#xff0c;我使用的温湿度传感器简介如下&#xff1a;DL11- MC-S1 温湿度传感器通过USB 接口采用标准MODBUS RTU 协议通信&#x…

【图论】 Graph.jl 操作汇总

文章目录图论的集合类操作Base.getindexBase.intersectBase.joinBase.reverseBase.reverse!Base.sizeBase.sumBase.sumBase.union图生成与转换Graphs.cartesian_productGraphs.complementGraphs.compute_shiftsGraphs.crosspathGraphs.differenceGraphs.egonetGraphs.induced_s…

【链表 - LeetCode】146. LRU 缓存

146. LRU 缓存 题解&#xff1a; class LRUCache {list<pair<int,int>>v;unordered_map<int,list<pair<int,int>>::iterator>idx;int capacity; public:LRUCache(int capacity):capacity(capacity){}int get(int key) {if(idx.count(key) 0) …

Elasticsearch vs Solr vs OpenSearch:搜索引擎方案对比与索引设计最佳实践

Elasticsearch vs Solr vs OpenSearch&#xff1a;搜索引擎方案对比与索引设计最佳实践 随着大数据和实时分析需求的爆发&#xff0c;搜索引擎已成为许多业务系统中的核心组件。本篇文章将从“技术方案对比分析型”角度切入&#xff0c;重点比较三大主流搜索引擎&#xff1a;El…

光颉科技)Viking)的CS25FTFR009 1225 0.009R/9mR 3W电阻介绍-华年商城

“**华年商城”**小编为您介绍&#xff1a;光颉科技&#xff08;Viking&#xff09;的CS25FTFR009 1225 0.009R/9mR 3W电阻 光颉CS25FTFR009合金电阻&#xff1a;0.009Ω/9mΩ 3W 1%精密采样电阻 光颉科技&#xff08;Viking&#xff09;的CS25FTFR009是一款高性能的电流检测电…

港科大开放世界长时域具身导航!LOVON:足式机器人开放词汇目标导航

作者&#xff1a;Daojie Peng1^{1}1, Jiahang Cao1,2^{1,2}1,2, Qiang Zhang1,2^{1,2}1,2, Jun Ma1,3^{1,3}1,3单位&#xff1a;1^{1}1香港科技大学&#xff08;广州&#xff09;&#xff0c;2^{2}2北京人形机器人创新中心&#xff0c;3^{3}3香港科技大学论文标题&#xff1a;L…

【前端教程】JavaScript 数组对象遍历与数据展示实战

在前端开发中&#xff0c;处理数组和对象是日常工作的基础。无论是篇文章将通过一个具体案例&#xff0c;详细讲解如何使用JavaScript遍历包含对象的数组&#xff0c;并将数据以清晰的格式展示在页面上。我们会从基础语法开始&#xff0c;逐步优化代码&#xff0c;最终实现一个…

无重复字符的最长子串,leetCode热题100,C++实现

题目来源&#xff1a;leetCode 3. 无重复字符的最长子串 - 力扣&#xff08;LeetCode&#xff09; 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长 子串 的长度。 解法 class Solution { public:int lengthOfLongestSubstring(string s) {unordered_set<…

卷积神经网络中1×1卷积的作用

part I &#xff1a;来源part II &#xff1a;应用part III &#xff1a;作用&#xff08;降维、升维、跨通道交互、增加非线性&#xff09;part IV &#xff1a;从fully-connected layers的角度理解一、来源&#xff1a;[1312.4400] Network In Network &#xff08;如果11…