优先级队列—数据结构

news/2024/11/8 8:40:36/

文章目录

  • 1.堆
    • 1.1概念
    • 1.2性质
    • 1.3存储方式
    • 1.4堆向下调整创建大根堆
    • 1.5堆的插入和删除
    • 1.6
  • 2.PriorityQueue
    • 2.1定义
    • 2.2性质
    • 2.3 PriorityQueue常用接口介绍
    • 2.4方法的使用
    • 2.5对复杂类型的PriorityQueue的使用
  • 3.堆的应用
    • 3.1PriorityQueue的实现
    • 3.2Top-k问题
    • 3.3堆排序
  • 4.经典习题

1.堆

1.1概念

小堆(小根堆):根结点比左右孩子都大的完全二叉树,左右孩子大小是不确定的
大堆(大根堆):根结点比左右孩子都小的完全二叉树,左右孩子大小是不确定的

1.2性质

(1)堆是一组集合中所有元素按照完全二叉树的顺序存储方式存储在一个一维数组中
(2)堆是一棵完全二叉树
(3)某个结点的值总是不大于或者不小于父节点的值

1.3存储方式

由于堆是一棵完全二叉树,采用顺序的方式存储,采取层序遍历,是以数组的形式进行存储;非完全二叉树,则不适合使用顺序方式进行存储,因为为了能够还原二叉树,空间中必须要存储空节点,就会导致空间利用率比较低

1.4堆向下调整创建大根堆

(1)已知孩子结点下标i:父亲结点下标为(i - 1)/2
(2)已知父亲结点下标j:左结点下标为2 * j + 1;右孩子结点下标:2 * j + 2

public class TestHeap {public int[] elem;public int usedSize;public TestHeap(){this.elem = new int[10];}//创建一个大根堆public void createHeap(int[] array){for (int i = 0; i < array.length; i++) {this.elem[i] = array[i];this.usedSize++;}for (int parent = (this.usedSize - 1 - 1)/2; parent >= 0; parent--) {shiftDown(parent,array.length);}}//向上调整public void shiftDown(int parent,int len){int child = parent * 2 + 1;while (child < len){if (child + 1 < len && elem[child] < elem[child + 1]){child++;}if (elem[child] > elem[parent]){int tmp = elem[child];elem[child] = elem[parent];elem[parent] = tmp;parent = child;child = parent * 2 + 1;}else {break;}}}
}

(3)向下调整的时间复杂度:最坏的情况是O(logN)
(4)建堆的时间复杂度是:O(N)
在这里插入图片描述

1.5堆的插入和删除

(1)堆的插入总共需要两个步骤:

  1. 先将元素放入到底层空间中(注意:空间不够时需要扩容)
  2. 将最后新插入的节点向上调整,直到满足堆的性质
    (2)堆的删除一定删除的是堆顶元素,需要两个步骤:
  3. 将堆顶元素对堆中最后一个元素交换
  4. 将堆中有效数据个数减少一个
  5. 对堆顶元素进行向下调整
import java.awt.*;
import java.util.Arrays;public class TestHeap {public int[] elem;public int usedSize;public TestHeap(){this.elem = new int[10];}//向上调整public void shiftDown(int parent,int len){int child = parent * 2 + 1;while (child < len){if (child + 1 < len && elem[child] < elem[child + 1]){child++;}if (elem[child] > elem[parent]){int tmp = elem[child];elem[child] = elem[parent];elem[parent] = tmp;parent = child;child = parent * 2 + 1;}else {break;}}}//添加一个数据到堆中public void push(int val){if (isFull()){this.elem = Arrays.copyOf(this.elem,this.elem.length * 2);}//把添加的数据放到堆中的最后一个位置this.elem[this.usedSize] = val;this.usedSize++;//向上调整将其变成一个大根堆shiftUp(this.usedSize - 1);}//向上调整public void shiftUp(int child){int parent = (child - 1) /2;while (parent >= 0){if (this.elem[parent] < this.elem[child]){int tmp = elem[child];elem[child] = elem[parent];elem[parent] = tmp;child = parent;parent = (child - 1) / 2;}else {break;}}}//判断堆是否为空public boolean isFull(){if (this.elem.length == this.usedSize){return true;}return false;}//出队,删除堆顶元素public int remove(){if (this.elem.length == 0){throw new HeadlessException("堆为空");}//交换堆顶元素和堆的最后一个元素int tmp = this.elem[this.usedSize - 1];this.elem[this.usedSize - 1] = this.elem[0];this.elem[0] = tmp;this.usedSize--;shiftDown(0,this.usedSize);return tmp;}
}

1.6

2.PriorityQueue

2.1定义

  1. 数据结构应该提供了两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象,这种数据结构就是优先级队列(PriorityQueue)
  2. PriorityQueue底层使用了堆的数据结构
  3. Java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级队列,PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的

