java-数据结构与算法-02-数据结构-07-优先队列

news2024/12/26 22:55:38

1. 概念

队列是一种先进先出的结构,但是有些时候,要操作的数据带有优先级,一般出队时,优先级较高的元素先出队,这种数据结构就叫做优先级队列。

比如:你在打音游的时候,你的朋友给你打了个电话,这种时候,就应该优先处理电话,然后再来继续打音游,此时,电话就是优先级较高的。

在这种情况下,数据结构应该提供两个最基本的操作,一个是返回优先级最高的对象,一个是添加新的对象。这种数据结构就是优先级队列(Priority Queue)。

2. 无序数组实现

要点

  1. 入队保持顺序
  2. 出队前找到优先级最高的出队,相当于一次选择排序

队列实现类:

package com.itheima.datastructure.priorityqueue;

import com.itheima.datastructure.queue.Queue;

/**
 * 优先队列实现类,基于无序数组。
 * 使用无序数组实现优先队列,队列中的元素必须实现Priority接口,以提供优先级比较的方法。
 * 该实现类提供了标准的队列操作,包括入队、出队、查看队首元素、判断队列为空或满等。
 *
 * @param <E> 队列元素类型,必须实现Priority接口。
 */
@SuppressWarnings("all")
public class PriorityQueue1<E extends Priority> implements Queue<E> {

    // 存储队列元素的数组,元素类型为Priority接口的实现类。
    Priority[] array;
    // 当前队列的大小。
    int size;

    /**
     * 构造函数,初始化优先队列。
     * 
     * @param capacity 队列的容量,即最大元素数量。
     */
    public PriorityQueue1(int capacity) {
        array = new Priority[capacity];
    }

    /**
     * 入队操作,将元素添加到队列尾部。
     * 
     * @param e 待添加到队列的元素,必须实现Priority接口。
     * @return 添加成功返回true,队列已满返回false。
     */
    @Override // O(1)
    public boolean offer(E e) {
        if (isFull()) {
            return false;
        }
        array[size++] = e;
        return true;
    }

    /**
     * 查找优先级最高的元素的索引。
     * 
     * @return 优先级最高的元素的索引。
     */
    // 返回优先级最高的索引值
    private int selectMax() {
        int max = 0;
        for (int i = 1; i < size; i++) {
            if (array[i].priority() > array[max].priority()) {
                max = i;
            }
        }
        return max;
    }

    /**
     * 出队操作,移除并返回优先级最高的元素。
     * 
     * @return 被移除的优先级最高的元素,队列为空时返回null。
     */
    @Override // O(n)
    public E poll() {
        if (isEmpty()) {
            return null;
        }
        int max = selectMax();
        E e = (E) array[max];
        remove(max);
        return e;
    }

    /**
     * 从数组中移除指定索引的元素,并将后续元素向前移动一位。
     * 
     * @param index 待移除元素的索引。
     */
    private void remove(int index) {
        if (index < size - 1) {
            // 移动
            System.arraycopy(array, index + 1,
                    array, index, size - 1 - index);
        }
        array[--size] = null; // help GC
    }

    /**
     * 查看队首元素,即优先级最高的元素,但不移除。
     * 
     * @return 队首元素,队列为空时返回null。
     */
    @Override
    public E peek() {
        if (isEmpty()) {
            return null;
        }
        int max = selectMax();
        return (E) array[max];
    }

    /**
     * 判断队列是否为空。
     * 
     * @return 队列为空返回true,否则返回false。
     */
    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 判断队列是否已满。
     * 
     * @return 队列已满返回true,否则返回false。
     */
    @Override
    public boolean isFull() {
        return size == array.length;
    }
}

优先级类:

package com.itheima.datastructure.priorityqueue;

public interface Priority {

    /**
     * 返回对象的优先级, 约定数字越大, 优先级越高
     * @return 优先级
     */
    int priority();
}

条目类:

/**
 * 优先队列中的条目类,实现了Priority接口。
 * 该类用于存储具有特定优先级的值。
 */
package com.itheima.datastructure.priorityqueue;

class Entry implements Priority {

