合并K个升序链表

本文介绍了一种使用优先级队列(基于二叉堆)合并升序链表的算法,通过一系列示例和代码展示了解决问题的关键步骤。
摘要由CSDN通过智能技术生成

 题目

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

示例 2:

输入:lists = []
输出:[]

示例 3:

输入:lists = [[]]
输出:[]

提示:

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i] 按 升序 排列
  • lists[i].length 的总和不超过 10^4

思路

        这道题使用的方法是优先级队列,优先级队列是一种当插入元素时,可以帮助你自动排好序的数据结构。而底层的原理就是二叉堆。

        二叉堆是一颗完全二叉树,但是这个树是存储在数组之中。

        举个例子,父亲节点的下标如果是a,那么左子节点下标就是2a,右子节点就是2a+1。也就是孩子结点的坐标可以利用父节点的索引值找到,下面是一张借用labuladong博主的图片:

 

         并且二叉堆还分为最大堆和最小堆,最大堆的意思就是父亲节点的值永远大于孩子节点。最小堆相反。

        优先级队列中有四个关键方法:上浮,下沉,插入,删除。

        上浮:
public class MaxPQ <Key extends Comparable<Key>> {
 
    private void swim(int x) {
        // 如果浮到堆顶,就不能再上浮了
        while (x > 1 && less(parent(x), x)) {
            // 如果第 x 个元素比上层大
            // 将 x 换上去
            swap(parent(x), x);
            //我们始终要知道,这是数组。
            //假如一开始只有两个元素,x是2,经过交换之后,下标1,2的元素已经互换了位置,x所代表的元素始终是原先的第二个元素,所以我们需要更新X的值
            x = parent(x);
        }
    }
}
  下沉

与上浮同理:只是在上浮的基础上补了一个先比较左右节点,再去和父节点进行比较。

public class MaxPQ <Key extends Comparable<Key>> {
 
    private void sink(int x) {
        // 如果沉到堆底,就沉不下去了
        while (left(x) <= size) {
            // 先假设左边节点较大
            int max = left(x);
            // 如果右边节点存在,比一下大小
            if (right(x) <= size && less(max, right(x)))
                max = right(x);
            // 结点 x 比俩孩子都大,就不必下沉了
            if (less(max, x)) break;
            // 否则,不符合最大堆的结构,下沉 x 结点
            swap(x, max);
            x = max;
        }
    }
}

 

insert:
public class MaxPQ <Key extends Comparable<Key>> {
    // 为了节约篇幅,省略上文给出的代码部分...
 
    public void insert(Key e) {
        size++;
        // 先把新元素加到最后
        pq[size] = e;
        // 然后让它上浮到正确的位置
        swim(size);
    }
}

 

 delMax:
public class MaxPQ <Key extends Comparable<Key>> {
    // 为了节约篇幅,省略上文给出的代码部分...
 
    public Key delMax() {
        // 最大堆的堆顶就是最大元素
        Key max = pq[1];
        // 把这个最大元素换到最后,删除之
        swap(1, size);
        pq[size] = null;
        size--;
        // 让 pq[1] 下沉到正确位置
        sink(1);
        return max;
    }
}

 

以上代码和思路取自于labuladong的博客 :至于内容方面都来自于本人二叉堆详解实现优先级队列 | labuladong 的算法笔记

关于这道题的思路

首先我们将k个链表的头节点放入我们的优先级队列中。由于是升序排列,所以我们使用小顶堆进行排序。

 之后我们获取这三个节点之中值最小的一个节点,并把他放入我们构建的新链表之中,并之后,链表节点顺势后移一位,方便继续添加。

//poll函数可以获取最高优先级的节点
ListNode node = pq.poll();
 
            p.next = node;
            p = p.next;

 

我们的链表移动好了之后,接下来我们要移动优先级队列中的节点。现在node所指向的不是最高优先级的节点了,我们首先需要让node=node.next,然后再将node添加进我们的队列之中,由于优先级队列会自动排序,并且队列是先进先出的数据结构,所以我们直接添加就可以进行排序,最高优先级的节点一定在第一位。但是在我们node添加的时候需要保证,node.next不为空。

 

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if(lists.length==0){
            return null;
        }
        ListNode dummy = new ListNode(-1);
        ListNode p = dummy;
        //选把链表整合到一起,然后利用优先级队列,小顶堆进行排序。
        PriorityQueue<ListNode> pq = new PriorityQueue<>(
            lists.length, (a, b)->(a.val - b.val));
        for(ListNode head:lists){
            if(head!=null)
            pq.add(head);
        }
        while(!pq.isEmpty()){
            //这里获取的是三个头节点之中最小的节点,也是所有节点之中最小的
            ListNode node = pq.poll();
 
            p.next = node;
            p = p.next;
            if(node.next!=null){
                //保证node的链表更新,node每次不是同一个链表的值,还得保证链表进入到优先级队列制作,所以不可以直接node=node.next
                pq.add(node.next);
                //因为这是一个队列,所以add方法会自动弹出第一个,然后将node.next放进去
            }
        }
        return dummy.next;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值