(一)堆的定义
优先队列(priority queue):特殊的“队列”,却出元素的顺序是依照元素的优先权(关键字)大小,而不是元素进入队列的先后顺序。
若采用数组或者链表实现优先队列:
-
数组
插入:元素总是插入尾部 ~O(1)
删除:查找最大(最小)关键字 ~O(n)
———从数组中删除元素并移动~O(n) -
链表
插入:元素总是插入链表的头部~O(1)
删除:查找最大(最小)关键字 ~O(n)
————删去结点 ~O(1) -
有序数组
插入:找到合适的位置~O(n)或O(log2n)
————移动元素并且插入~O(n)
删除:删去最后一个元素~O(1) -
有序链表
插入:找到合适的位置~O(n)
———插入元素~O(1)
删除:删除首元素或者最后元素~O(1)
是否可以采用二叉树存储结构?
- 二叉搜索树:每次删除最大的,意味着每次都删除右子树,那么树就不平衡了。
- 如果采用二叉树结构,应该更关注插入还是删除?
树的结点顺序怎么安排? 最大的/最小的保存在树根
树结构怎么样?完全二叉树
堆的两个特性
- 结构性:用数组表示的完全二叉树
- 有序性:任意结点的关键字是其子树所有结点的最大值(或最小值)
最大堆:大堆顶
最小堆:小顶堆
最大堆和最小堆每一条路径都满足:从大到小或从小到大,且都是完全二叉树
(二)最大堆的建立
- 方法1:通过插入操作,将N个元素一个个相机插入到一个促使为空的堆中去,其时间代价最大为O(NlogN)
- 方法2:在线性时间复杂度下建立最大堆
(1)将N个元素按输入顺序存入,先满足完全二叉树的结构特性
(2)调整各结点的位置,以满足最大堆的有序特性
**建堆时,最坏情况下需要挪动元素次数是等于树中各结点的高度和。**问:对于元素个数为12的堆,其各结点的高度之和是多少?
1
2 3
4 5 6 7
8 9 10 11 12
建堆的话,主要考虑下沉的次数,以此定义高度。8,9,10,11,12都是已经沉底的,要注意的是7也是在最底了。这些节点高度都是0。
4,5,6都是最多可以沉1次的,高度为1.
2,3最多能沉2次,高度为2.
1可以沉3次,高度为3.
1*3+2*2+3*1=3+4+3=10
05-树7 堆中的路径 (25 分)
将一系列给定数字插入一个初始为空的小顶堆H[]。随后对任意给定的下标i,打印从H[i]到根结点的路径。
输入格式:
每组测试第1行包含2个正整数N和M(≤1000),分别是插入元素的个数、以及需要打印的路径条数。下一行给出区间[-10000, 10000]内的N个要被插入一个初始为空的小顶堆的整数。最后一行给出M个下标。
输出格式:
对输入中给出的每个下标i,在一行中输出从H[i]到根结点的路径上的数据。数字间以1个空格分隔,行末不得有多余空格。
输入样例:
5 3
46 23 26 24 10
5 4 3
输出样例:
24 23 10
46 23 10
26 10
题意理解:
#include <vector>
#include <stdio.h>
#include <iostream>
using namespace std;
//05-树7 堆中的路径 (25 分)
#define MAXN 1001
#define MINH -10001
int heap[MAXN];
int h_size;
//建立一个空堆
void Create()
{
h_size = 0;
heap[0] = MINH; /*设置“岗哨”*/
}
//最小堆的插入,方法是从最后一个结点开始比较
void Insert(int X)
{
//省略判断堆是否已满
int i;
for (i = ++h_size; heap[i / 2] > X; i /= 2){
heap[i] = heap