    String value; // 条目的值
    int priority; // 条目的优先级

    /**
     * 构造函数,创建一个具有指定优先级的条目。
     * @param priority 条目的优先级
     */
    public Entry(int priority) {
        this.priority = priority;
    }

    /**
     * 构造函数,创建一个具有指定值和优先级的条目。
     * @param value 条目的值
     * @param priority 条目的优先级
     */
    public Entry(String value, int priority) {
        this.value = value;
        this.priority = priority;
    }

    /**
     * 返回条目的优先级。
     * @return 条目的优先级
     */
    @Override
    public int priority() {
        return priority;
    }

    /**
     * 返回表示条目的字符串,格式为"(值 priority=优先级)"。
     * @return 表示条目的字符串
     */
    @Override
    public String toString() {
        return "(" + value + " priority=" + priority + ")";
    }

    /**
     * 比较当前对象与另一个对象是否相等。
     * 两个条目相等的定义是它们的优先级相同。
     * @param o 要比较的对象
     * @return 如果两个对象的优先级相同,则返回true;否则返回false。
     */
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Entry entry = (Entry) o;

        return priority == entry.priority;
    }

    /**
     * 计算当前对象的哈希码,基于条目的优先级。
     * @return 当前对象的哈希码
     */
    @Override
    public int hashCode() {
        return priority;
    }
}

测试类:

package com.itheima.datastructure.priorityqueue;

import org.junit.jupiter.api.Test;

import java.util.Arrays;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;

/*
    优先级队列       一端进, 另一端出       按优先级出队
    普通队列        一端进, 另一端出       FIFO
*/
public class TestPriorityQueue1 {

    @Test
    public void poll() {
        PriorityQueue1<Entry> queue = new PriorityQueue1<>(5);
        queue.offer(new Entry("task1", 4));
        queue.offer(new Entry("task2", 3));
        queue.offer(new Entry("task3", 2));
        queue.offer(new Entry("task4", 5));
        queue.offer(new Entry("task5", 1));
        assertFalse(queue.offer(new Entry("task6", 7)));

        System.out.println(Arrays.toString(queue.array));
        assertEquals(5, queue.poll().priority());
        System.out.println(Arrays.toString(queue.array));
        assertEquals(4, queue.poll().priority());
        assertEquals(3, queue.poll().priority());
        assertEquals(2, queue.poll().priority());
        assertEquals(1, queue.poll().priority());
    }
}

测试结果:

[(task1 priority=4), (task2 priority=3), (task3 priority=2), (task4 priority=5), (task5 priority=1)]
[(task1 priority=4), (task2 priority=3), (task3 priority=2), (task5 priority=1), null]

3. 有序数组实现

要点

  1. 入队后排好序,优先级最高的排列在尾部
  2. 出队只需删除尾部元素即可

队列实现类:

package com.itheima.datastructure.priorityqueue;

import com.itheima.datastructure.queue.Queue;

/**
 * 基于有序数组实现的优先队列。
 * 使用优先级高的元素先出(FIFO)的策略。
 *
 * @param <E> 队列中元素的类型,必须实现Priority接口以定义优先级。
 */
@SuppressWarnings("all")
public class PriorityQueue2<E extends Priority> implements Queue<E> {

    // 存储元素的数组,数组中的元素必须实现Priority接口
    Priority[] array;
    // 当前队列中元素的数量
    int size;

    /**
     * 构造函数,初始化优先队列。
     *
     * @param capacity 队列的容量,即最大元素数量。
     */
    public PriorityQueue2(int capacity) {
        array = new Priority[capacity];
    }

    /**
     * 向队列中添加一个元素。
     * 如果队列已满,则返回false。
     *
     * @param e 要添加到队列的元素,必须实现Priority接口。
     * @return 如果添加成功,返回true;否则返回false。
     */
    @Override
    public boolean offer(E e) {
        if (isFull()) {
            return false;
        }
        insert(e);
        size++;
        return true;
    }

