39 篇文章 0 订阅

1.堆的实质

堆其实是二叉树按照层序遍历放在一个数组中的表示方法。

如果是堆,它有以下性质:

  1. 堆逻辑上是一棵完全二叉树
  2. 堆物理上是保存在数组中
  3. 满足任意结点的值都大于其子树中结点的值,叫做大堆,或者大根堆,或者最大堆
  4. 反之,则是小堆,或者小根堆,或者最小堆。

2.求双亲结点和左右子树的公式
已知双亲(parent)的下标,则:
左孩子(left)下标 = 2 * parent + 1;
右孩子(right)下标 = 2 * parent + 2;

已知孩子(不区分左右)(child)下标,则:
双亲(parent)下标 = (child - 1) / 2;

3.堆的向下调整

首先是小堆的调整,拿到一个结点去求它的左右子树,如果左右子树在这个数组中,就说明要进行一次调整,并且比较左右子树的值,找出左右子树中最小的,并把下标记下来。之后再把index所在处的值和最小的值进行比较,如果index所在处的值大于最小的值,把这两个数进行交换,之后index更新为之前最小值的下标,left进行一次更新;如果小于,跳出循环,调整结束。
代码如下:

 public static void shiftDownSmall(int[] array,int size,int index) {
        int left = 2 * index + 1;
        while (left < size) {
            int right = left + 1;
            int min = left;
            if (right < size) {
                if (array[right] < array[left]) {
                    min = right;
                }
            }
            if (array[index] > array[min]) {
                swap(array, index, min);
                index = min;
                left = 2 * index + 1;
            } else {
                break;
            }
        }
    }

其次是大堆的调整,和小堆的调整差不多,拿到一个结点去求它的左右子树,如果左右子树在这个数组中,就说明要进行一次调整,并且比较左右子树的值,找出左右子树中最大的,并把下标记下来。之后再把index所在处的值和最大的值进行比较,如果index所在处的值小于最大的值,把这两个数进行交换,之后index更新为之前最大值的下标,left进行一次更新;如果大于,跳出循环,调整结束。
代码如下:

public static void shiftDownBig(int[] array, int index, int size){
        int left=2*index+1;
        while(left<size){
            int right=left+1;
            int max=left;
            if(array[right]<size&&array[right]>array[left]){
                max=right;
            }
            if(array[index]<array[max]){
                swap(array,index,max);
                index=max;
                left=2*index+1;
            }else{
                break;
            }
        }
      }

4.堆的向上调整

拿调整小堆的例子来说,只要它的双亲结点还大于子树的结点,就交换位置并且更新下标。
调整大堆是一样的道理,这里就不再举例。

 public static void  shiftUpSmall(int[] arr,int i){
        while(i!=0){
            int parent=(i-1)/2;
            if(arr[parent]>arr[i]){
                swap(arr,parent,i);
                i=parent;
            }else{
                break;
            }
        }
    }

5.优先队列
(1)概念
在很多应用中,我们通常需要按照优先级情况对待处理对象进行处理,比如首先处理优先级最高的对象,然后处理次高的对象。最简单的一个例子就是,在手机上玩游戏的时候,如果有来电,那么系统应该优先处理打进来的电话。
在这种情况下,我们的数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象。
这种数据结构就是优先级队列。

(2)内部操作(用堆来实现)

- 操作-入队列

过程(以大堆为例):

  1. 首先按尾插方式放入数组
  2. 比较其和其双亲的值的大小,如果双亲的值大,则满足堆的性质,插入结束
  3. 否则,交换其和双亲位置的值,重新进行 2、3 步骤
  4. 直到根结点
public void offer(int element){
        array[size]=element;
        size++;
        Heap.shiftUpSmall(array, size - 1);
    }
  • 操作-出队列
    为了防止破坏堆的结构,删除时并不是直接将堆顶元素删除,而是用数组的最后一个元素替换堆顶元素,然后通过
    向下调整方式重新调整成堆
 public int poll(){
        int element=array[0];
        size--;
        array[0]=array[size];
        Heap.shiftDownSmall(array,size,0);
        return element;
    }

整体代码:

import java.util.PriorityQueue;

public class MyPriorQueue {
    // 不做扩容考虑
    private int[] array;
    private int size;

    MyPriorQueue() {
        array = new int[16];
        size = 0;
    }

    public void offer(int element){
        array[size]=element;
        size++;
        Heap.shiftUpSmall(array, size - 1);
    }
    public int poll(){
        int element=array[0];
        size--;
        array[0]=array[size];
        Heap.shiftDownSmall(array,size,0);
        return element;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值