technology-note/算法/leetcode/常规题/Q23. 合并K个升序链表.md
2022-02-07 17:06:38 +08:00

120 lines
4.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
id: "20220207"
date: "2022/02/07 10:38:05"
title: "leetcode.Q23.合并K个升序链表"
tags: ["java", "leetcode", "链表", "多路归并"]
index_img: https://qiniupic.fleyx.com/blog/202202071446729.png
banner_img: https://qiniupic.fleyx.com/blog/202202071447602.jpg
categories:
- "算法"
- "leetcode刷题"
---
## 解析思路
leetcode 困难,题目描述[点击这里](https://leetcode-cn.com/problems/merge-k-sorted-lists/)。
本题属于多路归并类题型。解法也是多路归并,但是怎么归呢?
对于两个有序链表的合并可以通过直接比较链表头的元素大小,将小的拿出来放到新的链表中,但是直接比较无法用于链表数不确定的情况。对于多路归并主要有以下几种办法:
<!-- more -->
1. 依次归并.先让第 1 和第 2 做二路归并,将结果再和第 3 做二路归并,知道全部归并完毕
2. 分治法.先将链表两两做二路归并,此次链表数变成 n/2(链表数为奇数时 n/2+1),然后再对结果两两做二路归并,知道最后只剩两条,继续归并即得到结果
3. 优先队列法.类比二路归并,比较两个数中的最小值,多路归并只需要比较多个数中的最小值即可,但是如果每次都重新比较一遍会比较慢,可以通过优先队列(最小堆)这种数据结构来避免多次归并,每次从队列中取出一个节点,并将这个节点的下一个节点加入队列(如果存在),就能避免重复比较。
## 代码
### 依次归并
```java
public ListNode so1(ListNode[] lists) {
List<ListNode> listNodeList = Arrays.stream(lists).filter(Objects::nonNull).collect(Collectors.toList());
if (listNodeList.isEmpty()) {
return null;
}
ListNode res = listNodeList.get(0);
for (int i = 1; i < listNodeList.size(); i++) {
//依次和第i合并
ListNode tempRes = new ListNode(), temp = tempRes;
ListNode cur = listNodeList.get(i);
while (res != null && cur != null) {
if (res.val < cur.val) {
temp.next = res;
res = res.next;
} else {
temp.next = cur;
cur = cur.next;
}
temp = temp.next;
}
//接上剩下的部分
temp.next = res == null ? cur : res;
//临时结果
res = tempRes.next;
}
return res;
}
```
### 分治法
```java
public ListNode so2(ListNode[] lists) {
List<ListNode> listNodeList = Arrays.stream(lists).filter(Objects::nonNull).collect(Collectors.toList());
int n = listNodeList.size();
if (n == 0) {
return null;
}
while (n > 1) {
int count = 0;
for (int i = 0; i + 1 < n; i += 2, count++) {
ListNode a = listNodeList.get(i), b = listNodeList.get(i + 1), head = new ListNode(), temp = head;
while (a != null && b != null) {
if (a.val < b.val) {
temp.next = a;
a = a.next;
} else {
temp.next = b;
b = b.next;
}
temp = temp.next;
}
temp.next = a == null ? b : a;
listNodeList.set(count, head.next);
}
//如果为奇数要把最后一个落单的放到count上
if (n % 2 != 0) {
listNodeList.set(count++, listNodeList.get(n - 1));
}
//新的链表数量为count
n = count;
}
return listNodeList.get(0);
}
```
### 优先队列
```java
public ListNode mergeKLists(ListNode[] lists) {
PriorityQueue<ListNode> queue = new PriorityQueue<>(Comparator.comparingInt(a -> a.val));
queue.addAll(Arrays.stream(lists).filter(Objects::nonNull).collect(Collectors.toList()));
if (queue.isEmpty()) {
return null;
}
ListNode res = queue.poll(), temp = res;
while (!queue.isEmpty()) {
if (temp.next != null) {
queue.add(temp.next);
}
temp.next = queue.poll();
temp = temp.next;
}
return res;
}
```