本次笔记内容:
5.1.1 什么是堆
5.1.2 堆的插入
5.1.3 堆的删除
5.1.4 堆的建立

文章目录

  • 什么是堆(Heap)
    • 优先队列(Priority Queue)
    • 是否可用二叉树实现?
    • 堆得两个特性
    • 堆的抽象数据类型描述
    • 堆的实现(以最大堆为例)
    • 最大堆的创建
    • 最大堆的插入
    • 最大堆的删除
    • 最大堆的建立

什么是堆(Heap)

优先队列(Priority Queue)

特殊的“队列”,取出元素的顺序是依照元素的优先权(关键字)大小,而不是元素进入队列的先后顺序。

如果采用数组、链表、有序数组或有序链表实现优先队列:

  • 数组:
    • 插入:元素总是插入尾部~o(1)
    • 删除:查找最大(或最小)关键字~o(n);从数组中删去需要移动元素~o(n)
  • 链表:
    • 插入:元素总是插入链表的头部~o(1)
    • 删除:查找最大(或最小)关键字~o(n);删去结点~o(1)
  • 有序数组:
    • 插入:找到合适的位置~o(n)或o(log2(n));移动元素并插入~o(n)
    • 删去最后一个元素~o(1)
  • 有序链表:
    • 插入:找到合适的位置~o(n);~插入元素
    • 删除:删除首元素或最后元素~o(1)

是否可用二叉树实现?

使用二叉搜索树可行么?

要删除(取出)的点(最大、最小),要么在最右边,要么最左边。但是存在一个问题:不断地删除行为使二叉树倾斜。

因此,不采用二叉搜索树。但如果继续采用二叉树结果,应该更关注插入还是删除

  • 删除,因为删除更难做。
  • 树结点顺序怎么安排?
  • 树结构怎样?

把数据放在二叉树中,组织成:最大的在树根(最大堆),使用完全二叉树。

因此堆的特性:使用完全二叉树存储,任何结点值都比其子树大(小)。

优先队列的完全二叉树表示:

堆得两个特性

  • 结构性:用数组表示的完全二叉树;
  • 有序性:任一结点的关键字是其子树所有结点的最大值(或最小值)
    • “最大堆(MaxHeap)”,也称“大顶堆”:最大值
    • “最小堆(MinHeap)”,也称“小顶堆”:最小值

堆的例子如上。

堆的抽象数据类型描述

如上图,是堆的对象集、操作集描述。

其中,Insert()和DeleteMax()是难点。

堆的实现(以最大堆为例)

最大堆的创建

typedef struct HeapStruct *MaxHeap;
struct HeapStruct
{ElementType *Elements; /* 存储堆元素的数组 */int Size;              /* 堆的当前元素个数 */int Capacity;          /* 堆的最大容量 */
};MaxHeap Create(int MaxSize)
{ /* 创建容量为MaxSize的空的最大堆 */MaxHeap H = malloc(sizeof(struct HeapStruct));H->Elements = malloc((MaxSize + 1) * sizeof(ElementType));H->Size = 0;H->Capacity = MaxSize;H->Elements[0] = MaxData;/* 定义“哨兵”为大于堆中所有可能元素的值,便于以后更快操作 */return H;
}

最大堆的插入

如上例图,算法为将新增结点插入到从其父结点到根结点的有序序列中。

void Insert(MaxHeap H, ElementType item)
{/* 将元素item插入最大堆H,其中H->Elements[0]已经定义为哨兵 */int i;if (IsFull(H)){printf("最大堆已满");return;}i = ++H->Size; /* i指向插入后堆中的最后一个元素的位置 */for (; H->Elements[i / 2] < item; i /= 2)H->Elements[i] = H->Elements[i / 2]; /* 向下过滤结点 */H->Elements[i] = item;                   /* 将item插入 */
}

上述代码中应用到了如下技巧:

  • H->Element[0]是哨兵元素,它不小于堆中的最大元素,控制循环结束;
  • 如此,甚至不用添加约束条件i>=1。

插入操作的时间复杂度是树的高度:

T=log2(N)T=log_2(N)T=log2​(N)

最大堆的删除

