力扣刷题笔记-23 合并k个升序链表

找对象是找绝对盟友

学习了这个算法之后,对于链表更加理解了一些。

链表的理解

链表的数据结构就是数据+指针,数据是int,String等类型,指针是什么?指针就是他自己这种类型的节点,直接指向下一个。

而我们在说,“给定一个链表”,实际上给的是一个头节点。这道题涉及到优先级队列,给定的参数是List lists,按照我以前对链表的理解,我就认为lists里面的每个元素都是一个完整的链表。

看完这个算法之后,实际上在lists的每个元素里存放的是头节点,最开始下面这段代码是不理解的

for (ListNode head : lists) {
    if (head != null){
        System.out.println("head.val = " + head.val);
        priorityQueue.add(head);
    }
}

疑惑的点:

为什么遍历lists,每个元素拿出来,就直接是头节点了呢?

因为链表存放在数组里,本来就是只有头节点。

算法的理解

这个算法主要使用到了优先级队列PriorityQueue这个东西,具体怎么理解后面继续研究,先说在这个算法里优先级队列的原理及作用。

虽说是一个队列,但是并不是遵循先进先出的原则,而是根据元素的优先级,进行排序,当插入一个元素的时候,会根据元素的优先级将其插入到合适的位置,但是删除一个元素的时候,会自动将优先级最高的元素出队。

所以这就引申出了一个问题:什么是优先级?怎么定义优先级?
解答:

实际上,PriorityQueue这个东西,用小顶堆来维护优先级,就是用小顶堆来对元素进行排序,让优先级最高的元素排在数组的最前面(就是最小/最大的元素排在最前面)。小顶堆是方式方法,一种概念,一种实现方式。在priorityQueue.poll()的源码里,有一句是

E result = (E)queue[0]

这说明,我们是将数组的第一个元素拿出来。

当然,拿出来之后还要对生育的元素进行排序

理解这个算法的关键,就是将原本升序排列好的链表都放到优先级队列里边,每次弹出最小的那个元素,将弹出来的元素拼接到新的结果链表上,这样当队列里没有数据的时候,就是合并完成的时候。

@Test
void leetCode23(){
    ListNode[] lists = new ListNode[3];
    lists[0] = new ListNode(1);
    lists[0].next = new ListNode(4);
    lists[0].next.next = new ListNode(5);

    lists[1] = new ListNode(1);
    lists[1].next = new ListNode(3);
    lists[1].next.next = new ListNode(4);

    lists[2] = new ListNode(2);
    lists[2].next = new ListNode(6);
    ListNode listNode = mergeKLists(lists);
    while (listNode != null){
        System.out.print(" " + listNode.val);
        listNode = listNode.next;
    }
}

private ListNode mergeKLists(ListNode[] lists){
    ListNode dummy = new ListNode(-1);
    ListNode p = dummy;

    PriorityQueue<ListNode> priorityQueue = new PriorityQueue<>(
            lists.length, Comparator.comparingInt(a -> a.val)
    );

    // 将k个链表的头节点存放到优先级队列里边
    for (ListNode head : lists) {
        if (head != null){
            priorityQueue.add(head);
        }
    }

    while (!priorityQueue.isEmpty()){
        // 每次弹出最小的元素
        ListNode temp = priorityQueue.poll();
        // 拼接到结果链表上
        p.next = temp;
        p = p.next;
        // 如果不是尾节点,将后续节点放到队列里面,进入下一次循环。下一次循环的时候仍旧弹出最小的节点
        if (temp.next != null){
            priorityQueue.add(temp.next);
        }
    }
    return dummy.next;
}
posted @ 2024-03-13 09:59  大海0101  阅读(7)  评论(0编辑  收藏  举报