Java实现优先级队列(堆)

前言

在学习完二叉树的相关知识后,我们对数据结构有了更多的认识,本文将介绍到优先级队列(堆)

1.优先级队列

1.1概念

前面介绍过队列,队列是一种先进先出(FIFO)的数据结构,但有些情况下,操作的数据可能带有优先级,一般出队列时,可能需要优先级高的元素先出队列,该中场景下,使用队列显然不合适,对此数据结构应该提供两个最基本的操作,一个是返回最高优先级对象一个是添加新的对象。这种数据结构就是优先级队列(Priority Queue)

1.2模拟实现

JDK1.8中的PriorityQueue底层使用了堆这种数据结构,而堆实际就是在完全二叉树的基础上进行了一些调整

2.堆

2.1概念

如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆 


2.2性质 

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

2.3堆的存储方式

根据堆的概念以及性质,我们可以根据层序的规则采用顺序的方式来高效存储

注意:对于非完全二叉树,则不适合使用顺序方式进行存储,因为为了能够还原二叉树,空间中必须要存储空节点,就会导致空间利用率比较低

根据我们对二叉树性质的学习,有一下性质:

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

2.4堆的创建 

我们以集合{ 27,15,19,18,28,34,65,49,25,37 }中的数据为例,建堆

按照二叉树的顺序排列发现,根子树已经为堆的结构,只需将根节点向下调整即可

2.4.1向下调整

以大根堆为例

    private void swap(int i,int j){
        int tmp=elem[i];
        elem[i]=elem[j];
        elem[j]=tmp;
    }
    //大根堆
    /*
    时间复杂度为从根一路比较到叶子,比较的次数为完全二叉树的高度,O(log2 n)
     */
    public void siftDown(int parent,int end){
        int child=parent*2+1;//先标记左孩子,因为可能该树没有右孩子
        while(child<end) {
            if (child + 1 < end && elem[child] < elem[child + 1]) {//假如右孩子存在,找到其中较大的孩子
                child++;
            }
            //找到了最大的子孩子
            if (elem[child] > elem[parent]) {//若孩子比双亲大,进行交换,同时继续向下调整
                swap(child, parent);
                parent = child;
                child = 2 * parent + 1;
            } else {//若双亲大,则满足堆的特性,即跳出循环
                break;
            }
        }
    }

注意:在调整以parent为根的二叉树时,必须要满足parent的左子树和右子树已经是堆了才可以向下调整

2.4.2堆创建

但是当二叉树中,子树不满足堆时,该如何建堆呢?

这里,我们找到第一个非叶子节点,从该节点开始往前一直到根节点,遇到一个节点,向下调整

    public void creatHeap(){
        for (int parent = (usedSize-1-1/2); parent >=0 ; parent--) {
            siftDown(parent,usedSize);
        }
    }

2.5堆的插入与删除 

 2.5.1插入

堆的插入总共需要两个步骤:
1. 先将元素放入到底层空间中(注意:空间不够时需要扩容)
2. 将最后新插入的节点向上调整,直到满足堆的性质

//以大根堆为例
    public void siftUp(int child){
        int parent=(child-1)/2;
        while(parent>=0){
            if(elem[child]>elem[parent]){
                swap(child,parent);
                child=parent;
                parent=(child-1)/2;
            }else{
                break;
            }
        }
    }

2.5.2删除

注意:堆的删除一定删除的是堆顶元素
1. 将堆顶元素对堆中最后一个元素交换
2. 将堆中有效数据个数减少一个
3. 对堆顶元素进行向下调整
 

  public boolean isFull(){
        return usedSize==elem.length;
    }

    public int poll(){
        if(isEmpty()){
            return -1;
        }
        int old=elem[0];
        swap(0,usedSize-1);
        usedSize--;
        siftDown(0,usedSize-1);
        return old;
    }
  public boolean isEmpty(){
        return usedSize==0;
    }

2.6模拟实现优先级

public class Test {
    public static void main(String[] args) {
        int[] array = {25, 56, 21, 45, 75};
        TestHeap testHeap = new TestHeap();
        testHeap.init(array);
        testHeap.creatHeap();

        testHeap.heapSort();

    }
}

 如果上述内容对您有帮助,希望给个三连谢谢!

  • 82
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 68
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值