2.2性质

  1. 使用时必须导入PriorityQueue所在的包,import java.util.PriorityQueue;
  2. PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出ClassCastException异常
  3. 不能插入null对象,否则会抛出NullPointerException
  4. 没有容量限制,可以插入任意多个元素,其内部可以自动扩容
  5. 插入和删除元素的时间复杂度为O(logN)
  6. PriorityQueue底层使用了堆数据结构
  7. PriorityQueue默认情况下是小堆,也就是每次获取到的元素都是最小的元素

2.3 PriorityQueue常用接口介绍

import java.util.*;
public class TestHeap2 {public static void main(String[] args) {//创建一个空的优先级队列,默认容量是11(默认是小根堆)PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();priorityQueue.offer(311);priorityQueue.offer(223);System.out.println(priorityQueue);//[223, 311]//创建一个初始容量为3的优先级队列(默认是小根堆)PriorityQueue<Integer> priorityQueue2 = new PriorityQueue<>(3);priorityQueue2.offer(16);priorityQueue2.offer(4);System.out.println(priorityQueue2);//[4, 16]//创建一个大根堆PriorityQueue<Integer> priorityQueue1 = new PriorityQueue<>(Collections.reverseOrder());priorityQueue1.offer(16);priorityQueue1.offer(4);System.out.println(priorityQueue1);//[16, 4]//创建一个大根堆(PriorityQueue<Integer> priorityQueue3 = new PriorityQueue<>(new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o2 - o1;}});priorityQueue3.offer(16);priorityQueue3.offer(4);System.out.println(priorityQueue3);//[16, 4]}
}

2.4方法的使用

public class TestHeap2 {public static void main(String[] args) {//创建一个空的优先级队列,默认容量是11(默认是小根堆)PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();priorityQueue.offer(311);//添加元素priorityQueue.offer(223);System.out.println(priorityQueue);//[223, 311]System.out.println(priorityQueue.peek());//获取堆顶元素 233priorityQueue.poll();//删除堆顶元素System.out.println(priorityQueue);//[311]System.out.println(priorityQueue.isEmpty());//falseSystem.out.println(priorityQueue.size());//1}
}

2.5对复杂类型的PriorityQueue的使用