    /**
     * 将元素插入到队列的正确位置,以保持队列的有序性。
     *
     * @param e 要插入的元素。
     */
    // O(n)
    private void insert(E e) {
        int i = size - 1;
        while (i >= 0 && array[i].priority() > e.priority()) {
            array[i + 1] = array[i];
            i--;
        }
        array[i + 1] = e;
    }

    /**
     * 从队列中移除并返回优先级最高的元素。
     * 如果队列为空,则返回null。
     *
     * @return 被移除的元素,如果队列为空,则返回null。
     */
    // O(1)
    @Override
    public E poll() {
        if (isEmpty()) {
            return null;
        }
        E e = (E) array[size - 1];
        array[--size] = null; // help GC
        return e;
    }

    /**
     * 返回优先级最高的元素,但不移除它。
     * 如果队列为空,则返回null。
     *
     * @return 队列头部的元素,如果队列为空,则返回null。
     */
    @Override
    public E peek() {
        if (isEmpty()) {
            return null;
        }
        return (E) array[size - 1];
    }

    /**
     * 检查队列是否为空。
     *
     * @return 如果队列为空,返回true;否则返回false。
     */
    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 检查队列是否已满。
     *
     * @return 如果队列已满,返回true;否则返回false。
     */
    @Override
    public boolean isFull() {
        return size == array.length;
    }
}

测试类:

package com.itheima.datastructure.priorityqueue;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;

public class TestPriorityQueue2 {

    @Test
    public void poll() {
        PriorityQueue2<Entry> queue = new PriorityQueue2<>(5);
        queue.offer(new Entry("task1", 4));
        queue.offer(new Entry("task2", 3));
        queue.offer(new Entry("task3", 2));
        queue.offer(new Entry("task4", 5));
        queue.offer(new Entry("task5", 1));
        assertFalse(queue.offer(new Entry("task6", 7)));

        assertEquals("task4", queue.peek().value);
        assertEquals("task4", queue.poll().value);
        assertEquals("task1", queue.poll().value);
        assertEquals("task2", queue.poll().value);
        assertEquals("task3", queue.poll().value);
        assertEquals("task5", queue.poll().value);
    }
}

4. 堆实现

JDK1.8中的 PriorityQueue 底层使用了堆这种数据结构,而堆实际就是在完全二叉树的基础上进行了一些调整。也就是说,堆的是由完全二叉树调整而来的,可以存储到数组中。

