• 【数据结构】——优先级队列(堆)


    一、概念

    在一些情况下,操作的数据可能带有优先级,一般出队列时,可能需要优先级高的元素出队;在这种的情况下,就可以使用优先级队列:返回最高优先级对象和添加新的对象。

    堆的性质:

    1. 堆中某个节点的值总是不大于或小于其父亲节点的值
    2. 堆总是一颗完全二叉树

    二、优先级队列的实现

    顺序存储实现堆:

    1. public int[] elem;
    2. public int usedSize;
    3. public TestHeap(){
    4. this.elem = new int[10];
    5. this.usedSize = 0;
    6. }
    7. public void initArray(int[] array){
    8. elem = Arrays.copyOf(array, array.length);
    9. usedSize = elem.length;
    10. }

    建大根堆:

    1. /**
    2. * 建堆[大根堆]
    3. */
    4. public void createHeap(){
    5. for (int parent = (usedSize-1-1)/2; parent >= 0; parent--) {
    6. shiftDown(parent,usedSize);
    7. }
    8. }
    9. /**
    10. * 实现向下调整
    11. * @param parent 每颗子树的根结点的下标
    12. * @param len
    13. */
    14. private void shiftDown(int parent,int len){
    15. int child = 2*parent+1;
    16. //最起码有左孩子
    17. while(child < len){
    18. //判断左右孩子谁最大,前提 必须有右孩子
    19. if (child+1 < len&& elem[child] < elem[child+1]){
    20. child++;//此时保留了最大值下标
    21. }
    22. if (elem[child] > elem[parent]){
    23. swap(elem,child,parent);
    24. parent = child;
    25. child = 2*parent+1;
    26. }else {
    27. break;
    28. }
    29. }
    30. }
    31. private void swap(int[] array,int i,int j){
    32. int tmp = array[i];
    33. array[i] = array[j];
    34. array[j] = tmp;
    35. }

    入队:

    1. public void offer(int x){
    2. if (isFull()){
    3. elem = Arrays.copyOf(elem, elem.length*2);
    4. }
    5. this.elem[usedSize] = x;
    6. usedSize++;
    7. shiftUp(usedSize-1);
    8. }
    9. private void shiftUp(int child) {
    10. int parent = (child - 1) / 2;
    11. while (child > 0) {
    12. if (elem[child] > elem[parent]) {
    13. swap(elem, child, parent);
    14. child = parent;
    15. parent = (child - 1) / 2;
    16. } else {
    17. break;
    18. }
    19. }
    20. }
    21. private boolean isFull() {
    22. return usedSize == elem.length;
    23. }

    删除:

    1. //优先级队列的删除 只能删除堆顶的元素
    2. public int poll(){
    3. if (isEmpty()) {
    4. return -1;
    5. }
    6. int old = elem[0];
    7. swap(elem,0,usedSize-1);
    8. usedSize--;
    9. shiftDown(0,usedSize);
    10. return old;
    11. }
    12. private boolean isEmpty(){
    13. return usedSize == 0;
    14. }

    三、常用接口的介绍

    Java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级队列,PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的,本文主要介绍PriorityQueue。

     使用PriorityQueue的使用注意:

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

       

    接口的介绍:

    思路一:

    1. 把数据全部放到一个小根堆当中,堆顶元素就是最小值
    2. 出队k次,就能找到k个最小的值,每出队一次就会重新调整为小根堆,时间复杂度为O(n^2).
    1. public class Test {
    2. public static void topK1(int[] array,int k){
    3. PriorityQueue priorityQueue = new PriorityQueue<>(new Comparator() {
    4. @Override
    5. public int compare(Integer o1, Integer o2) {
    6. return o1 - o2;
    7. }
    8. });
    9. for (int i = 0; i < array.length; i++){
    10. priorityQueue.offer(array[i]);
    11. }
    12. for (int i = 0; i < k; i++) {
    13. int val = priorityQueue.poll();
    14. System.out.print(val+" ");
    15. }
    16. System.out.println();
    17. }
    18. public static void main(String[] args) {
    19. int[] array = {12,45,32,17,2,18,5,10};
    20. topK1(array,3);
    21. }
    22. public static void main1(String[] args) {
    23. TestHeap testHeap = new TestHeap();
    24. int[] array = {27,15,19,18,28,34,65,49,25,37};
    25. testHeap.initArray(array);
    26. testHeap.createHeap();
    27. testHeap.offer(100);
    28. System.out.println();
    29. }
    30. }

    思路二:

    1. 建立一个只有k个元素的大根堆,入队数组的前k个元素,此时堆顶元素就是最大的元素,
    2. 然后再遍历数组剩下的元素,如果小于堆顶元素,就交换,,再调整为大根堆
    3. 遍历完数组之后,大根堆里就是前k个最小的元素
    1. class Solution {
    2. public int[] smallestK(int[] array, int k) {
    3. int[] ret = new int[k];
    4. if(k == 0){
    5. return ret;
    6. }
    7. PriorityQueue maxPq = new PriorityQueue<>(k,new Comparator() {
    8. @Override
    9. public int compare(Integer o1, Integer o2) {
    10. return o2 - o1;
    11. }
    12. });
    13. for (int i = 0; i < array.length; i++) {
    14. if (maxPq.size() < k){
    15. maxPq.offer(array[i]);
    16. }else {
    17. int top = maxPq.peek();
    18. if (array[i] < top){
    19. maxPq.poll();
    20. maxPq.offer(array[i]);
    21. }
    22. }
    23. }
    24. for (int i = 0; i < k; i++) {
    25. int val = maxPq.poll();
    26. ret[i] = val;
    27. }
    28. return ret;
    29. }
    30. }

    大根堆实现从小到大排序:

    1. /**
    2. * 实现向下调整
    3. * @param parent 每颗子树的根结点的下标
    4. * @param len
    5. */
    6. private void shiftDown(int parent,int len){
    7. int child = 2*parent+1;
    8. //最起码有左孩子
    9. while(child < len){
    10. //判断左右孩子谁最大,前提 必须有右孩子
    11. if (child+1 < len && elem[child] < elem[child+1]){
    12. child++;//此时保留了最大值下标
    13. }
    14. if (elem[child] > elem[parent]){
    15. swap(elem,child,parent);
    16. parent = child;
    17. child = 2*parent+1;
    18. }else {
    19. break;
    20. }
    21. }
    22. }
    23. private void swap(int[] array,int i,int j){
    24. int tmp = array[i];
    25. array[i] = array[j];
    26. array[j] = tmp;
    27. }
    28. public void heapSort(){
    29. int end = usedSize-1;
    30. while (end > 0){
    31. swap(elem,0,end);
    32. shiftDown(0,end);
    33. end--;
    34. }
    35. }

  • 相关阅读:
    景观连接度和连通性的区别
    Linux【1】vim编辑器
    创建servlet,完成信息获取
    【深度学习】优化器详解
    github提示Permission denied (publickey),如何才能解决
    webui automatic1111上可以跑stable diffusion 3的方法
    fabric.js的使用
    简单漂亮的登录页面
    MySQL 8.0安装及配置教程
    刚刚:2023阿里云双十一优惠活动上线了!
  • 原文地址:https://blog.csdn.net/weixin_62678196/article/details/126691881