问题:
给你一个链表数组,每个链表都已经按升序排列。
请将所有链表合并到一个升序链表中,返回合并后的链表。
示例 1:
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
javascript
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
javascript
1->1->2->3->4->4->5->6
示例 2:
输入:lists = []
输出:[]
示例 3:
输入:lists = [[]]
输出:[]
解题思路
将所有值放进数组中排序,再新造ListNode
javascript
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode[]} lists
* @return {ListNode}
*/
var mergeKLists = function (lists) {
// 初始化一个数组来存储所有链表的值
const list = [];
// 遍历所有链表
for (let i = 0; i < lists.length; i++) {
let node = lists[i];
// 遍历链表并收集值
while (node) {
list.push(node.val);
node = node.next;
}
}
// 排序所有值
list.sort((a, b) => a - b);
// 创建结果链表的头节点
const res = new ListNode();
// 使用哑节点简化链表构建过程
let now = res;
// 构建排序后的链表
for (let i = 0; i < list.length; i++) {
now.next = new ListNode(list[i]);
now = now.next;
}
// 返回合并和排序后的链表的头节点
return res.next;
};
代码解释:
这段代码通过嵌套循环遍历了 lists
数组中的每个链表。lists
数组包含了 k
个链表,每个链表由 ListNode
对象组成。以下是遍历过程的详细解释
1.初始化外部循环:
使用 for
循环初始化索引 i
,从 0 开始,直到 lists.length
(即 k
),因为 lists
数组的长度表示链表的数量。
2.获取当前链表:
在每次迭代中,lists[i]
获取 lists
数组中第 i
个链表的引用。
3.初始化内部循环:
let node = lists[i]
声明了一个变量 node
,它指向当前链表的头节点。
4.遍历当前链表:
使用 while
循环遍历当前链表。循环条件是 node
不为 null
,即链表尚未到达末尾。
5.收集节点值:
在 while
循环内部,node.val
获取当前节点的值,并将其添加到 list
数组中:list.push(node.val)
。
6.移动到下一个节点:
node = node.next
将 node 指针移动到链表的下一个节点。
7.继续遍历:
如果 node
仍然不是 null
,则继续循环,重复步骤 5 和 6。
8.完成当前链表的遍历:
当 node
为 null
时,while
循环结束,表示当前链表已遍历完毕。
9.移动到下一个链表:
for
循环的下一次迭代将 i
增加,并将 node
重置为下一个链表的头节点。
10.完成所有链表的遍历:
当 i
达到 lists.length
时,for
循环结束,表示所有链表都已遍历完毕。
示例:
假设 lists 包含以下链表:
javascript
lists[0]: 1 -> 3 -> 5
lists[1]: 2 -> 4
lists[2]: 6
遍历过程如下:
i = 0:
node
从 1 遍历到 5,将值推入 list
。
i = 1:
node
从 2 遍历到 4,将值推入 list
。
i = 2:
node
从 6 开始,将其值推入 list
。
最终,list
数组包含所有链表的值 [1, 3, 5, 2, 4, 6]
。
这种嵌套循环的方法确保了所有 k
个链表中的所有节点都被访问并收集其值。
在这个 mergeKLists
函数中,res
是一个新创建的 ListNode
实例,用作合并后链表的头节点。然而,这个头节点最初是空的,它的 next
指针被初始化为 null
。now
指针用于在构建新链表的过程中遍历链表并添加新的节点。
解释 res
和 now
:
res
是合并后链表的头节点,但在初始化时,res.next
是null
。now
是一个辅助指针,用于在链表构建过程中指向当前正在操作的节点。
构建链表的过程:
1.首先,创建一个空的头节点 res
。
2.使用 now
指针从 res
开始,逐个添加新节点到链表中。
3.在每次迭代中,将 now.next
设置为一个包含当前值的新 ListNode
,然后将 now
移动到这个新节点。
为什么返回 res.next 而不是 now.next:
在链表构建的最后,now
指针位于链表的末尾(最后一个节点)。如果返回 now.next
,这将返回链表的最后一个节点之后的 null
指针,这不是链表的有效部分。
res 是链表的头节点,即使在迭代过程中 now
移动了,res
始终指向链表的开始。因此,res.next
是链表的第一个有效节点,这是我们想要返回的。
总结:
返回 res.next
是因为我们想要返回合并后链表的头节点,它是链表的第一个有效节点。now
指针仅用于遍历和构建链表,而 res
始终保持对链表头部的引用。