Java堆&优先级队列示例讲解(上)

作者:爱干饭的猿 时间:2023-04-09 11:09:59 

1. 二叉树的顺序存储

1.1 存储方式

使用数组保存二叉树结构,方式即将二叉树用 层序遍历 方式放入数组中。

一般只适合表示完全二叉树,这种方式的主要用法就是堆的表示。

因为非完全二叉树会有空间的浪费(所有非完全二叉树用链式存储)。

Java堆&优先级队列示例讲解(上)

1.2 下标关系

已知双亲 (parent) 的下标,则:

左孩子 (left) 下标 = 2 * parent + 1;

右孩子 (right) 下标 = 2 * parent + 2;

已知孩子(不区分左右) (child) 下标,则:

双亲 (parent) 下标 = (child - 1) / 2;

2. 堆(heap)

2.1 概念

1. 堆逻辑上是一棵完全二叉树

2. 堆物理上是保存在数组中

3. 满足任意结点的值都大于其子树中结点的值,叫做大堆,或者大根堆,或者最大堆

4. 反之,则是小堆,或者小根堆,或者最小堆

5. 堆的基本作用是,快速找集合中的 最值

2.2 操作-(下沉&上浮)本例是最大堆

元素下沉:

/**
    * 下沉操作
    */
   public void siftDown(int k){
       //还存在子树
       while (leftChild(k) < data.size()){
           int j = leftChild(k);
           //判断是否存在右子树且大于左子树的值
           if(j+1 < data.size() && data.get(j+1) > data.get(j)){
               j=j+1;
           }
           //此时j为左右子树最大值
           //和当前节点比较大小
           if(data.get(j) <= data.get(k)){
               break;
           }else {
               swap(k,j);
               k=j;
           }
       }
   }

元素上浮:

/**
    * 上浮操作
    */
   // 上浮操作的终止条件: 已经走到根节点 || 当前节点值 <= 父节点值
   // 循环的迭代条件 : 还存在父节点并且当前节点值 > 父节点值
   private void siftUp(int k) {
       while (k>0 && data.get(k)>data.get(parent(k))){
           swap(k,parent(k));
           k=parent(k);
       }
   }

其中swap方法是交换操作:

//交换三连
   private void swap(int i,int j) {
       int temp = data.get(j);
       data.set(j,data.get(i));
       data.set(i,temp);
   }

堆化数组:

/**
    * 将任意数组堆化
    * @param arr
    */
   public MaxHeap(int[] arr){
       data = new ArrayList<>(arr.length);
       // 1.先将arr的所有元素复制到data数组中
       for(int i : arr){
           data.add(i);
       }
       // 2.从最后一个非叶子结点开始进行siftDown
       for (int i = parent(data.size()-1); i >=0 ; i--) {
           siftDown(i);
       }
   }

图示:

以此数组为例:

// 调整前
int[] array = { 27,15,19,18,28,34,65,49,25,37 };
// 调整后
int[] array = { 15,18,19,25,28,34,65,49,27,37 };

Java堆&优先级队列示例讲解(上)

时间复杂度分析:

最坏的情况即图示的情况,从根一路比较到叶子,比较的次数为完全二叉树的高度

即时间复杂度为 O(log(n))

2.3 建堆-完整代码(最大堆)

/**
* 基于整形最大堆实现
* 时根节点从0开始编号,若此时节点编号为k
* 左孩子为2k+1
* 右孩子为2k+2
* 父节点为(k-1)/2
*/

import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;

