算法-堆、队列、分治法-合并 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;
    }
}
相关推荐
涅槃寂雨23 分钟前
C语言小任务——寻找水仙花数
c语言·数据结构·算法
『往事』&白驹过隙;30 分钟前
操作系统(Linux Kernel 0.11&Linux Kernel 0.12)解读整理——内核初始化(main & init)之缓冲区的管理
linux·c语言·数据结构·物联网·操作系统
就爱学编程31 分钟前
从C语言看数据结构和算法:复杂度决定性能
c语言·数据结构·算法
半桔36 分钟前
栈和队列(C语言)
c语言·开发语言·数据结构·c++·git
刀客12337 分钟前
数据结构与算法再探(六)动态规划
算法·动态规划
金融OG1 小时前
99.11 金融难点通俗解释:净资产收益率(ROE)VS投资资本回报率(ROIC)VS总资产收益率(ROA)
大数据·python·算法·机器学习·金融
king-xxz1 小时前
动态规划:斐波那契形(初阶)
算法·动态规划
墨楠。2 小时前
数据结构学习记录-树和二叉树
数据结构·学习·算法
小唐C++2 小时前
C++小病毒-1.0勒索
开发语言·c++·vscode·python·算法·c#·编辑器
醇醛酸醚酮酯3 小时前
Leetcode热题——移动零
算法·leetcode·职场和发展