堆
(一)存储
堆是一棵完全二叉树 :使用数组存储结点
结点按层序存储在数组中,
第一个结点存储在1号位,i
号位结点的左右孩子分别存储在2i
和2i+1
号位
(二)基本操作实现 (以大顶堆为例)
1、建堆 (数据提前保存在数组中,只需调整为大顶堆即可)
- 思路:由于完全二叉树的叶节点个数为
【n / 2】
(取上限),所以数组下标下1 ~ 【n / 2】
(取下限)中都是非叶节点;因此从n/2
号开始向上枚举节点,对每一个节点向下调整; - 时间复杂度:O(n) <算法导论有证>
int heap[maxn], n = 100; //堆,元素个数
//对heap[low, high]范围内进行向下调整 时间复杂度O(logn)-树高
//low和high分别为欲调整结点和最后结点的下标
void downAdjust(int low, int high) {
//向下调整
int i = low, j = i * 2;
while(j <= high) {
if(j + 1 <= high && heap[j] < heap[j + 1]) { //选择较大的子结点
j = j + 1;
}
//父节点 < 子结点
if(heap[i] < heap[j]) {
swap(heap[i], heap[j]); //换位
i = j; //继续向下调整
j = i * 2;
}
else {
break;
}
}//while
}//downAdjust
//建堆(数据已经保存在数组中,调整就行)- O(n) <算法导论有证>
void create_heap() { //
for(int i = n / 2; i >= 1; i--) //从第一个非叶节点开始
downAdjust(i, n);
}
2、删除堆顶元素
void delete_top() {
heap[1] = heap[n--]; //尾结点覆盖即可
downAdjust(1, n); //调整
}
3、插入元素
- 思路:将要插入的结点放在数组最后,然后向上调整即可
//向上调整(根节点下标为low,欲调整结点下标为high)
void upAdjust(int low, int high) {
//low设置为1,high表示欲调整结点的数组下标
int i = high, j = i / 2; //i为欲调整结点,j为其父结点
while(j >= low) {
if(heap[i] > heap[j]){
swap(heap[i], heap[j]);
i = j; //不断向上回溯即可
j = i / 2;
} else {
break;
}
}
}
void intsert(int x) {
heap[n++] = x; //先加在数组最后
upAdjust(1, n); //向上调整
}
4、堆排序
- 思路: 不断摘取堆顶元素放在数组末尾即可,最后数组按递增排序
void heapSort() {
create_heap(); //建堆
for(int i = n; i > 1; i--) { //直到堆中只剩一个元素
swap(heap[i], heap[1]);
downAdjust(1, i - 1);
}
}