当前位置: 首页 > article >正文

Java数据结构——优先队列

目录

  • 引言
  • 1. 优先队列
  • 2. 优先队列的实现
    • 2.1 堆的概念
    • 2.2 堆的创建
    • 2.2.1 堆的向下调整
    • 2.3 堆的插入
    • 2.4 堆的删除
  • 3. 总结

引言

前面一篇文章讲了二叉树,本篇将讲述数据结构中的优先队列,优先队列则需要用到完全二叉树来实现。

1. 优先队列

队列(Queue)是一个线性数据结构,它的理念是“先进先出”,而优先队列(Priority Queue)是一种特殊的队列数据结构,其中每个元素都有一个优先级,元素按照优先级进行排序。也就是说,要保证每次都是优先级最高的元素进行出列。

2. 优先队列的实现

既然队列是线性数据结构,为何还需要用二叉树?
优先队列本身确实是线性结构,但是优先队列的核心需求是高效地插入元素和删除具有最高优先级的元素。为了满足这一要求,需要用到堆这一数据结构。下面将对堆来进行介绍,并使用堆来完成优先队列的相关操作。

2.1 堆的概念

堆(Heap)是一种特殊的完全二叉树数据结构,分为大顶堆(也叫大根堆)(Max Heap)和小顶堆(小根堆)(Min Heap)。堆是一棵完全二叉树,以数组方式存储,且数组中的元素 Ki 满足 Ki <= K2i+1 且Ki <= K2i+2,这则是小顶堆,若是>= 则是大顶堆。
堆的存储:
{1,2,3,4,5,6,7,8,9,10}
在这里插入图片描述

2.2 堆的创建

以如下集合来创建堆:{65,50,23,81,75,32,16,7,43,3}
在这里插入图片描述

2.2.1 堆的向下调整

下面将以上面的乱序集合来建立堆。假设要构建大顶堆。不妨先将其中一个节点(23)进行调整,
用parent标记需要调整的节点,用child标记其左孩子。
在这里插入图片描述
然后判断child是否存在(即child小于usedSize)
存在则比较左孩子和右孩子的大小,将child下标改为值更大的下标。
而后比较child和parent对应的值大小,若child>parent,则交换,否则结束。
如此反复循环,直到child>=usedSize或者child<=parent。
代码如下:

private void siftDown(int parent, int usedSize) {
        int child = 2 * parent + 1;
        while(child < usedSize){
            if(child + 1 < usedSize && elem[child] < elem[child + 1]){
                child++;
            }
            if(elem[child] > elem[parent]){
                swap(child, parent);
                parent = child;
                child = 2 * parent + 1;
            }else{
                break;
            }
        }
    }

在这里插入图片描述
如果要全部调整,则需要从最后一个非叶子进行遍历,往前进行向下调整:

	public void createHeap(int[] array) {
        for(int parent = (array.length - 1 - 1) / 2; parent >= 0; parent--){
            siftDown(parent, array.length);
        }
    }

具体步骤如下:
步骤 1:调整节点索引 4(值为 75)
左子节点索引为 24 + 1 = 9,值为 3
右子节点索引为 2
4 + 2 = 10,不存在
节点 75 大于其左子节点 3,不需要调整。

步骤 2:调整节点索引 3(值为 81)
左子节点索引为 23 + 1 = 7,值为 7
右子节点索引为 2
3 + 2 = 8,值为 43
节点 81 大于其子节点 7 和 43,不需要调整。

步骤 3:调整节点索引 2(值为 23)
左子节点索引为 22 + 1 = 5,值为 32
右子节点索引为 2
2 + 2 = 6,值为 16
比较节点 23 和其左子节点 32,交换它们的位置:

	索引:  0   1   2   3   4   5   6   7   8   9
	数组: [65, 50, 32, 81, 75, 23, 16, 7, 43, 3]

在这里插入图片描述

继续调整节点 23 的位置:

左子节点索引为 25 + 1 = 11,不存在
右子节点索引为 2
5 + 2 = 12,不存在
节点 23 没有子节点,不需要进一步调整。

步骤 4:调整节点索引 1(值为 50)
左子节点索引为 21 + 1 = 3,值为 81
右子节点索引为 2
1 + 2 = 4,值为 75
比较节点 50 和其左子节点 81,交换它们的位置:

	索引:  0   1   2   3   4   5   6   7   8   9
	数组: [65, 81, 32, 50, 75, 23, 16, 7, 43, 3]