  1. 实现Comparable接口
import java.util.*;class Student implements Comparable<Student>{public String name;public int age;public Student(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic int compareTo(Student o) {
//        return this.age - o.age;//小堆return o.age - this.age;//大堆}
}
public class TestHeap2 {public static void main(String[] args) {PriorityQueue<Student> students = new PriorityQueue<>();students.offer(new Student("张三",10));students.offer(new Student("李四",19));System.out.println(students);}
}
  1. 重写比较器
import java.util.*;
class Student{public String name;public int age;public Student(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}
}/*** 重写comparator年龄比较器*/
class AgeComparator implements Comparator<Student>{@Overridepublic int compare(Student o1, Student o2) {
//        return o1.age - o2.age;//小堆return o2.age - o1.age;//大堆}
}
public class TestHeap2 {public static void main(String[] args) {AgeComparator ageComparator = new AgeComparator();PriorityQueue<Student> students = new PriorityQueue<>(ageComparator);students.offer(new Student("张三",10));students.offer(new Student("李四",19));System.out.println(students);}
}

3.堆的应用

3.1PriorityQueue的实现

用堆作为底层结构封装优先级队列

3.2Top-k问题

基本思路:

  1. 用数据集合中前K个元素来建堆(前k个最大的元素,则建小堆;前k个最小的元素,则建大堆)
  2. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素
/*** 找前k个最小值* @param array* @param k* @return*/public int[] topK(int[] array,int k){//创建一个大堆PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(k, new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o2 - o1;}});//PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(k, Collections.reverseOrder());for (int i = 0; i < array.length; i++) {if (i < k){priorityQueue.offer(array[i]);}else {if (array[i] < priorityQueue.peek()){priorityQueue.poll();priorityQueue.offer(array[i]);}}}int[] tmp = new int[priorityQueue.size()];for (int i = 0; i < tmp.length; i++) {tmp[i] = priorityQueue.poll();}return tmp;}

3.3堆排序

思路:

  1. 建堆
    升序:建大堆
    降序:建小堆
  2. 利用堆删除思想来进行排序,向上调整进行排序
/*** 堆排序* @param array*/public static void heapSort(int[] array){//创建一个大根堆createHeap(array);//向下调整进行排序int end = array.length - 1;while (end > 0){swap(array,0,end);shiftDown(array,0,end);end--;}}/*** 创建大根堆* @param array*/private static void createHeap(int[] array){for (int parent = (array.length - 1- 1)/2;parent >= 0;parent--){shiftDown(array,parent,array.length);}}/*** 向下调整* @param array* @param parent* @param len*/private static void shiftDown(int[] array,int parent,int len){int child = parent * 2 + 1;while (child < len){if (child + 1 < len && array[child] < array[child + 1]){child++;}if (array[child] > array[parent]){swap(array,child,parent);parent = child;child = parent * 2 + 1;}else {break;}}}

4.经典习题

1.下列关键字序列为堆的是:(A)
A: 100,60,70,50,32,65
B: 60,70,65,50,32,100
C: 65,100,70,32,50,60
D: 70,65,100,32,50,60
E: 32,50,100,70,65,60
F: 50,100,70,65,60,32
解析:堆只能是小根堆或者大根堆,也就是说他的父亲结点的大小必须全部大于孩子节点(大根堆)或者小于孩子节点(小根堆)

2.已知小根堆为8,15,10,21,34,16,12,删除关键字8之后需重建堆,在此过程中,关键字之间的比较次数是©
A: 1 B: 2 C: 3 D: 4在这里插入图片描述

3.一组记录排序码为(5 11 7 2 3 17),则利用堆排序方法建立的初始堆为©
A: (11 5 7 2 3 17)
B: (11 5 7 2 17 3)
C: (17 11 7 2 3 5)
D: (17 11 7 5 3 2)
E: (17 7 11 3 5 2)
F: (17 7 11 3 2 5)
解析:堆排需要先建立大根堆
在这里插入图片描述

4.最小堆[0,3,2,5,7,4,6,8],在删除堆顶元素0之后,其结果是©
A: [3,2,5,7,4,6,8]
B: [2,3,5,7,4,6,8]
C: [2,3,4,5,7,8,6]
D: [2,3,4,5,6,7,8]
解析:删除元素是交换根结点和最后一个元素,再进行调整


http://www.ppmy.cn/news/1059871.html

相关文章

【中危】Apache XML Graphics Batik<1.17 存在SSRF漏洞 (CVE-2022-44729)

zhi.oscs1024.com​​​​​ 漏洞类型SSRF发现时间2023-08-23漏洞等级中危MPS编号MPS-2022-63578CVE编号CVE-2022-44729漏洞影响广度极小 漏洞危害 OSCS 描述Apache XML Graphics Batik 是一个开源的、用于处理可缩放矢量图形(SVG)格式图像的工具库。 受影响版本中&#xff0…

智能引领物流,AGV与工控机完美搭配!

AGV小车现已广泛被制造业使用&#xff0c;成为智能工厂、智能车间的重要组成部分。在制造业的生产中用AGV小车代替工进行装载、搬运、卸载等工作&#xff0c;实现了车间物流的自动化&#xff0c;极大的提高了生产自动化水平。通过AGV小车与产线进行完美结合&#xff0c;可自动化…

零信任体系化能力建设(4):应用安全与开发部署

应用和工作负载是企业资产的重要组成部分&#xff0c;也是用户访问企业数据的主要手段和攻击者关注的首要目标&#xff0c;因此&#xff0c;强化对IT栈内软件部分的安全控制是企业推进零信任成熟度的必由之路。 通常&#xff0c;零信任网络访问&#xff08;ZTNA&#xff09;通…

Python requests实现图片上传接口自动化测试

最近帮别人写个小需求&#xff0c;需要本地自动化截图&#xff0c;然后图片自动化上传到又拍云&#xff0c;实现自动截图非常简单&#xff0c;在这里就不详细介绍了&#xff0c;主要和大家写下&#xff0c;如何通过Pythonrequests实现上传本地图片到又拍云服务器。 话不多说&a…

“Go程序员面试笔试宝典”复习便签

一.逃逸分析 1.1逃逸分析是什么&#xff1f; 逃逸分析&#xff0c;主要是Go编译器用来决定变量分配在堆或者栈的手段。 区分于C/C手动管理内存分配&#xff0c;Go将这些工作交给了编译器。 1.2逃逸分析有什么作用 解放程序员。程序员不需要手动指定指针分配内存。 灵活的…

GFPGAN 集成Flask 接口化改造

GFPGAN是一款腾讯开源的人脸高清修复模型&#xff0c;基于github上提供的demo&#xff0c;可以简单的集成Flask以实现功能接口化。 GFPGAN的安装&#xff0c;Flask的安装请参见其他文章。 如若使用POSTMAN进行测试&#xff0c;需使用POST方式&#xff0c;form-data的请求体&am…

远程连接虚拟机中ubuntu报错:Network error:Connection refused

ping检测一下虚拟机 可以ping通&#xff0c;说明主机是没问题 #检查ssh是否安装&#xff1a; ps -e |grep ssh发现ssh没有安装 #安装openssh-server sudo apt-get install openssh-server#启动ssh service ssh startps -e |grep ssh检查一下防火墙 #防火墙状态查看 sudo ufw…

centos7.9 用docker安装mysql8.0

一.安装docker 切换到root1.安装依赖包 $ yum install -y yum-utils2.registry更换阿里源 $ yum-config-manager \--add-repo \https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo$ sed -i s/download.docker.com/mirrors.aliyun.com\/docker-ce/g /etc/yum.…