堆的概念
如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为 小堆(或 大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆的性质:

  • 堆中某个节点的值总是不大于或不小于其父节点的值。
  • 堆总是一棵完全二叉树。
    如图:
    在这里插入图片描述
    在这里插入图片描述

计算机科学中,堆是一种基于树的数据结构,通常用完全二叉树实现。堆的特性如下

  • 在大顶堆中,任意节点 C 与它的父节点 P 符合 P . v a l u e ≥ C . v a l u e P.value \geq C.value P.valueC.value
  • 而小顶堆中,任意节点 C 与它的父节点 P 符合 P . v a l u e ≤ C . v a l u e P.value \leq C.value P.valueC.value
  • 最顶层的节点(没有父亲)称之为 root 根节点

完全二叉树可以使用数组来表示

在这里插入图片描述
特征

● 如果从索引 0 开始存储节点数据
○ 节点 i i i 的父节点为 f l o o r ( ( i − 1 ) / 2 ) floor((i-1)/2) floor((i1)/2),当 i > 0 i>0 i>0
○ 节点 i i i 的左子节点为 2 i + 1 2i+1 2i+1,右子节点为 2 i + 2 2i+2 2i+2,当然它们得 < s i z e < size <size
● 如果从索引 1 开始存储节点数据
○ 节点 i i i 的父节点为 f l o o r ( i / 2 ) floor(i/2) floor(i/2),当 i > 1 i > 1 i>1
○ 节点 i i i 的左子节点为 2 i 2i 2i,右子节点为 2 i + 1 2i+1 2i+1,同样得 < s i z e < size <size

实现类:

package com.itheima.datastructure.priorityqueue;

import com.itheima.datastructure.queue.Queue;

/**
 * 基于大顶堆实现的优先队列。
 * 大顶堆是一个完全二叉树,每个父节点的优先级不小于其子节点的优先级。
 * @param <E> 队列中元素的类型,必须实现Priority接口以定义元素的优先级。
 */
@SuppressWarnings("all")
public class PriorityQueue4<E extends Priority> implements Queue<E> {

    Priority[] array; // 存储优先队列元素的数组
    int size; // 当前队列的大小

    /**
     * 构造函数,初始化优先队列。
     * @param capacity 队列的初始容量。
     */
    public PriorityQueue4(int capacity) {
        array = new Priority[capacity];
    }

    /**
     * 向队列中添加一个元素。
     * 如果队列已满,则返回false;否则将元素加入队列,并调整堆以保持大顶堆的性质。
     * @param offered 要添加到队列的元素。
     * @return 如果添加成功,返回true;如果队列已满,返回false。
     */
    /*
    1. 入堆新元素, 加入到数组末尾 (索引位置 child)
    2. 不断比较新加元素与它父节点(parent)优先级 (上浮)
        - 如果父节点优先级低, 则向下移动, 并找到下一个 parent
        - 直至父节点优先级更高或 child==0 为止
     */
    @Override
    public boolean offer(E offered) {
        // 检查队列是否已满,如果已满,则拒绝添加新元素并返回false。
        if (isFull()) {
            return false;
        }
        // 将新元素插入到数组的末尾,并记录其位置。
        int child = size++;
        // 计算新元素的父节点位置。
        int parent = (child - 1) / 2;
        // 上浮新元素,直到它位于正确的位置或者它是根元素。
        while (child > 0 && offered.priority() > array[parent].priority()) {
            // 将当前元素的父元素移动到当前元素的位置,准备继续上浮。
            array[child] = array[parent];
            child = parent;
            // 更新当前元素的父节点位置。
            parent = (child - 1) / 2;
        }
        // 将新元素放置在最终的位置上,完成添加。
        array[child] = offered;
        // 添加成功,返回true。
        return true;
    }

    /**
     * 从队列中移除并返回优先级最高的元素(即堆顶元素)。
     * 如果队列为空,则返回null。
     * @return 优先级最高的元素,如果队列为空,则返回null。
     */
    /*
    1. 交换堆顶和尾部元素, 让尾部元素出队
    2. (下潜)
        - 从堆顶开始, 将父元素与两个孩子较大者交换
        - 直到父元素大于两个孩子, 或没有孩子为止
     */
    @Override
    public E poll() {
        // 检查队列是否为空,如果为空则返回null
        if (isEmpty()) {
            return null;
        }
        // 交换堆顶元素(优先级最高)和队列尾部元素,准备移除堆顶元素
        swap(0, size - 1);
        // 更新队列大小,表示已移除一个元素
        size--;
        // 获取并保存即将返回的堆顶元素
        Priority e = array[size];
        // 将队列尾部元素置为null,帮助垃圾回收
        array[size] = null; // help GC

        // 调整堆结构,确保堆的性质依然满足
        // 下潜
        down(0);

        // 返回移除的堆顶元素
        return (E) e;
    }

    /**
     * 将元素向下调整以保持大顶堆的性质。
     * 此方法假设调用时堆已经部分有序,它通过比较父节点和其子节点的优先级来确保整个堆仍然满足大顶堆的定义。
     * 如果父节点的优先级低于某个子节点,则交换它们,并继续向下调整子节点,直到整个子树满足大顶堆的条件。
     *
     * @param parent 需要向下调整的父节点的索引。
     */
    /**
     * 将元素向下调整以保持大顶堆的性质。
     * @param parent 需要向下调整的元素的索引。
     */
    private void down(int parent) {
        // 计算左子节点和右子节点的索引
        int left = 2 * parent + 1;
        int right = left + 1;

        // 假设当前父节点的优先级最高
        int max = parent; // 假设父元素优先级最高

        // 如果左子节点存在且优先级高于当前最大值,则更新最大值索引
        if (left < size && array[left].priority() > array[max].priority()) {
            max = left;
        }

        // 如果右子节点存在且优先级高于当前最大值,则更新最大值索引
        if (right < size && array[right].priority() > array[max].priority()) {
            max = right;
        }

        // 如果最大值不是初始的父节点,则交换它们,并递归向下调整新父节点
        if (max != parent) { // 有孩子比父亲大
            swap(max, parent);
            down(max);
        }
    }

    /**
     * 交换数组中两个元素的位置。
     * @param i 第一个元素的索引。
     * @param j 第二个元素的索引。
     */
    private void swap(int i, int j) {
        Priority t = array[i];
        array[i] = array[j];
        array[j] = t;
    }

    /**
     * 返回队列中优先级最高的元素(即堆顶元素),但不移除它。
     * 如果队列为空,则返回null。
     * @return 优先级最高的元素,如果队列为空,则返回null。
     */
    @Override
    public E peek() {
        if (isEmpty()) {
            return null;
        }
        return (E) array[0];
    }

    /**
     * 检查队列是否为空。
     * @return 如果队列为空,返回true;否则返回false。
     */
    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 检查队列是否已满。
     * @return 如果队列已满,返回true;否则返回false。
     */
    @Override
    public boolean isFull() {
        return size == array.length;
    }
}


5. 习题

E01. 合并多个有序链表-Leetcode 23

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

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

示例 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

这道题目之前解答过,现在用刚学的优先级队列来实现一下
题目中要从小到大排列,因此选择用小顶堆来实现,思路如下图:
在这里插入图片描述

自定义小顶堆如下

package com.itheima.datastructure.priorityqueue;

import com.itheima.datastructure.linkedlist.ListNode;

/**
 * 小顶堆类,实现优先队列功能。
 * 小顶堆是一个完全二叉树,每个父节点的值都小于或等于其子节点的值。
 */
/**
 * <b>小顶堆</b>
 */
public class MinHeap {

    /**
     * 堆数组,存储堆中的节点。
     */
    ListNode[] array;
    /**
     * 堆的当前大小,即堆中节点的数量。
     */
    int size;

    /**
     * 构造函数,初始化小顶堆。
     *
     * @param capacity 堆的容量,即堆数组的大小。
     */
    public MinHeap(int capacity) {
        array = new ListNode[capacity];
    }

    /**
     * 将一个节点添加到最小堆中。
     * 如果堆已满,则拒绝添加,并返回false;否则,将节点添加到堆中并维护堆的性质。
     *
     * @param offered 要添加到堆中的节点。
     * @return 如果成功添加节点,则返回true;如果堆已满,则返回false。
     */
    /**
     * 向堆中插入一个节点。
     *
     * @param offered 要插入的节点。
     * @return 如果堆已满,返回false;否则返回true。
     */
    public boolean offer(ListNode offered) {
        // 检查堆是否已满,如果已满则无法添加新节点
        if (isFull()) {
            return false;
        }
        // 将新节点插入到堆的最后一个位置,并更新堆的大小
        int child = size++;
        // 计算新节点的父节点位置
        int parent = (child - 1) / 2;
        // 当节点不在根位置且小于其父节点时,向上调整节点位置以维护最小堆性质
        while (child > 0 && offered.val < array[parent].val) {
            // 将父节点值复制到当前节点
            array[child] = array[parent];
            // 更新当前节点为父节点,并计算新的父节点位置
            child = parent;
            parent = (child - 1) / 2;
        }
        // 将新节点值插入到最终位置,完成添加
        array[child] = offered;
        return true;
    }


    /**
     * 从堆中移除并返回最小的节点。
     *
     * @return 如果堆为空,返回null;否则返回移除的最小节点。
     */
    public ListNode poll() {
        if (isEmpty()) {
            return null;
        }
        swap(0, size - 1);
        size--;
        ListNode e = array[size];
        array[size] = null; // help GC

        // 下潜
        down(0);

        return e;
    }

    /**
     * 将堆中指定元素下沉以维护堆的性质。
     * 这个方法是堆排序或优先队列操作中的关键部分,它确保堆的性质得以维持。
     * 当插入一个新元素或某个元素的值被更新后,可能需要调用此方法来重新调整堆。
     *
     * @param parent 要下沉的元素的索引,该元素是其子节点的父节点。
     */
    private void down(int parent) {
        // 计算左子节点和右子节点的索引
        int left = 2 * parent + 1;
        int right = left + 1;
        // 假设当前父节点是最小的
        // 如果左子节点存在且值小于当前最小值,则更新最小值为左子节点
        int min = parent; // 假设父元素最小
        if (left < size && array[left].val < array[min].val) {
            min = left;
        }
        // 如果右子节点存在且值小于当前最小值,则更新最小值为右子节点
        if (right < size && array[right].val < array[min].val) {
            min = right;
        }
        // 如果找到的最小值不是初始的父节点,则交换它们并继续下沉最小值节点
        if (min != parent) { // 有孩子比父亲小
            swap(min, parent);
            down(min);
        }
    }


    /**
     * 交换堆数组中两个节点的位置。
     *
     * @param i 第一个节点的索引。
     * @param j 第二个节点的索引。
     */
    private void swap(int i, int j) {
        ListNode t = array[i];
        array[i] = array[j];
        array[j] = t;
    }

    /**
     * 检查堆是否为空。
     *
     * @return 如果堆为空,返回true;否则返回false。
     */
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 检查堆是否已满。
     *
     * @return 如果堆已满,返回true;否则返回false。
     */
    public boolean isFull() {
        return size == array.length;
    }
}

代码

package com.itheima.datastructure.priorityqueue;

import com.itheima.datastructure.linkedlist.ListNode;

/**
 * 合并多个有序链表的工具类。
 */
public class E01Leetcode23 {
    
    /**
     * 使用最小堆合并多个有序链表。
     * 
     * @param lists 多个有序链表的数组形式。
     * @return 合并后的单个有序链表。
     */
    public ListNode mergeKLists2(ListNode[] lists) {
        MinHeap heap = new MinHeap(100);
        // 将所有链表的节点加入最小堆
        // 1. 将链表的所有节点加入小顶堆
        for (ListNode p : lists) {
            while (p != null) {
                heap.offer(p);
                p = p.next;
            }
        }
        // 从最小堆中依次取出节点构建合并后的链表
        // 2. 不断从堆顶移除最小元素, 加入新链表
        ListNode s = new ListNode(-1, null);
        ListNode t = s;
        while(!heap.isEmpty()) {
            ListNode min = heap.poll();
            t.next = min;
            t = min;
            t.next = null; // 保证尾部节点指向 null
        }
        return s.next;
    }

    /**
     * 使用最小堆合并多个有序链表的另一种实现方式。
     * 
     * @param lists 多个有序链表的数组形式。
     * @return 合并后的单个有序链表。
     */
    public ListNode mergeKLists(ListNode[] lists) {
        MinHeap heap = new MinHeap(lists.length);
        // 将所有链表的头节点加入最小堆
        // 1. 将链表的头节点加入小顶堆
        for (ListNode h : lists) {
            if(h != null) {
                heap.offer(h);
            }
        }
        // 从最小堆中依次取出节点构建合并后的链表
        // 2. 不断从堆顶移除最小元素, 加入新链表
        ListNode s = new ListNode(-1, null);
        ListNode t = s;
        while(!heap.isEmpty()) {
            ListNode min = heap.poll();
            t.next = min;
            t = min;
            // 将当前节点的下一个节点加入最小堆
            // 将最小元素的下一个节点加入到堆
            if(min.next != null) {
                heap.offer(min.next);
            }
        }
        return s.next;
    }

    /**
     * 测试合并多个有序链表的函数。
     * 
     * @param args 命令行参数。
     */
    public static void main(String[] args) {
        ListNode[] lists = {
                ListNode.of(1, 4, 5),
                ListNode.of(1, 3, 4),
                ListNode.of(2, 6),
                null,
        };
        ListNode m = new E01Leetcode23().mergeKLists2(lists);
        System.out.println(m);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1962989.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

安全基础学习-CRC理解与计算

由于一些任务要求需要了解CRC校验&#xff0c;于是来学习一下。 新人学习&#xff0c;大佬绕路。 前言 CRC即循环冗余校验码&#xff1a;是数据通信领域中最常用的一种查错校验码&#xff0c;其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查&#xff08;CRC&…

《计算机工程与应用》投稿经验

目录 前言期刊简介详细过程第1步&#xff1a;网站投稿第2步&#xff1a;交审稿费第3步&#xff1a;等外审第4步&#xff1a;返修所需材料描述 第5步&#xff1a;外审/复审/录用第6步&#xff1a;录用 前言 记录下投《计算机工程与应用》的正刊投稿经验。 整理了一些投稿期间常…

【C#】 使用GDI+获取两个多边形区域相交、非相交区域

一、使用GDI获取两个多边形区域相交、非相交区域 在 C# 中使用 GDI&#xff08;Graphics Device Interface Plus&#xff09;处理图形时&#xff0c;你可以使用 System.Drawing 和 System.Drawing.Drawing2D 命名空间中的类来操作区域&#xff08;Region&#xff09;。下面是一…

取图小程序搭建教程,达人+流量主+会员快速部署上线指南

目录 一、取图小程序是什么&#xff1f; 二、搭建教程 &#xff08;一&#xff09;前期准备 &#xff08;二&#xff09;开发环境搭建 &#xff08;三&#xff09;部署与上线 三、部分代码展示 一、取图小程序是什么&#xff1f; 取图小程序以其多元化的功能而著称&…

抖音本地生活城市代理保证金多少?最新标准出炉!

随着本地生活的前景和潜力不断显现&#xff0c;抖音本地生活城市代理的申请热度不断攀升&#xff0c;并逐渐成为众多创业者入局本地生活的首要选择&#xff0c;而与之相关的问题&#xff0c;如抖音本地生活城市代理保证金等更是长居相关社群话题榜的前列。 而就目前的市场情况来…

2024华为数通HCIP-datacom最新题库(H12-831变题更新⑧)

请注意&#xff0c;华为HCIP-Datacom考试831已变题 请注意&#xff0c;华为HCIP-Datacom考试831已变题 请注意&#xff0c;华为HCIP-Datacom考试831已变题 近期打算考HCIP的朋友注意了&#xff0c;如果你准备去考试&#xff0c;还是用的之前的题库&#xff0c;切记暂缓。 1、…

抖音开放平台API接口如何开发||抖音相关接口数据采集数据分析 【附实例】

抖音开放平台提供了多种接口&#xff0c;包括授权登录、用户信息、视频管理、评论互动、消息通知、数据分析等。 以下是开发抖音接口的一些步骤&#xff1a; 1. 注册开发者账号&#xff1a;在抖音开放平台上注册开发者账号&#xff0c;获取开发者身份认证。 2. 创建应用&…

学习鸿蒙-利用Code Linter检查代码

如何检查&#xff1a; 1.在已打开的代码编辑器窗口单击右键点击Code Linter&#xff0c;或在工程管理窗口中鼠标选中单个或多个工程文件/目录&#xff0c;右键选择Code Linter > Full Linter执行代码全量检查。 2.通过git 提交&#xff0c;可在commit界面右下角点击齿轮图标…

嵌入式开发问题总汇

1. 事情经过&#xff1a;有天快下班的时候&#xff0c;我们采购经理找到我&#xff0c;说&#xff0c;生成的bin文件通过软件上传烧录时&#xff0c;烧录就会重启&#xff0c;但是其他的板子就不会。最后他换了一个A厂家的flash芯片&#xff0c;就可以了。但是&#xff0c;B厂…

使用 Postman 进行 Trello API 自动化测试的完整指南

文章目录 前言一、自动化测试是什么&#xff1f;二、比较自动化测试与手工测试1. 自动化测试2. 手工测试 三、环境搭建1.创建Collection2.创建环境变量3.添加API请求 四、设计测试用例1. API简单调用2. 获取所有emoji3. 创建一个新看板&#xff1a;4. 获得创建的看板信息5. 在看…

【Git】git 从入门到实战系列(一)—— Git 的诞生,Linus 如何在 14 天内编写出 Git?

<> 博客简介&#xff1a;Linux、rtos系统&#xff0c;arm、stm32等芯片&#xff0c;嵌入式高级工程师、面试官、架构师&#xff0c;日常技术干货、个人总结、职场经验分享   <> 公众号&#xff1a;嵌入式技术部落   <> 系列专栏&#xff1a;C/C、Linux、rt…

【机器学习】决策边界的基本概念以及如何在逻辑回归中找到决策边界

引言 在机器学习中&#xff0c;决策边界是分类算法用来区分不同类别数据点的线、面或超平面。这些边界通常是模型的预测规则&#xff0c;用于将特征空间中的点分配到不同的类别。决策边界可以是线性的&#xff0c;也可以是非线性的&#xff0c;取决于数据的分布和所使用的分类算…

探索开明软件包:应用开发指南轻松掌握,快速上手!

开明软件包格式&#xff0c;是openKylin社区Kaiming SIG为应对传统包格式存在的系统与应用无明确界限、发行版碎片化、兼容性等问题而开发的一款全新软件包格式&#xff0c;满足ISV&#xff08;Independent Software Vendors&#xff09;应用发布和应用独立发布需求&#xff0…

AI绘画:PS+comfyUI 工作流,直接在PS搞AI!

前言 直接在PS搞AI要不要&#xff1f; 大神 Nima Nazari 发布了一个可以无缝链接PS和comfyUI的工作流。 小编对该工作流进行了一个安装使用的测评&#xff0c;该工作流可以无缝与PS进行互联操作&#xff0c;切换模型和修改参数时还是需要在PS和ComfyUI间切换。总体而言&#x…

CSCP、CPIM和CLMP三大证书的区别?如何选择?

在制造型企业、供应链和运营管理专业人士都会不断寻找方法来提升他们的技能和职业前景。三种流行的认证——CSCP&#xff08;Certified Supply Chain Professional&#xff09;、CPIM&#xff08;Certified in Planning and Inventory Management&#xff09;以及CLMP&#xff…

其他:trycatch模块捕获循环错误,避免中断程序

介绍 今天有位同事问我怎么在某次循环报错后仍然可以继续程序运行&#xff0c;最后我们使用了trycatch模块。 代码解读 任务&#xff1a;在循环中&#xff0c;如果某次循环的calc出现错误则跳过这次循环并重新赋值结果 res_list <- list() # 创建一个空列表来存储结果fo…

整箱排柜不返工?用易境通散拼系统就OK

想必困扰散货拼柜小伙伴们一大难题就是&#xff0c;怎么把错乱纷繁的货物有序地整箱排柜&#xff0c;并且要保证集装箱高效利用&#xff0c;运输成本尽量降低。这不仅要求操作者具备卓越的统筹规划能力&#xff0c;更需长期积累的实践经验和敏锐的应变能力。易境通散拼系统可以…

[python] 启发式算法库scikit-opt使用指北

scikit-opt是一个封装了多种启发式算法的Python代码库&#xff0c;可以用于解决优化问题。scikit-opt官方仓库见&#xff1a;scikit-opt&#xff0c;scikit-opt官网文档见&#xff1a;scikit-opt-doc。 scikit-opt安装代码如下&#xff1a; pip install scikit-opt # 调用scik…

永结无间Ⅶ--具有知识图谱的高级 RAG

将知识图谱与先进的检索增强生成 (RAG) 策略相结合&#xff0c;以增强人工智能驱动的信息检索和生成 在增强 RAG 系统方面&#xff0c;知识图谱已成为改变游戏规则的因素。与传统矢量数据库不同&#xff0c;知识图谱提供丰富、互联的信息表示&#xff0c;可显著提高检索内容的…

Docker简介和Docker常见命令

目录 1. Docker 简介 1.1 Docker 的核心概念 1.2 Docker 的优势 1.3 Docker 工作流程 2. 常见命令 2.1 基本命令 2.2 镜像操作 2.3 容器操作 2.4 网络操作 2.5 卷操作 2.6 日志和监控 2.7 清理命令 3. 注意事项和最佳实践 3.1 镜像操作 3.2 容器操作 3.3 网络操…