【数据结构笔记15】优先队列、堆、最大堆、堆的操作(插入、删除、建立)与C实现
本次笔记内容:
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实现相关推荐
- 【数据结构笔记11】二叉搜索树,动态查找,删除操作
本次笔记内容: 4.1.1 二叉搜索树及查找 4.1.2 二叉搜索树的插入 4.1.3 二叉搜索树的删除 文章目录 动态查找 什么是二叉搜索树(BST) 二叉树的操作 二叉搜索树的查找操作 二叉搜索树 ...
- 数据结构笔记(十三)-- 串的堆分配存储表示
串的堆分配存储表示 一.堆分配存储概述 堆分配内存仍是一组地址连续的存储单元来存放串值字符序列,但是存储空间实在程序执行过程中动态分配而得.在C语言中,存在一个称为"堆"的自由存储 ...
- python自学笔记15之实例之绘图、dataframe操作、读写csv,excle
用Python绘图,借助强大的numpy和matplotlib import numpy as np import matplotlib.pyplot as plt import pandas as ...
- 图论——Dijkstra+prim算法涉及到的优先队列(二叉堆)
[0]README 0.1)为什么有这篇文章?因为 Dijkstra算法的优先队列实现 涉及到了一种新的数据结构,即优先队列(二叉堆)的操作需要更改以适应这种新的数据结构,我们暂且吧它定义为Dista ...
- 【数据结构】堆,大根堆,小根堆,优先队列 详解
目录 堆 1.堆的数组实现 2.小根堆 3.大根堆 4.优先队列 例题 1.SP348 EXPEDI - Expedition(有趣的贪心思路,优先队列) 2.合并果子 堆 要了解堆之前,请先了解树, ...
- 2021-10-21 二叉堆 恋上数据结构笔记
文章目录 引言:Top K问题 堆 二叉堆 二叉堆的实现原理 二叉堆的添加原理 二叉堆的删除 批量建堆(自上而下的上滤与自下而上的下滤)及其效率对比 引言:Top K问题 堆 二叉堆 二叉堆的实现原理 ...
- 【LeetCode笔记】347. 前K个高频元素(Java、优先队列、小顶堆、HashMap)
文章目录 题目描述 思路 & 代码 更新版:引入 stream 流 + Lambda 题目描述 时间复杂度小于O(n*logn),否则直接sort,再遍历就很轻松. 很有学习价值的题目,第一次 ...
- 【数据结构笔记18】堆中的路径与C实现(堆元素到根的路)径)
本次笔记内容: 小白专场:堆中的路径 文章目录 题意理解 堆的表示及其操作 主程序 题意理解 将一系列给定数字插入一个初始为空的小顶堆H[].随后对任意给定的下标'i',打印从H[i]到根结点的路径. ...
- 数据结构 - 最小堆最大堆
可以在O(nlogn)的时间复杂度内完成排序 典型的用法是,寻找 第k个/前k个 最大/最小元素,k个有序序列合并 1.合并K个升序链表(最小堆实现) 或许可以改进成每次堆只存放K个元素? # Def ...
最新文章
- 数据结构:单向环形链表
- Netty中集成Protobuf实现Java对象数据传递
- Linux sem_init函数用法
- java linkedlist和arraylist添加元素时性能比较
- 第三届人本沙龙12月活动小结
- java认证框架_sa-token 一个的JavaWeb权限认证框架,强大、简单、好用
- 解决AndroidStudio更新后在 Building gradle project info 一直卡住
- udp聊天?使用udp+python实现多人聊天室
- 5G模块M.2座子管脚和封装分析
- 油猴脚本的安装、配置及使用方法
- 图灵在计算机科学方面主要贡献,图灵在计算机理论方面的贡献
- android srgb模式,一加3固件官方更新:加入屏幕边缘防误触和sRGB显示模式功能
- lucas–kanade_Lucas–Kanade
- 动态修改esxi虚拟机的CPU和内容
- 【洛谷P1903】数颜色
- Hive SQL 每日SQL
- 迅为iTOP-i.MX6ULL开发板I2C驱动程序实现 I2C通信
- 硕士卖房,到底是行业内卷,还是自我突破
- 2018年最新从PayPal提现美金的方法(送$25+1%提现费)!
- 大华监控硬盘录像机恢复程序2.0
热门文章
- hive:导出数据记录中null被替换为\n的解决方案
- eslint 验证vue文件 报错 unexpected token =解决方法
- Sqlite 数据库出现database disk image is malformed报错的解决方法
- 树莓派3B的WiFi中文乱码及搜索不到附近的WiFi_解决方案:
- 如何在隐藏视图时使用自动布局移动其他视图?
- 在Visual Studio Code中查找并​​替换为换行符
- IntelliJ检查给出“无法解析符号”但仍编译代码
- 电脑一直弹出传奇游戏网页弹窗怎么办
- mybatis jar包_Spring4+SpringMVC+MyBatis整合思路
- python增强运算符_Python学习【第3篇】:Python之运算符