LeetCode算法日記 - Day 29: 重排鏈表、合并 K 個升序鏈表

目錄

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…