堆和优先级队列

本文深入探讨了堆的顺序存储方式、堆的构建与调整方法,包括大根堆和小根堆的概念。此外,还介绍了优先级队列的实现,它利用堆的特性快速找到集合中的最值。通过向下调整和向上调整操作,可以保持堆的性质。文章还展示了如何使用堆来解决TopK问题,并提供了Java代码示例。最后,讨论了优先级队列在实际问题中的应用。
摘要由CSDN通过智能技术生成

堆和优先级队列

一.二叉树的顺序存储方式

1.存储方式

在这里插入图片描述

2.下标关系

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

二.堆

1.概念

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

2.向下调整

<1>思路

前提:左右子树必须已经是一个堆,才能调整。
注:

  1. array 代表存储堆的数组
  2. size 代表数组中被视为堆数据的个数
  3. index 代表要调整位置的下标

思路:
在这里插入图片描述
注意的小问题:
1.判断index位置是不是叶子
<1>. 判断 index 位置有没有孩子
<2> 因为堆是完全二叉树,没有左孩子就一定没有右孩子,所以判断是否有左孩子
<3> 因为堆的存储结构是数组,所以判断是否有左孩子即判断左孩子下标是否越界,即 left >= size 越界

2.如何找最小的孩子
<1> 如果右孩子不存在,则 min = left
<2> 否则,比较 array[left] 和 array[right] 值得大小,选择小的为 min

<2>代码示例

//向下调整
    public static void AdjustDown(Integer []array,int size,int index ){
        while (true) {
            int leftindex=2*index+1;
            //index是叶子结点,直接return
            if(leftindex>=size){
                return;
            }
            //找最小的孩子
            int minIndex=leftindex;
            int rightIndex=leftindex+1;
            //存在右孩子,且右孩子的值比左孩子的小,将最小孩子小标改为右孩子
            if(rightIndex<size&&array[rightIndex]<array[leftindex]){
                minIndex=rightIndex;
            }
            //3.比较最小孩子的值和index位置的值
            if(array[index]<=array[minIndex]){
                return;
            }
            //4.交换
            int t=array[index];
            array[index]=array[minIndex];
            array[minIndex]=t;

            //5.把最小的孩子视为index,继续循环
            index=minIndex;
        }
    }

3.向上调整

<1>思路

1.判断index是不是树的根,如果是根调整结束
2.找到index的父节点
3.比较父节点的值和index的值
4.只要父节点的值<=index的值,调整结束
5.否则交换父节点和index的值
6.把父节点看作index,继续循环

<2>代码示例

 //向上调整
    public static void AdjustUp(Integer []array,int size,int index){
        //1.判断index是不是树的根,如果是根调整结束
        //2.找到index的父节点
        //3.比较父节点的值和index的值
        //4.只要父节点的值<=index的值,调整结束
        //5.否则交换父节点和index的值
        //6.把父节点看作index,继续循环
        while (true) {
            if(index==0){
                break;
            }

            //找到index父节点的下标
            int ParentIndex=(index-1)/2;
            if(array[index]<array[ParentIndex]){
                int t=array[index];
                array[index]=array[ParentIndex];
                array[ParentIndex]=t;
            }else {
                break;
            }
            index=ParentIndex;
        }
    }

3.建堆

<1>思路

1.找到层序遍历最后一个结点的下标
2.找到最后一个结点的父节点下标(最后一个不是叶子节点的下标),记位ParentIndex
3.从[lastParentIndex,0]不断地向下调整(从后往前)

<2>代码示例

 //最坏时间复杂度是O(log(n))
    //建堆
    public static void BuildHeap(Integer array[],int size){
   //找到层序遍历最后一个结点的下标
        int lastIndex=size-1;
        //找到最后一个结点的父节点下标(最后一个不是叶子节点的下标)
        int lastParentIndex=(lastIndex-1)/2;//下标是从0开始
        //从[lastParentIndex,0]不断地向下调整(从后往前)
        for (int i=lastParentIndex;i>=0;i--){
            AdjustDown(array, size, i);
        }
    }

三.优先级队列

1.概念

返回最高优先级对象,添加新的对象。这种数据结构就是优先级队列(Priority Queue)

2.优先级队列的实现

使用堆来构建
代码示例:返回队首元素,入队,出队
需要使用上述提到的:堆的向下和向上调整

public class MyPriorityQueue {
    private Integer[] array;
    private int size;

    public MyPriorityQueue() {
        //简单起见,不考虑扩容
        array=new Integer[100];
        size=0;
    }
   //返回队首元素
    public Integer element(){
        if(size==0){
            throw new RuntimeException("空了" );
        }
        return array[0];
    }

    //返回并删除队首元素
    public Integer remove(){
        if(size==0){
            throw new RuntimeException("空了" );
        }
        int e=array[0];
        //将堆的最后一个元素拿到第一位(堆首),此时满足向下调整的条件
        array[0]=array[size-1];

        //将堆首(0号下标)进行向下调整
        Heap.AdjustDown(array, size, 0);
        return e;
    }
    //插入一个元素
    //时间复杂度O(log(n))
    public void add(Integer e){
       array[size]=e;
       size++;
       Heap.AdjustUp(array, size, size-1);
    }
}

四.TopK问题

举例(Top5问题)
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Serendipity sn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值