---
id: "20220207"
date: "2022/02/07 10:38:05"
title: "leetcode.Q23.合并K个升序链表"
tags: ["java", "leetcode", "链表", "多路归并"]
index_img: https://qiniupic.fleyx.com/blog/202202071447602.jpg?imageView2/2/w/200
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;
}

```
