堆
时间:2019-03-20
本文章向大家介绍堆,主要包括堆使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
1. 定义
堆由两条性质来定义。
-
首先,它是一棵完全二叉树:从根节点起每一层从左到右填充。一棵高度为 的完全二叉树除了第 层外,每一层都是满的;且底层节点集中在左边的若干位置上。
-
其次,堆中存储的数据是局部有序的。即,节点存储的值与子节点存储的值之间存在某种关系。根据这种关系,堆可分为以下两种:
- 最大堆:任意一个节点存储的值都大于等于其任意一个子节点存储的值。
- 最小堆:任意一个节点存储的值都小于等于其任意一个子节点存储的值。
无论最大堆还是最小堆,任何一个节点与其兄弟节点之间都没有必然联系!
2. 使用数组实现完全二叉树
- 假设在完全二叉树中,逐层而下、从左到右,给每一个节点编号(从0开始)。
- 把每一个节点按照其节点序号放在一个数组中。
- 假设 为节点的总数, 为节点序号,则有如下关系:
- 的父节点序号:,当 时(取下整)
- 的左子节点序号:,当 时
- 的右子节点序号:,当 时
3. 建堆
-
方法:自底向上构建堆。
-
具体过程:从最后的内部节点开始,到根为止,该算法检查这些节点存储的值 是否满足父母优势(即堆的第二条性质)。如果该节点不满足父母优势要求,算法把节点存储的值 和它子女中存储的最大的值进行交换,然后再次检查在新位置上, 是否满足父母优势要求。这个过程一直持续到对 的父母优势要求满足为止。
对于以当前内部节点为根的子树,在完成它的“堆化”以后,算法对于该节点的直接前趋进行同样的操作。在对树的根完成这种操作以后,算法停止。
4. 堆排序
- 思想:每次删除堆中最大的值,即从大到小依次取出堆中的元素。
- 具体过程
- 根的值和堆的最后一个节点的值 做交换;
- 堆的规模减1;
- 再一次构建堆,即从根开始,严格按照我们自底向上构建堆的做法,把 沿着树向下筛选,来对这颗较小的树进行“堆化”;
- 重复1~3步,直至取出所有的节点。
5. C++ 实现
// 为节点pos找到一个合适的容身之所,使以pos节点为根的二叉树满足堆的要求,前提是pos的左右子树皆已是堆
void shiftdown(int heap[], size_t n, size_t pos) {
int node_cpy = heap[pos];
size_t cur_pos = pos;
size_t maximum = 2 * cur_pos + 1; // 左子节点(如果存在的话),且设左子节点存储的值比右子节点存储的值大
size_t right = maximum + 1; // 右子节点(如果存在的话)
// 左子节点存在,即当前节点还是内部节点
while(maximum < n) {
// 右子节点也存在,此时需要找出左右子节点中较大者
if(right < n) {
maximum = heap[right] > heap[maximum] ? right : maximum;
}
// 小于左右子节点中的最大值
if(node_cpy < heap[maximum]) {
heap[cur_pos] = heap[maximum];
cur_pos = maximum;
maximum = 2 * cur_pos + 1;
right = maximum + 1;
}
else {
break;
}
}
heap[cur_pos] = node_cpy;
}
// 建堆过程
void build_heap(int A[], size_t n) {
// 从最后一个内部节点开始
for(int i=n/2-1; i>=0; --i) {
shiftdown(A, n, i);
}
}
// 堆排序
void heap_sort(int A[], size_t n) {
build_heap(A, n);
size_t size = n;
for(size_t i=0; i<n; ++i) {
shiftdown(A, size, 0);
std::swap(A[0], A[--size]);
}
}
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法