在这里插入图片描述

继续调整节点 50 的位置:

左子节点索引为 23 + 1 = 7,值为 7
右子节点索引为 2
3 + 2 = 8,值为 43
节点 50 大于其子节点 7 和 43,不需要进一步调整。

步骤 5:调整节点索引 0(值为 65)
左子节点索引为 20 + 1 = 1,值为 81
右子节点索引为 2
0 + 2 = 2,值为 32
比较节点 65 和其左子节点 81,交换它们的位置:

	索引:  0   1   2   3   4   5   6   7   8   9
	数组: [81, 65, 32, 50, 75, 23, 16, 7, 43, 3]

在这里插入图片描述

继续调整节点 65 的位置:

左子节点索引为 21 + 1 = 3,值为 50
右子节点索引为 2
1 + 2 = 4,值为 75
比较节点 65 和其右子节点 75,交换它们的位置:

	索引:  0   1   2   3   4   5   6   7   8   9
	数组: [81, 75, 32, 50, 65, 23, 16, 7, 43, 3]

在这里插入图片描述
继续调整节点 65 的位置:

左子节点索引为 24 + 1 = 9,值为 3
右子节点索引为 2
4 + 2 = 10,不存在
节点 65 大于其左子节点 3,不需要进一步调整。

	最后调整后的数组如下:{81 75 32 50 65 23 16 7 43 3}

在这里插入图片描述

2.3 堆的插入

插入操作如下:
先将新元素插入到最后一个位置,而后向上调整这个元素。
向上调整和向下调整反过来,将子节点与其父节点进行比较而后调整。

    private void siftDown(int parent, int usedSize) {
        int child = 2 * parent + 1;
        while(child < usedSize){
            if(child + 1 < usedSize && elem[child] < elem[child + 1]){
                child++;
            }
            if(elem[child] > elem[parent]){
                swap(child, parent);
                parent = child;
                child = 2 * parent + 1;
            }else{
                break;
            }
        }
    }

插入操作:

    public void offer(int val){
        if(isFull()){
            elem = Arrays.copyOf(elem, elem.length * 2);
        }
        elem[usedSize] = val;
        siftUp(usedSize);
        usedSize++;
    }

假设插入一个值为80的元素,操作如下:
在这里插入图片描述
在这里插入图片描述

2.4 堆的删除

堆的每次删除是指定位置,即删除根节点的元素。
操作如下:
交换根节点和最后一个节点的位置,usedSize减少1,而后对堆顶元素进行向下调整。 代码如下:

    public int poll(){
        if(isEmpty()){
            throw new UnsupportedOperationException("队列为空");
        }
        int ret = elem[0];
        elem[0] = elem[--usedSize];
        siftDown(0, usedSize);
        return ret;
    }

执行删除操作后:
在这里插入图片描述

3. 总结

到这就是优先队列的基本内容了,此外,使用优先队列可以对数据进行堆排序,将在后面讲述。


http://www.kler.cn/a/513369.html

相关文章:

  • 99.10 金融难点通俗解释:投资资本回报率(ROIC)
  • 如何使用 Pytest 断言测试 Python 异常处理
  • 数据结构之堆排序
  • 在K8S中,如果后端NFS存储的IP发送变化如何解决?
  • Qt基础项目篇——Qt版Word字处理软件
  • 计算机网络 (54)系统安全:防火墙与入侵检测
  • 2025牛客寒假训练营1【代码】 A B D E G H J M
  • 线性回归笔记1-4
  • java依赖问题
  • VBA语言的区块链
  • 人工智能技术在冷链物流行业的应用前景
  • Java Web开发高级——单元测试与集成测试
  • Yearning开源MySQL SQL审核平台
  • 稳定的通信桥梁,CCLINKIE转ModbusTCP网关实现AGV运输的光速效应
  • 基于Python的多元医疗知识图谱构建与应用研究(上)
  • 基于Hadoop MapReduce的WordCount任务实现与部署
  • 什么是可信数据空间?有什么作用?
  • matlab构造线性相位FIR滤波器
  • 【算法】字符串之227.基本计算器 -- 双栈的变形
  • docker安装rabbitmq并启动测试页面
  • Spring AI PromptChatMemoryAdvisor
  • Docker导入镜像
  • 动手学深度学习11.6. 动量法-笔记练习(PyTorch)
  • golang网络编程
  • 【leetcode100】验证二叉搜索树
  • 【力扣:新动计划,编程入门 —— 题解 ①】