优先级队列(堆)总结

基础概念

  1. 堆逻辑上是一棵完全二叉树,将二叉树用层序遍历保存在数组中。
  2. 满足任意结点的值都大于其子树中结点的值,叫做大堆,或者大根堆,或者最大堆
  3. 满足任意结点的值都小于其子树中结点的值,则是小堆,或者小根堆,或者最小堆
    在这里插入图片描述
  4. 堆的基本作用是,快速找集合中的最值
下标关系

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

向下调整成小堆

根据下标找到index的左孩子,堆如果没有左孩子一定没有右孩子,右孩子等于左孩子下标+1,如果超过size,说明没有右孩子。
找出左右孩子中的最小的一个,然后和array[index]比较,比array[index]小就交换,index就变为了min,左孩子下标=2*index+1也发生了改变,然后继续while循环,直到左孩子不存在也就是left>=size的时候。如果比array[index]大,则不用交换

 private static void swap(int[] array, int index, int min) {
        int k=array[index];
        array[index]=array[min];
        array[min]=k;
    }
 private static void shiftDownSmall(int[]array, int index, int size) {
       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;
           }
       }
    }

在这里插入图片描述

向下调整成大堆

和向下调整成小堆是同理的。只不过比array[index]大才交换。

private 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(right<size){
                if(array[right]>array[left])
                    max=right;
            }
            if(array[index]<array[max]){
                swap(array,index,max);
                index=max;
                left=2*index+1;
            }else{
                break;
            }
        }
    }
建小堆

建堆首先要从倒数第一个非叶子结点开始向下调整,最后一个叶子结点下标为size-1,它的父母结点为(size-2)/2(即倒数第一个非叶子结点)。

 public static void createHeapSmall(int[] a, int s) {
        for(int i=(s-2)/2;i>=0;i--){
            shiftDownSmall(a,i,s);
        }
    }
建大堆

和建小堆同理哦。

public static void createHeapBig(int[] a, int s) {
        for(int i=(s-2)/2;i>=0;i--){
            shiftDownBig(a, i, s);
        }
    }

在这里插入图片描述

入队列
  1. 首先按尾插方式放入数组2. 比较其和其双亲的值的大小,如果双亲的值小,则满足堆的性质,插入结束。3.否则,交换其和双亲位置的值,重新进行 2、3 步骤4. 直到根结点
public class MyPriorityQueue {
    // 不做扩容考虑
    private int[] array;
    private int size;

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

    public void offer(int element) {
        array[size++] = element;
        shiftUpSmall(array, size - 1);
    }
}
public static void shiftUpSmall(int[] array, int i){
        // 直到 i == 0 之前,一直
        // 先找到 i 的双亲的下标
        // 比较 array[parent] 和 array[i]
        // 如果满足条件,调整结束
        // 否则,交换,然后 让 i = parent 继续
       while(i!=0){
           int p=(i-1)/2;
           if(array[p]<=array[i]){
               break;
           }
           swap(array,p,i);
           i=p;
       }
    }
出队列

为了防止破坏堆的结构,删除时并不是直接将堆顶元素删除,而是用数组的最后一个元素替换堆顶元素,然后通过向下调整方式重新调整成堆。

public int poll() {
        int element = array[0];
        array[0] = array[--size];
        Heap.shiftDownSmall(array, 0, size);
        return element;
    }
//返回队首元素
    public int peek() {
        // 不做错误处理
        return array[0];
    }

堆排序

升序建大堆,将第一个结点(max)与堆中最后一个叶子结点交换,然后将此叶子结点之前的结点重新进行向下调整成大堆,直到i=size-1。

/堆排序(升序用大堆,降序用小堆)
    public static void heapSort(int[] array){
        createHeapBig(array,array.length);
        for(int i=0;i<array.length-1;i++){
            // 无序 [0, array.length - i)
            // 有序 [array.length - i, array.length)
            swap(array, 0, array.length - i - 1);
            // 无序 [0, array.length - i - 1)
            // 长度 array.length - i - 1
            shiftDownBig(array,0,array.length-i-1);
        }
    }

降序建小堆,同理哒。

import java.util.Arrays;

public class Solution1 {
    //堆排序
    public static void HeapSort(int array[]){
        creatDownSmall(array,array.length);
        for(int i=0;i<array.length-1;i++){
            swap(array,0,array.length-i-1);
            shiftDownSmall(array,0,array.length-i-1);
        }
    }

    private static void shiftDownSmall(int[] array, int index, int size) {
        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;
            }
        }
    }

    private static void swap(int[] array, int i, int i1) {
        int k=array[i];
        array[i]=array[i1];
        array[i1]=k;
    }

    private static void creatDownSmall(int[] array, int size) {
        for(int i=(size-2)/2;i>=0;i--){
            shiftDownSmall(array,i,size);
        }
    }

    public static void main(String[] args) {
        int[] a = { 9,5,2,7,3,4,5,2,6,8 };
        HeapSort(a);
        System.out.println(Arrays.toString(a));
    }
}

TOP k问题

在海量数据中,寻找前k大的数。考虑到快排,但是不能解决海量问题,所以只能用堆。建含有k个元素的小堆,用于存储当前最大的k个元素,接着,从k+1个元素开始扫描,和堆顶元素进行比较,如果被扫描的元素大于堆顶,则替换堆顶元素,并调整堆。时间复杂度为O(n*log k),空间复杂度为O (k)。
寻找前k小的数,建大堆。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值