public class MaxHeap {
   // 使用JDK的动态数组(ArrayList)来存储一个最大堆
   List<Integer> data;

// 构造方法的this调用
   public MaxHeap(){
       this(10);
   }

// 初始化的堆大小
   public MaxHeap(int size){
       data = new ArrayList<>(size);
   }

/**
    * 将任意数组堆化
    * @param arr
    */
   public MaxHeap(int[] arr){
       data = new ArrayList<>(arr.length);
       // 1.先将arr的所有元素复制到data数组中
       for(int i : arr){
           data.add(i);
       }
       // 2.从最后一个非叶子结点开始进行siftDown
       for (int i = parent(data.size()-1); i >=0 ; i--) {
           siftDown(i);
       }
   }

/**
    * 向最大堆中增加值为Value的元素
    * @param value
    */
   public void add(int value){
       //1.先直接加到堆的末尾
       data.add(value);
       //2.元素上浮操作
       siftUp(data.size()-1);
   }

/**
    * 只找到堆顶元素值
    * @return
    */
   public int peekMax (){
       if(isEmpty()){
           throw new NoSuchElementException("heap is empty!connot peek");
       }
       return data.get(0);
   }

/**
    * 取出当前最大堆的最大值
    */
   public int extractMax(){
       // 取值一定注意判空
       if(isEmpty()){
           throw new NoSuchElementException("heap is empty!connot extract");
       }
       int max = data.get(0);
       // 1.将数组末尾元素顶到堆顶
       int lastValue =data.get(data.size()-1);
       data.set(0,lastValue);
       // 2.将数组末尾的元素删除
       data.remove(data.size()-1);
       // 3.进行元素的下沉操作
       siftDown(0);
       return max;
   }

/**
    * 下沉操作
    */
   public void siftDown(int k){
       //还存在子树
       while (leftChild(k) < data.size()){
           int j = leftChild(k);
           //判断是否存在右子树且大于左子树的值
           if(j+1 < data.size() && data.get(j+1) > data.get(j)){
               j=j+1;
           }
           //此时j为左右子树最大值
           //和当前节点比较大小
           if(data.get(j) <= data.get(k)){
               break;
           }else {
               swap(k,j);
               k=j;
           }
       }
   }

/**
    * 上浮操作
    */
   // 上浮操作的终止条件: 已经走到根节点 || 当前节点值 <= 父节点值
   // 循环的迭代条件 : 还存在父节点并且当前节点值 > 父节点值
   private void siftUp(int k) {
       while (k>0 && data.get(k)>data.get(parent(k))){
           swap(k,parent(k));
           k=parent(k);
       }
   }

//交换三连
   private void swap(int i,int j) {
       int temp = data.get(j);
       data.set(j,data.get(i));
       data.set(i,temp);
   }

//判读堆为空
   public boolean isEmpty(){
       return data.size() == 0;
   }
   //根据索引找父节点
   public int parent(int k){
       return (k-1)>>1;
   }
   //根据索引找左孩子
   public int leftChild(int k){
       return k<<2+1;
   }
   //根据索引找右孩子
   public int rightChild(int k){
       return k<<2+2;
   }

@Override
   public String toString() {
       return data.toString();
   }
}

ps:随机数操作

int[] data=new int[10000];
       //随机数
       ThreadLocalRandom random = ThreadLocalRandom.current();
       for (int i = 0; i < data.length; i++) {
           data[i] = random.nextInt();
       }

3. 优先级队列

详见下节:Java堆&优先级队列示例讲解(下)

来源:https://blog.csdn.net/m0_62218217/article/details/123279819

标签:Java,堆,优先级队列
0
投稿

猜你喜欢

  • newtonsoft.json解析天气数据出错解决方法

    2022-03-10 12:23:21
  • Thymeleaf 3.0 自定义标签方言属性的实例讲解

    2023-03-24 20:40:23
  • Java Fluent Mybatis 项目工程化与常规操作详解流程篇 上

    2022-08-28 16:47:28
  • Android垂直切换的圆角Banner与垂直指示器相关介绍与应用详解

    2023-01-09 02:59:15
  • Android动态自定义圆形进度条

    2022-07-21 21:29:50
  • Java集合框架ArrayList源码分析(一)

    2022-05-12 19:32:50
  • java 格式化输出数字的方法

    2022-11-10 05:04:07
  • C#中#define后面只加一个参数的解释

    2022-09-06 07:23:55
  • C#特性(Attribute)

    2022-08-18 18:34:51
  • 如何在c语言下关闭socket

    2021-12-26 10:22:46
  • C#无损转换Image为Icon的方法

    2023-07-30 02:38:40
  • 详解C#借助.NET框架中的XmlTextReader类读取XML的方法

    2023-01-18 23:31:47
  • 详解SpringMVC @RequestBody接收Json对象字符串

    2022-03-20 05:00:10
  • C#验证身份证的函数

    2022-06-16 04:49:37
  • C#利用File方法对文件的操作总结(字节写入和读取)

    2022-07-20 09:30:55
  • Java应用开源框架实现简易web搜索引擎

    2023-08-22 20:20:54
  • Java+Swing实现医院管理系统的完整代码

    2023-03-17 00:40:21
  • 浅谈为什么要使用mybatis的@param

    2023-07-01 20:12:39
  • 再谈异常处理try catch finally

    2021-11-12 11:17:31
  • Java中如何对字符串进行utf-8编码

    2023-01-20 16:13:04
  • asp之家 软件编程 m.aspxhome.com