对于堆来讲,删除的元素的是固定的。

取出根结点(最大值)元素,即删除堆的那个结点。

T=log2(N)T=log_2(N)T=log2​(N)

经过多次比较后(最左右孩子相比),保证根为最大值,最终结果如下。

实现代码如下:

ElementType DeleteMax(MaxHeap H)
{ /* 从最大堆H中取出键值为最大的元素,并删除 */int Parent, Child;ElementType MaxItem, temp;if (IsEmpty(H)){printf("最大堆已为空");return;}MaxItem = H->Elements[1]; /* 取出根结点的最大值 *//* 用最大堆中最后一个元素从根结点开始向上过滤下层结点 */temp = H->Elements[H->Size--];for (Parent = 1; Parent * 2 <= H->Size; Parent = Child){Child = Parent * 2;if ((Child != H->Size) &&(H->Elements[Child] < H->Elements[Child + 1]))Child++; /* Child指向左右子结点的较大者 */if (temp >= H->Elements[Child])break;else /* 移动temp元素到下一层 */H->Elements[Parent] = H->Elements[Child];}H->Elements[Parent] = temp;return MaxItem;
}

最大堆的建立

将已经存在的N个元素按最大堆的要求存放在一个一维数组中。

方法1:通过插入操作,将N个元素一个个相继插入到一个初始为空的堆中去,其时间代价最大为O(NlogN)。

方法2:在线性时间复杂度下建立最大堆。
(1)将N个元素按输入顺序存入,先满足完全二叉树的结构特性;
(2)调整各结点位置,以满足有序特性。

采用方法2。

从倒数第一个有儿子的结点开始,用Delete()过程的策略,将这个小子树调成堆。依此往上调成堆。

起始点如上图。

然后是30那个点,然后是83那个点。

之所以这么做,是因为使用向下过滤法,必须保证根结点的两个子树都是堆。而对于最小的子树(87-9),9结点单独成树,一定是一个堆,逐渐往上调整。

时间复杂度:O(n)O(n)O(n),即树中各结点的高度和。

代码来自Feemic:

void PercDown(MaxHeap H, int p)
{ /* 下滤:将H中以H->Element[p]为根的子堆调整为最大堆 */int Parent, Child;ElementType X;X = H->Elements[p]; /* 取出根结点存放的值 */for (Parent = p; Parent * 2 <= H->Size; Parent = Child){Child = Parent * 2;if ((Child != H->Size) && (H->Elements[Child] < H->Elements[Child + 1]))Child++; /* Child指向左右子结点的较大者 */if (X >= H->Elements[Child])break; /* 找到了合适位置 */else       /* 下滤X */H->Elements[Parent] = H->Elements[Child];}H->Elements[Parent] = X;
}void BuildHeap(MaxHeap H)
{   /* 调整H->Elements[]中的元素,使满足最大堆的有序性  *//* 这里假设所有H->Size个元素已经存在H->Elements[]中 */int i;/* 从最后一个结点的父节点开始,到根结点1 */for (i = H->Size / 2; i > 0; i--)PercDown(H, i);
}

