算法-堆、队列、分治法-合并 K 个升序链表

算法-堆、队列、分治法-合并 K 个升序链表

1 题目概述

1.1 题目出处

https://leetcode.cn/problems/merge-k-sorted-lists

1.2 题目描述

2 题解

2.1 队列

2.1.1 解题思路

将各个有序子链表放入队列,两两合并,最后队列中剩的最后的子链表就是合并后的结果。

2.1.2 代码

java 复制代码
/**
 * 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.length == 0) {
            return null;
        }
        Queue<ListNode> queue = new LinkedList<>();
        for(ListNode node : lists) {
            if (node != null) {
                queue.add(node);
            }
        }
        while (queue.size() > 1) {
            ListNode left = queue.poll();
            ListNode right = queue.poll();
            queue.add(mergeKLists(left, right));
        }
        
        return queue.poll();
    }
    private ListNode mergeKLists(ListNode left, ListNode right) {
        ListNode head = new ListNode();
        ListNode tmpH = head;
        ListNode tmpL = left;
        ListNode tmpR = right;
        while (tmpL != null && tmpR != null) {
            if (tmpL.val < tmpR.val) {
                tmpH.next = tmpL;
                tmpL = tmpL.next;
            } else {
                tmpH.next = tmpR;
                tmpR = tmpR.next;
            }
            tmpH = tmpH.next;
        }
        if (tmpL == null) {
            tmpH.next = tmpR;   
        }
        if (tmpR == null) {
            tmpH.next = tmpL;   
        }
        return head.next;
    }
}

2.1.3 时间复杂度

参考 https://leetcode.cn/problems/merge-k-sorted-lists/solutions/219756/he-bing-kge-pai-xu-lian-biao-by-leetcode-solutio-2/?envType=study-plan-v2\&envId=top-interview-150

2.1.4 空间复杂度

O(log⁡k)

递归会使用到 O(log⁡k)空间代价的栈空间。

2.2 堆-由优先级队列实现

2.2.1 解题思路

  1. 将所有元素一次插入小顶堆
  2. 将小顶堆的所有元素依次取出并组成链表
  3. 最后得到的链表就是从小到大排列的链表

2.2.2 代码

java 复制代码
/**
 * 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) {
        ListNode head = new ListNode();
        if (lists.length == 0) {
            return null;
        }

        PriorityQueue<ListNode> queue = new PriorityQueue<>((o1, o2) -> {
            return o1.val - o2.val;
        });

        for (ListNode listNode : lists) {
            if (null == listNode) {
                continue;
            }
            ListNode tmp = listNode;
            while (null != tmp) {
                queue.add(tmp);
                tmp = tmp.next;
            }
        }
        ListNode tmp = queue.poll();
        head.next = tmp;
        while (queue.size() > 0) {
            tmp.next = queue.poll();
            tmp = tmp.next;
        }
        if (null != tmp) {
            tmp.next = null;
        }
        
        return head.next;
    }
}

2.2.3 时间复杂度

O(NKlogNK)

2.2.4 空间复杂度

O(NK)

2.3 堆-由自己实现堆-优化

2.2.1 解题思路

  1. 将K个队列的元素每次都拿1个插入小顶堆,组成一个大小为K的小顶堆
  2. 每次将小顶堆的堆顶元素取出,并将该元素的下一个元素放入小顶堆
  3. 最后得到的链表就是从小到大排列的链表

2.2.2 代码

java 复制代码
/**
 * 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; }
 * }
 */
/**
 * 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 {
    private class MinHeap {
        private List<ListNode> list = new ArrayList<>();
        public void add(ListNode node) {
            // 添加时,先把node放到小顶堆尾
            list.add(node);
            int i = list.size() - 1;
            ListNode tmp = node;
            while (i > 0) {
                // 每次和父节点对比,
                // 这是小顶堆,则如果当前节点比父节点更小,就和父节点交还位置
                int parent = (i - 1) / 2;
                if (tmp.val < list.get(parent).val) {
                    list.set(i, list.get(parent));  
                    i = parent;
                } else {
                    break;
                }
            }
            // 最后i的位置就是合适位置了,将目标节点放入即可
            list.set(i, tmp);
        }
        public ListNode poll() {
            if (size() == 0) {
                return null;
            }
            // 小顶堆取节点时,直接取首节点即val最小的节点
            ListNode target = list.get(0);
            if (size() == 1) {
                list.remove(0);
                return target;
            }
            // 将堆顶节点和尾节点交换位置,并从上至下开始调整堆
            list.set(0, list.get(size() - 1));
            list.remove(size() - 1);
            int i = 0;
            ListNode tmp = list.get(0);
            while ((i * 2 + 1) < size()) {
                int child = i * 2 + 1;
                if (child < list.size() - 1 && list.get(child).val > list.get(child + 1).val) {
                    child = child + 1;
                }
                if (tmp.val > list.get(child).val) {
                    list.set(i, list.get(child));
                    i = child;
                } else {
                    break;
                }
            }
            list.set(i, tmp);
            return target;
        }
        public int size() {
            return list.size();
        }
    }
    public ListNode mergeKLists(ListNode[] lists) {       
        if (lists.length == 0) {
            return null;
        }
        MinHeap heap = new MinHeap();
        for(ListNode node : lists) {
            if (node != null) {
                heap.add(node);
            }
        }
        ListNode dummpy = new ListNode();
        ListNode tail = dummpy;
        while (heap.size() > 0) {
            ListNode node = heap.poll();
            tail.next = node;
            tail = node;
            if (null != node.next) {
                heap.add(node.next);
            }
        }
        
        return dummpy.next;
    }
}
相关推荐
啊阿狸不会拉杆2 小时前
《算法导论》第 13 章 - 红黑树
数据结构·c++·算法·排序算法
三次拒绝王俊凯3 小时前
用生活日常的案例来介绍“程序运行时,对函数的调用一般有两种形式:传值调用和引用调用 和 这两种调用有什么区别?
java·数据结构
qiuyunoqy3 小时前
蓝桥杯算法之搜索章 - 3
c++·算法·蓝桥杯·深度优先·dfs·剪枝
lifallen4 小时前
Kafka ISR机制和Raft区别:副本数优化的秘密
java·大数据·数据库·分布式·算法·kafka·apache
m0_626535204 小时前
贪心算法学习 3 买卖股票的最佳时机 i ii
学习·算法·贪心算法
zxctsclrjjjcph5 小时前
【递归、搜索和回溯】FloodFill 算法介绍及相关例题
c++·算法·leetcode·宽度优先·深度优先遍历
机器学习之心5 小时前
灰狼算法+四模型对比!GWO-CNN-LSTM-Attention系列四模型多变量时序预测
算法·cnn·lstm·gwo-cnn-lstm
GoodTime5 小时前
Datawhale AI夏令营 -「多模态RAG图文问答挑战赛」
人工智能·python·算法
2501_924877356 小时前
化工安防误报率↓82%!陌讯多模态融合算法实战解析
大数据·算法·目标跟踪·边缘计算
2501_924747456 小时前
强光干扰下误报率↓82%!陌讯多模态算法在睡岗检测的落地优化
人工智能·深度学习·算法·目标检测·计算机视觉