堆和优先队列总结

概念

  1. 堆逻辑上是一棵完全二叉树
  2. 堆物理上是保存在数组中
  3. 满足任意结点的值都大于其子树中结点的值,叫做大堆,或者大根堆,或者最大堆
  4. 反之则是小堆,或者小根堆,或者最小堆
    向下调整
    前提:左右子树必须是一个堆才能调整
    过程(以小堆为例)
    1.index如果已经是叶子节点,则整个调整过程结束
    ①判断index位置有没有孩子
    ②因为堆是完全二叉树,没有左孩子就一定没有右孩子,所以判断是否有左孩子
    ③因为堆的存储结构是数组,所以判断是否有左孩子即判断左孩子下标是否越界,即 left >= size 越界
    2.确定 left 或 right,谁是 index 的最小孩子 min
    ①如果右孩子不存在,则 min = left
    ② 否则,比较 array[left] 和 array[right] 值得大小,选择小的为 min
    3.比较 array[index] 的值 和 array[min] 的值,如果 array[index] <= array[min],则满足堆的性质,调整结束
    4.否则,交换 array[index] 和 array[min] 的值
    5.然后因为 min 位置的堆的性质可能被破坏,所以把 min 视作 index,向下重复以上过程
import java.util.Arrays;
//以小堆为例  向下调整
public class ShiftDown {
    public static void shiftDown(int[] array,int size,int index){
        int left=2*index+1;
        while(left<size){
            int mid=left;
            int right=2*index+2;
            if(right<size){
                if(array[right]<array[left]){
                    mid=right;
                }
            }
            if(array[index]<=array[mid]){
                break;
            }
            int t=array[index];
            array[index]=array[mid];
            array[mid]=t;
            index=mid;
            left=2*index+1;
        }
    }
    public static void createHeap(int[] array,int size){
        for(int i=(size-1-1)/2;i>=0;i--){
            shiftDown(array,size,i);
        }
    }
    public static void main(String[] args) {
        int[] array = { 27,15,19,18,28,34,65,49,25,37 };
        createHeap(array,array.length);
        System.out.println(Arrays.toString(array));
    }
}

import java.util.Arrays;
//以大堆为例,向下调整
public class shiftDownBig {
    public static void shiftDown(int[] array,int size,int index){
        int parent=index;
        int childer=2*parent+1;
        while(childer<size){
            if(childer+1<size&&array[childer+1]>array[childer]){
                childer=childer+1;
            }
            if(array[childer]>array[parent]){
                int temp=array[childer];
                array[childer]=array[parent];
                array[parent]=temp;
            }else{
                break;
            }
            parent=childer;
            childer=2*parent+1;
        }
    }
    public static void createHeap(int[] array,int size){
        for(int i=(size-1-1)/2;i>=0;i--){
            shiftDown(array,size,i);
        }
    }

    public static void main(String[] args) {
        int[] array = { 27,15,19,18,28,34,65,49,25,37 };
        createHeap(array,array.length);
        System.out.println(Arrays.toString(array));
    }
}

优先队列

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

  1. 首先按尾插方式放入数组
  2. 比较其和其双亲的值的大小,如果双亲的值大,则满足堆的性质,插入结束
  3. 否则,交换其和双亲位置的值,重新进行 2、3 步骤
  4. 直到根结点
public class MyPriorityQueue {
    // array 看起来是一个数组, 其实应该是一个堆的结构
    private int[] array = new int[100];
    private int size = 0;

    public void offer(int x) {
        array[size] = x;
        size++;
        // 把新加入的元素进行向上调整
        shiftUp(array, size - 1);
    }

    // 此时发现 size 这个参数没用上. 仔细想想, 好像也确实不需要.
    // 判定调整完了, 只需要和 0 比较即可. 不需要知道整个堆有多大.
    private static void shiftUp(int[] array, int index) {
        int child = index;
        int parent = (child - 1) / 2;
        while (child > 0) {
            if (array[parent] < array[child]) {
                // 当前不符合大堆要求.
                int tmp = array[parent];
                array[parent] = array[child];
                array[child] = tmp;
            } else {
                // 发现当前 父节点 比子节点大. 这个时候说明整个数组已经符合堆的结构了
                break;
            }
            child = parent;
            parent = (child - 1) / 2;
        }
    }

    public int poll() {
        // 下标为 0 的元素就是队首元素. 删掉的同时, 我们也希望剩下的结构仍然是一个堆
        int oldValue = array[0];
        array[0] = array[size - 1];
        size--;
        shiftDown(array, size, 0);
        return oldValue;
    }

    private static void shiftDown(int[] array, int size, int index) {
        int parent = index;
        int child = 2 * parent + 1;
        // 这个条件的含义是看看 parent 有没有子节点
        while (child < size) {
            // 把左右子树中较大的节点找到
            if (child + 1 < size && array[child + 1] > array[child]) {
                child = child + 1;
            }
            // 上述条件结束后, child 肯定对应左右子树中比较大的元素.
            // 再拿当前的这个 child 和 parent 位置的元素比较一下
            if (array[child] > array[parent]) {
                // 交换父子节点的位置
                int tmp = array[child];
                array[child] = array[parent];
                array[parent] = tmp;
            } else {
                // 当前这个位置开始, 已经符合堆的要求了, 不需要继续调整
                break;
            }
            parent = child;
            child = 2 * parent + 1;
        }
    }

    public int peek() {
        return array[0];
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public static void main(String[] args) {
        MyPriorityQueue queue = new MyPriorityQueue();
        queue.offer(9);
        queue.offer(5);
        queue.offer(2);
        queue.offer(7);
        queue.offer(3);
        queue.offer(6);
        queue.offer(8);

        while (!queue.isEmpty()) {
            int cur = queue.poll();
            System.out.println(cur);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值