【数据结构笔记15】优先队列、堆、最大堆、堆的操作(插入、删除、建立)与C实现相关推荐

  1. 【数据结构笔记11】二叉搜索树,动态查找,删除操作

    本次笔记内容: 4.1.1 二叉搜索树及查找 4.1.2 二叉搜索树的插入 4.1.3 二叉搜索树的删除 文章目录 动态查找 什么是二叉搜索树(BST) 二叉树的操作 二叉搜索树的查找操作 二叉搜索树 ...

  2. 数据结构笔记(十三)-- 串的堆分配存储表示

    串的堆分配存储表示 一.堆分配存储概述 堆分配内存仍是一组地址连续的存储单元来存放串值字符序列,但是存储空间实在程序执行过程中动态分配而得.在C语言中,存在一个称为"堆"的自由存储 ...

  3. python自学笔记15之实例之绘图、dataframe操作、读写csv,excle

    用Python绘图,借助强大的numpy和matplotlib import numpy as np import matplotlib.pyplot as plt import pandas as ...

  4. 图论——Dijkstra+prim算法涉及到的优先队列(二叉堆)

    [0]README 0.1)为什么有这篇文章?因为 Dijkstra算法的优先队列实现 涉及到了一种新的数据结构,即优先队列(二叉堆)的操作需要更改以适应这种新的数据结构,我们暂且吧它定义为Dista ...

  5. 【数据结构】堆,大根堆,小根堆,优先队列 详解

    目录 堆 1.堆的数组实现 2.小根堆 3.大根堆 4.优先队列 例题 1.SP348 EXPEDI - Expedition(有趣的贪心思路,优先队列) 2.合并果子 堆 要了解堆之前,请先了解树, ...

  6. 2021-10-21 二叉堆 恋上数据结构笔记

    文章目录 引言:Top K问题 堆 二叉堆 二叉堆的实现原理 二叉堆的添加原理 二叉堆的删除 批量建堆(自上而下的上滤与自下而上的下滤)及其效率对比 引言:Top K问题 堆 二叉堆 二叉堆的实现原理 ...

  7. 【LeetCode笔记】347. 前K个高频元素(Java、优先队列、小顶堆、HashMap)

    文章目录 题目描述 思路 & 代码 更新版:引入 stream 流 + Lambda 题目描述 时间复杂度小于O(n*logn),否则直接sort,再遍历就很轻松. 很有学习价值的题目,第一次 ...

  8. 【数据结构笔记18】堆中的路径与C实现(堆元素到根的路)径)

    本次笔记内容: 小白专场:堆中的路径 文章目录 题意理解 堆的表示及其操作 主程序 题意理解 将一系列给定数字插入一个初始为空的小顶堆H[].随后对任意给定的下标'i',打印从H[i]到根结点的路径. ...

  9. 数据结构 - 最小堆最大堆

    可以在O(nlogn)的时间复杂度内完成排序 典型的用法是,寻找 第k个/前k个 最大/最小元素,k个有序序列合并 1.合并K个升序链表(最小堆实现) 或许可以改进成每次堆只存放K个元素? # Def ...

最新文章

  1. 数据结构:单向环形链表
  2. Netty中集成Protobuf实现Java对象数据传递
  3. Linux sem_init函数用法
  4. java linkedlist和arraylist添加元素时性能比较
  5. 第三届人本沙龙12月活动小结
  6. java认证框架_sa-token 一个的JavaWeb权限认证框架,强大、简单、好用
  7. 解决AndroidStudio更新后在 Building gradle project info 一直卡住
  8. udp聊天?使用udp+python实现多人聊天室
  9. 5G模块M.2座子管脚和封装分析
  10. 油猴脚本的安装、配置及使用方法
  11. 图灵在计算机科学方面主要贡献,图灵在计算机理论方面的贡献
  12. android srgb模式,一加3固件官方更新:加入屏幕边缘防误触和sRGB显示模式功能
  13. lucas–kanade_Lucas–Kanade
  14. 动态修改esxi虚拟机的CPU和内容
  15. 【洛谷P1903】数颜色
  16. Hive SQL 每日SQL
  17. 迅为iTOP-i.MX6ULL开发板I2C驱动程序实现 I2C通信
  18. 硕士卖房,到底是行业内卷,还是自我突破
  19. 2018年最新从PayPal提现美金的方法(送$25+1%提现费)!
  20. 大华监控硬盘录像机恢复程序2.0

热门文章

  1. hive:导出数据记录中null被替换为\n的解决方案
  2. eslint 验证vue文件 报错 unexpected token =解决方法
  3. Sqlite 数据库出现database disk image is malformed报错的解决方法
  4. 树莓派3B的WiFi中文乱码及搜索不到附近的WiFi_解决方案:
  5. 如何在隐藏视图时使用自动布局移动其他视图?
  6. 在Visual Studio Code中查找并​​替换为换行符
  7. IntelliJ检查给出“无法解析符号”但仍编译代码
  8. 电脑一直弹出传奇游戏网页弹窗怎么办
  9. mybatis jar包_Spring4+SpringMVC+MyBatis整合思路
  10. python增强运算符_Python学习【第3篇】:Python之运算符