Java实现优先级队列--堆

目录

1.优先级队列和堆的概念

2.优先级队列的实现


1.优先级队列和堆的概念

1.1.什么是优先级队列??

我们都学过队列,队列是一种先进先出的数据结构,但有些情况下,操作的数据可能带有优先级,一般出队列时,可能需要优先级高的元素先出队列,这就是优先级队列。比如有时候我们在打游戏的时候,别人打电话给你,那么系统一定是先处理打进来的电话。

1.2.什么是堆??

简言之,堆其实就是一棵完全二叉树,它的底层是一个数组,数组中存储这棵完全二叉树层序遍历的结果,按种类分,它又分为大根堆和小根堆,请看下图:

  • 性质:

1.堆中的某个结点的值总是不大于或不于其父节点的值;

2.堆总是一棵完全二叉树;

  所以完全二叉树的性质就可以拿来用:

  • 如果 i 为0,则 i 表示的结点为根节点,否则 i 结点的双亲结点为(i - 1)/ 2;
  • 如果 2 * i + 1 小于结点个数,则节点 i 的左孩子下标为 2 * i + 1,否则没有左孩子;
  • 如果 2 * i + 2 小于结点个数,则节点 i 的左孩子下标为 2 * i + 2,否则没有右孩子。

注意堆是一棵完全二叉树,它有着层序遍历的规则,所以采用顺序存储的方式来提高效率,而非完全二叉树是不适合使用顺序存储的方式来进行存储的,因为为了能够还原二叉树,空间中必须要存储空节点,就会导致空间利用率比较低。


2.优先级队列的实现

2.1创建大根堆

public class TestHeap {
    public int elem[];
    public int usedSize;
    public static int DEFAULT_SIZE = 10;

    public TestHeap() {
        this.elem = new int[DEFAULT_SIZE];
    }

    public int[] createHeap(int[] array) {
        //准备数据
        for (int i = 0; i < array.length; i++) {
            this.elem[i] = array[i];
            this.usedSize++;
        }
        //创建大根堆
        for (int p = (this.usedSize - 1 - 1) / 2; p >= 0 ; p--) {
            //向下调整
            shiftDown(p, this.usedSize);
        }
        return this.elem;
    }
    private void shiftDown(int parent, int len) {
        //左孩子
        int child = 2 * parent + 1;
        //每一次调整的结束条件 --> child < len
        while(child < len) {
            //拿到左右孩子的最大值
            if(child + 1 < len && this.elem[child] < this.elem[child + 1]) {
                child++;
            }
            if(this.elem[child] > this.elem[parent]){
                swap(parent, child);
                //继续判断它子树是否调整
                parent = child;
                child = 2 * parent + 1;
            } else {
                //无需调整
                break;
            }
        }
    }
    private void swap(int parent, int child) {
        int tmp = this.elem[parent];
        this.elem[parent] = this.elem[child];
        this.elem[child] = tmp;
    }
}
  • 思路

  •  时间复杂度

 1.向下调整的时间复杂度:

最坏情况就是调整完 0 这棵树,那么调整的次数就是完全二叉树的高度,此时时间复杂度为 O(log2^n);

2.建大根堆的时间复杂度:

分析建堆的时间复杂度,我们需要从它的思想层面去分析,不能果断的断定:

每次调整的时间为 O(log2^n),然后调整 n 次,所以时间复杂度为 n * (log2^n),这是错误的!


 假设这棵完全二叉树是一棵满二叉树,树的高度为 n ,那么我们只需要调整第 n 层以上的树(调整 1 -  n - 1层):


 2.2插入数据

   public void offerHeap(int val) {
        if(isFull()) {
            this.elem = Arrays.copyOf(this.elem, 2*this.elem.length);
        }
        //1.放在最后一个位置
        this.elem[this.usedSize] = val;
        //2.进行向上调整
        shiftUp(usedSize);
        //3.有效数据+1
        this.usedSize++;
    }
    private void shiftUp(int child) {
        //父节点
        int parent = (child - 1) / 2;
        while(child > 0) {
            if(this.elem[child] > this.elem[parent]) {
                swap(child,parent);
                //向上调整
                child = parent;
                parent = (child - 1) / 2;
            } else {
                break;
            }
        }
    } 
    private boolean isFull() {
        return this.usedSize == this.elem.length;
    }
  • 思路

1.将待插入的数据放在 usedSize 位置

2.从usedSize下标的数据向上调整


 2.3删除数据

    public int pollHeap() {
        if(isEmpty()) {
            throw new MyHeapIsEmptyException("优先级队列为空!");
        }
        int tmp = this.elem[0];
        //将最后一个数据与堆顶数据进行交换
        swap(usedSize-1,0);
        this.usedSize--;
        //向下调整
        shiftDown(0, this.usedSize);
        return tmp;
    }
    private boolean isEmpty() {
        return this.usedSize == 0;
    }
  • 思路

1.将最后一个数据与堆顶数据进行交换

3.将堆中有效数据个数减少一个

2.然后再进行向下调整


2.4获取堆顶元素

    public int peekHeap() {
        if(isEmpty()) {
            throw new MyHeapIsEmptyException("优先级队列为空!");
        }
        return this.elem[0];
    }

谢谢观看!!!

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 15
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Master_hl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值