算法(附思维导图+全部解法)300题(23)合并K个升序链表
零 标题:算法(leetode,附思维导图 + 全部解法)300题之(23)合并K个升序链表
导读:
项目&作者
1 GitHub - LeetCode项目仓库
0)本项目地址: https://github.com/CYBYOB/algorithm-leetcode 。 目标、愿景: 让每个人都能拥有一定的算法能力、以应对面试中(会举一反三的同学还可以将其融入自己的肌肉和血液,甚至能够赋能于公司的业务和技术)的算法。 1)项目的根目录下的 README.md 文件, 可以帮您快速查阅每1道题的来源、难度、所有的题解方案等。 2)而每个题解(即 index.md 文件)中, 还将附带题目描述、所有的题解方案的思维导图( .xmind 文件)、思路和技巧等。 3)每种题解方案都有详细的注释, 通过“数字步骤”将抽象的算法逻辑、 清晰和有层次的展示于您的面前。 可以说是, 开箱即用~ 4)所有的题解方案都是经过作者1人之手, 故代码风格及其统一。 一旦阅读达到一定量后, 后续将大大提升您的阅读速度 —— “正所谓、量变引起质变”。 5)本人每周仍在不断的更新 —— 保证每周都有新的题目、题解方案刺激着您的神经 和 刷题欲望。 欢迎对算法感兴趣的同学加入我们的社群。 QQ群: 933919972 ; 作者QQ: 1520112971 ; 作者VX: c13227839870(可拉您进群、一起学习与交流~) 。
2 作者标签
1)“伪全栈工程师,主攻前端,偶尔写点后端”。 2)2019年的微信小程序应用开发赛 - 全国三等奖; 2019CODA比赛 - 前 17/211 强 且 荣获“优秀团队”称号 等。 3)“半自媒体人”, 在校期间、个人公众号(IT三少。新自媒体(公众号)号: 码农三少 ) 在半年内实现了0到5.8K+的粉丝增长等。
一 题目描述
二 解法总览(思维导图)
三 全部解法
1 方案1
1)代码:
// 方案1 “化归思想” —— “不熟悉的 --> 熟悉的”。 // 技巧:若 我们并不知道从 A-->C 的最佳路线、但知道 B-->C 的路线, // 则 原先的 A-->C 问题就变成了 A-->B 问题。 // 步骤: // 1)初始化变量。 // 2)遍历所有链表,并将每个节点上的值 存入 数组 resArr 里。 // 3)重排 resArr ,使其变得有序。 // 4)遍历 resArr ,将每个元素存在新链表里。 // 5)返回新链表的头结点。 var mergeKLists = function(lists) { // 1)初始化变量。 const l = lists.length; let resArr = []; // 2)遍历所有链表,并将每个节点上的值 存入 数组 resArr 里。 for (let i = 0; i < l; i++) { let tempHead = lists[i]; // 2.1)取得每一个链表,不断将它们往后拉 并 存节点值 至 resArr 。 while (tempHead) { resArr.push(tempHead.val); tempHead = tempHead.next; } } // 3)重排 resArr ,使其变得有序。 resArr = resArr.sort((a, b) => a - b); // 4)遍历 resArr ,将每个元素存在新链表里。 const resArrLength = resArr.length; let index = 1, resHead = tempHead = new ListNode(resArr[0]); // 边界:若 lists 为[]、[[]] 时,则直接 return null 即可。 if (resArrLength === 0) { return null; } // 4.1)遍历 resArr ,将当前元素放入 节点 并 往后拉链表。 while (index < resArrLength) { tempHead.next = new ListNode(resArr[index]); tempHead = tempHead.next; index++; } // 5)返回新链表的头结点。 return resHead; };
2 方案2
1)代码:
// 方案2 “升级版的化归法”。 // 我们之前写过合并2个有序链表。 // 1)处理边界(其实可以省略,因为核心处理能覆盖)。如 [] 、 [[]] 或 [[1,4,5]] 等。 // 2)核心处理:不断遍历 lists ,每次取其中的2项(l1、l2)、调用 mergeTwoLists 不断将 l1、l2 合并。 // 3)返回结果 resHead(头结点) 。 var mergeKLists = function(lists) { // 合并2个有序链表(使用递归)。 const mergeTwoLists = (l1, l2) => { // 1)递归出口。 if (l1 === null) { return l2; } if (l2 === null) { return l1; } // 2)递归主体。 // 核心:此时 l1、l2 均不为null。 const val_1 = l1.val, val_2 = l2.val; if (val_1 <= val_2) { // 2.1)核心:“谁小就需要自己去主动找到自己的下家(即下一个节点 next 值)”。 l1.next = mergeTwoLists(l1.next, l2); // 并将当前小的的节点返回出去 —— 供上层调用 mergeTwoLists 的调用者使用。 return l1; } else { // 2.2)核心:“谁小就需要自己去主动找到自己的下家(即下一个节点 next 值)”。 l2.next = mergeTwoLists(l2.next, l1); // 并将当前小的的节点返回出去 —— 供上层调用 mergeTwoLists 的调用者使用。 return l2; } } const l = lists.length; // 1)边界。 if (l === 0) { return null; } if (l === 1) { return lists[0]; } // 2)核心处理:不断遍历 lists ,每次取其中的2项(l1、l2)、调用 mergeTwoLists 不断将 l1、l2 合并。 // 技巧:数组的“多 变 一” —— 优先考虑 reduce(或 reduceRight) 函数。 const resHead = lists.reduce((acc, cur) => { acc = mergeTwoLists(acc, cur); return acc; }, null); // 3)返回结果 resHead(头结点) 。 return resHead; }
3 方案3
1)代码:
// 方案3 ”N指针法“。通过 24 / 133 ,TODO:写法有问题,待完善~ // 注:此时, N = K 。 var mergeKLists = function(lists) { // 获取下一个节点(即 tempHeadList 里最小值对应的节点)。 const getNextNodeByTempHeadList = (tempHeadList) => { // 边界:当前位于“头结点”为null时,不参与“比较”过程。 tempHeadList = tempHeadList.filter(item => item !== null); // 1)找出目前最小值对应的下标 const l = tempHeadList.length; let index = 0, resMinIndex = -1, resMinVal = Number.POSITIVE_INFINITY; while (index < l) { const tempVal = tempHeadList[index].val; if (tempVal <= resMinVal) { resMinVal = tempVal; resMinIndex = index; } index++; } // 边界:目前最小值对应的下标 为 -1时。 if (resMinIndex === -1) { return [null, []]; } const tempHeadNew = tempHeadList[resMinIndex]; tempHeadList[resMinIndex] = tempHeadList[resMinIndex].next; // 2)返回下一个节点(tempHeadNew) 和 新的tempHeadList。 return [tempHeadNew, tempHeadList]; } const l = lists.length; let tempHeadList = []; // 1)遍历 lists ,将每一组的“头结点(非 null 时)”放入 tempHeadList 中, // tempHeadList 作为 获取下一个节点(即 tempHeadList 里最小值对应的节点) // —— getNextNodeByTempHeadList的输入参数。 for (let i = 0; i < l; i++) { if (lists[i] !== null) { tempHeadList.push(lists[i]); } } let resHead = null, tempHead = null; while (tempHeadList.length !== 0) { const [tempHeadNew, tempHeadListNew] = getNextNodeByTempHeadList(tempHeadList); // 边界 if (tempHeadNew === null) { break; } if (resHead === null) { resHead = tempHead = tempHeadNew; } else { tempHead = tempHeadNew; } tempHead = tempHead.next; tempHeadList = tempHeadListNew; } // 2)返回结果 return resHead; }
4 方案4
1)代码:
class Solution { public ListNode mergeKLists(ListNode[] lists) { int k = lists.length; ListNode dummyHead = new ListNode(0); ListNode tail = dummyHead; while (true) { ListNode minNode = null; int minPointer = -1; for (int i = 0; i < k; i++) { if (lists[i] == null) { continue; } if (minNode == null || lists[i].val < minNode.val) { minNode = lists[i]; minPointer = i; } } if (minPointer == -1) { break; } tail.next = minNode; tail = tail.next; lists[minPointer] = lists[minPointer].next; } return dummyHead.next; } } 参考: 1)https://leetcode-cn.com/problems/merge-k-sorted-lists/solution/4-chong-fang-fa-xiang-jie-bi-xu-miao-dong-by-sweet/
四 内推&更多
1 内推
本人是百度的1名工程师,欢迎校招、社招同学向本人投递简历。
本人可内推公司(也可帮忙内推 阿里、腾讯、字节、美团、滴滴、京东等)的所有岗位,欢迎私信。
2 更多
以下是个人整理的一些笔记和书籍(永久有效链接: https://pan.baidu.com/s/1SPc3umO6cZlBtoPylSaHzw 密码: eqee ,若失效的话可私信本人 或 向VX公众号 “码农三少” 发送 “资料” 以进行最新资料的获取):