算法-堆、队列、分治法-合并 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;
    }
}
相关推荐
智能优化与强化学习4 小时前
Gym(Gymnasium)仿真环境详解(二):环境简介、入门算法、调参要点、核心挑战
算法·强化学习·gym·零基础入门·算法评估
mxwin4 小时前
Unity Shader exp 函数的算法与渲染应用
算法·unity·游戏引擎·shader
“码”力全开4 小时前
AI视频分析误报优化完整流程
算法·架构·边缘计算
深盾科技_Virbox4 小时前
深盾科技·Virbox产品体系全景解读:软件安全如何从加密锁走向全生命周期
java·大数据·算法·安全·软件需求
可编程芯片开发5 小时前
基于VSG虚拟同步发电机控制的三相并网逆变器带多组可变负载Simulink建模与仿真
算法
AI服务老曹5 小时前
国产NPU视觉算法参数配置说明
算法·性能优化·边缘计算
彦为君5 小时前
Redis最新版本特性
java·数据库·redis·算法·bootstrap
触底反弹5 小时前
🔥 字符串算法面试三连击:反转、回文、回文变种,搞懂这三题稳了!
前端·javascript·算法
aaaameliaaa6 小时前
计算斐波那契数(递归、迭代)(1,1,2,3,5.....)
c语言·开发语言·笔记·算法·排序算法
Jerry6 小时前
LeetCode 977. 有序数组的平方
算法