【数据结构】堆

文章目录

优先级队列

在优先级队列中存储的元素具有优先级,可以返回优先级较高的元素。
逻辑结构采用的是堆(完全二叉树),存储结构是用了数组(将二叉树层序遍历存储)

几点说明

  1. 设父节点的下标为i,那么左孩子的下标为2i+1,右孩子的下标为2i+2

小根堆

大根堆

建堆算法

向下调整算法 & 初始化堆

从最后一个根节点(也就是倒数第二层)开始向上进行建堆,对每一棵子树都进行大(小)根堆的调整。

  1. 从两个(一个)子树中找到比根节点大(小)的结点,然后进行交换
  2. 父节点向上走(令parent = child),子节点也往上走(child = 2*parent+1)

需要注意的是:

① 不是每个结点都是有左(右)子树的,需要进行判断,防止越界

② 调整一直要进行到数组的末尾(child < array.length)

代码:

java 复制代码
public PriorityQueue(int[] array) {
        this.array = new int[11];
        System.arraycopy(array,0,this.array,0,array.length);
        usedSize = array.length;
        // 只要根节点没有到顶,那就一直向上进行调整
        //(宏观向上寻找根节点,微观从每个根节点向下进行调整)
        for (int root = (array.length-1-1)/2; root >= 0; root--) {
            shiftDown(root, usedSize);
        }
    }
    
public void shiftDown(int parent, int usedSize) {
        // 左孩子
        int child = 2 * parent + 1;
        // 因为是向下调整,所以找到头上的根节点后,一直向下走,
        // 直到不能再走(child < usedSize)
        while (child < usedSize) {
            // 如果右孩子的值更大,那就应该将child变为右孩子的下标,同时保证这个结点有右孩子
            if ((child + 1 < usedSize) && (array[child + 1] > array[child])) {
                child = 2 * parent + 2;
            }

            // 与根节点进行比较,如果孩子大,那就交换
            if (array[child] > array[parent]) {
                swap(child, parent);
                // 孩子和父亲都向下移动进行调整
                parent = child;
                child = 2 * parent + 1;
            } else {
                // 只要有一个结点不用调整,那就不需要继续往下走了,因为下面的之前已经调整过了
                break;
            }
        }

向上调整算法 & 插入

在插入的操作中,向上调整只需要进行一次,因为除了刚刚插进来的最后一个元素与其他的结点不匹配,其他的结点都已经在各自的树中形成自己的大根堆,所以只需要从最后一个结点出发向上调整,直到这个叶子结点变为新的根节点(最大的根节点或者是某一层的新根节点再不能向上走)。

java 复制代码
public void offer(int val) {
        // 先判断是否已满
        if (isFull() == true) {
            // 已满,扩容
            array = Arrays.copyOf(array, 2*array.length);
        }

        // 插到数组的最后一个位置
        array[usedSize++] = val;
        // 使用向上调整进行重新分配
        int child = usedSize-1;
        shiftUp(child);
    }
    
private void shiftUp(int child) {
        int parent = (child - 1) / 2;
        // 只要这个插入的孩子节点没到根部,
        // 那就一直进行调整,或者到了某一层不用调整了
        while (child >= 0) {
            if (array[child] > array[parent]) {
                swap(child, parent);
                child = parent;
                parent = (child - 1) / 2;
            } else {
                break;
            }
        }
    }

删除

删除的就是堆头元素,最大(小)元素。

具体做法:

将堆头元素与末尾元素交换位置,随后对根节点所在的树进行向下调整。

  • 问:为什么只用从根节点开始进行向下调整?
  • 因为在之前的初始化过程中,所有树都已经是大(小)根堆,只有刚才进行交换的过程中,改变了根节点的数据使得这棵树不为大(小)根堆,所以只需要调整这棵树。

代码:

java 复制代码
public void poll() {
        // 删除最大的元素,先将堆头的元素与最后一个进行交换,然后再进行向下调整
        swap(0, usedSize-1);
        usedSize--;

        // 只用调整根节点所在的那一棵树,因为其他的树在之前已经调整过了
        shiftDown(0,usedSize);
    }

时间复杂度

向下调整算法时间复杂度:O(n)

算最坏的情况(每次调整都需要从根一直调整到倒数第二层):

所以 每一层所需的次数就是 结点数 * (高度-1)
(结点数为2^h-1^,高度为log~2~(n+1) )

之后利用错位相减法求得O(n)

向上调整算法时间复杂度:O(n*log~2~n)

算最坏的情况(每次调整都需要从叶子一直调整到根):

所以 每一层所需的次数就是 结点数 * 层数
(结点数为2^h-1^,高度为log~2~(n+1) ,高度也即层数)

之后利用错位相减法求得O(n)

手稿:

相关推荐
Monodye3 分钟前
【Java】网络编程:TCP_IP协议详解(IP协议数据报文及如何解决IPv4不够的状况)
java·网络·数据结构·算法·系统架构
pzx_0018 分钟前
【内积】内积计算公式及物理意义
数据结构·python·opencv·算法·线性回归
重生之我要进大厂4 小时前
LeetCode 876
java·开发语言·数据结构·算法·leetcode
Happy鱿鱼5 小时前
C语言-数据结构 有向图拓扑排序TopologicalSort(邻接表存储)
c语言·开发语言·数据结构
KBDYD10105 小时前
C语言--结构体变量和数组的定义、初始化、赋值
c语言·开发语言·数据结构·算法
Crossoads5 小时前
【数据结构】排序算法---桶排序
c语言·开发语言·数据结构·算法·排序算法
QXH2000005 小时前
数据结构—单链表
c语言·开发语言·数据结构
imaima6665 小时前
数据结构----栈和队列
开发语言·数据结构
David猪大卫6 小时前
数据结构修炼——顺序表和链表的区别与联系
c语言·数据结构·学习·算法·leetcode·链表·蓝桥杯
Iceberg_wWzZ6 小时前
数据结构(Day14)
linux·c语言·数据结构·算法