算法-堆、队列、分治法-合并 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;
    }
}
相关推荐
地平线开发者3 小时前
SparseDrive 模型导出与性能优化实战
算法·自动驾驶
董董灿是个攻城狮4 小时前
大模型连载2:初步认识 tokenizer 的过程
算法
地平线开发者4 小时前
地平线 VP 接口工程实践(一):hbVPRoiResize 接口功能、使用约束与典型问题总结
算法·自动驾驶
罗西的思考4 小时前
AI Agent框架探秘:拆解 OpenHands(10)--- Runtime
人工智能·算法·机器学习
HXhlx8 小时前
CART决策树基本原理
算法·机器学习
Wect8 小时前
LeetCode 210. 课程表 II 题解:Kahn算法+DFS 双解法精讲
前端·算法·typescript
颜酱9 小时前
单调队列:滑动窗口极值问题的最优解(通用模板版)
javascript·后端·算法
Gorway15 小时前
解析残差网络 (ResNet)
算法
拖拉斯旋风16 小时前
LeetCode 经典算法题解析:优先队列与广度优先搜索的巧妙应用
算法
Wect16 小时前
LeetCode 207. 课程表:两种解法(BFS+DFS)详细解析
前端·算法·typescript