PriorityQueue(一)——用堆实现优先级队列

思路大纲:

因为堆是一棵完全二叉树,所以可以使用数组存储。

所以,首先需要创建数组,并对数组分配内存和初始化,模拟实现包括创建大根堆、向下调整、插入、删除等功能,其中这些功能需要许多交换、判断isFull或者empty,所以这些可以单独写一个方法

思维导图:

模拟实现:

完整代码可以参考我的码云:

PriorityQueue(一)——用堆模拟实现优先级队列

堆是一棵完全二叉树,因此可以层序的规则采用顺序的方式来高效存储。

基本要素的创建:

包括:数组、记录元素个数的计数器 

数组创建完成,1,首先分配内存2、其次数组的初始化

public class TestHeap {
    //定义数组
    public int[] elem;
    //定义元素个数
    public int usedSize;
    //数组分配内存
    public TestHeap(){
       this.elem = new int[10];
    }
    //数组初始化
    public void initElem(int[] array){
        //初始化的个数取决于传过来的array的length
        for (int i = 0; i < array.length; i++) {
            elem[i] = array[i];
            usedSize++;
        }
    }
}

 创建大根堆:

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

创建大根堆的思路:

 usedSize是元素的个数,因为是使用数组实现的,所以usedSize-1才是真正子在树中第“usedSize”个元素

得到父母结点的公式

  public void createHeap(){
        //只需要得到有子树的父母结点
        for (int parent = (usedSize -1 -1) / 2; parent >= 0 ; parent--) {
            siftDown(parent , usedSize);
        }
    }

其中对树中的元素进行大根堆排序 进行调整称作向下调整,思路里第一条强调的很重要

向下调整:

向下调整的思路就是大根堆中的那个方法,思路一样

 代码中的注释也是强调的重点:

    public void siftDown(int parent,int len){
    int childe = 2*parent +1;
    //至少 有左孩子
    while(childe < len){
        //如果有右孩子 那么右孩子的下标也要小于len
        //找出两个中间的最大值
    if(childe+1<len&&elem[childe] < elem[childe+1]){
             childe = childe+1;
    }
    if(elem[childe]>elem[parent]){
        swap(childe,parent);
        parent = childe;
        childe = 2*parent + 1;
    }else {
        break;
    }
    }

 插入元素:

插入之后也要保证大根堆的要求

先插入最后一个位置,然后进行child和parent的比较,下面c是child的缩写,p是parent的缩写

插入时要判断是否数组已经满了,如果满了就扩容

在把元素插入后,将插入的元素也按照大根堆排序完成的过程叫做向上调整

但传入向上调整的参数是usedSize加1前的值,也就是插入元素,计数器还没加,就传,原因是usedSize是可以存储元素的下标,此时传的usedSize正好是数组中元素的个数

  public void push(int val){
        //满了
        if(isFull()){
            elem = Arrays.copyOf(elem,2*elem.length);
            //usedSize新增元素的下标
            elem[usedSize] = val;
        }
        //向上调整
        siftUp(usedSize);
        usedSize++;
    }

向上调整:

向上调整的过程中只需要child和parent进行比较不需要两个child之间再进行比较,因为如果原来的parent就是比child大,只要插入的这个比parent大,那他也比另一个child大

 

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

判断是不是满了:

    public boolean isFull(){
//        if(usedSize == elem.length){
//            return true;
//        }
//        return false;
        return usedSize == elem.length;
    }

当两个元素需要交换时:

    public void swap(int i, int j){
        int tmp = elem[i];
        elem[i] = elem[j];
        elem[j] = tmp;
    }

删除元素:

 删除的时候,只需要最后一个元素和第一个元素交换,并且计数器减1,也要注意传入向下调整的usedSize的值,siftDown和usedSIze的顺序有影响

因为是把原来的第usedSize和第1个互换了,所以删除之后,除了根节点,其他的都是保持着小堆或者大堆,只需要从 p = 0向下调整就可以了

   //删除 /判断是不是空 /根节点和最后一个结点交换 /usedSize -- /向下调整
    public int pop(){
        //是不是empty
        if(empty()){
            return -1;
        }
        int oldVal = elem[0];
        //usedSize才是最后一个叶子结点元素的下标
        swap(0,usedSize-1);
        usedSize--;
        siftDown(0,usedSize);
        return oldVal;
    }

判断是不是空数组:

public boolean empty(){
        return usedSize == 0;
    }

堆排序:

复杂度O(N*logN)

建立大根堆

 public void heapSort(){
        int end = usedSize - 1;
        while(end > 0){
            swap(0,end);
            siftDown(0,end);
            end--;
        }
    }

  • 16
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值