堆定义
堆,是通常是一个可以被看做一棵完全二叉树的数组对象。
堆特性
- 堆中某个节点的值总是不大于或不小于其父节点的值;
- 堆总是一棵完全二叉树。
完全二叉树数组表示时的特性
完全二叉树可以使用数组表示,树的节点层次遍历存放到数组,则会有如下关系:
若父节点表示为 i,则子节点可以表示为:
left = 2 * i + 1;
right = 2 * i + 2;
逆推,如子节点为i,则父节点可以表示为:
root = (i - 1) / 2;
堆使用完全二叉树数组表示时的特性
设一维数组为 tree = [], 父节点下标为 root, 左右子节点下标分别为left、right,则满足:
tree[root] > tree[left] && tree[root] > tree[right]
堆排序
堆排序-不稳定选择排序
时间复杂度:平均=O(nlog n) 最坏=O(nlog n) 最好=O(nlog n)
#include <stdio.h>
/**
* 辅助打印函数
*/
void print_array(int *a, int len) {
for (int i = 0; i < len; i++) {
printf("%d ", a[i]);
}
printf("\n");
}
/**
* 辅助交换函数
*/
void swap(int tree[], int i, int j) {
int temp = tree[i];
tree[i] = tree[j];
tree[j] = temp;
}
/**
* 堆调整
* i 表示调整以i为根的子树的堆
*/
void heapify(int tree[], int n, int i) {
int left = 2 * i + 1; //左孩子的数组坐标
int right = 2 * i + 2; //右孩子的数组坐标
int max = i; //默认最大值为根节点
if ( left < n && tree[left] > tree[max] ) {
max = left; //记录左孩子节点为最大值
}
if ( right < n && tree[right] > tree[max] ) {
max = right; //记录右孩子节点为最大值
}
if (max != i) { //最大值为子节点时
swap(tree, i, max); //调整最大值到根节点
heapify(tree, n, max); //破坏了原有子树的堆结构,故而继续调整子树以维护堆的特效
}
}
/**
* 把n个乱序的数构建为堆
* 堆是一个完全二叉树,填充的数据的时候,从上到下,从左往右,都是有序的,故而存在这样的特效
* 若 i 为父节点,则
* left = 2 * i + 1;
* right = 2 * i + 2;
* 逆推 子节点为i,则父节点为 (i-1)/2
* 最后一个节点的坐标 last_node=n-1
* 最后一个节点的父坐标为 parent=(last_node-1)/2
*/
void build_heap(int tree[], int n) {
int last_node = n - 1, parent = (last_node - 1) / 2;
for (int i = parent; i >= 0; i--) {
heapify(tree, n, i);
}
}
void heap_sort(int tree[], int n) {
build_heap(tree, n); //构建堆
for (int i = n-1; i >= 0; i--) {
swap(tree, i, 0); //隐形消除最后一个元素(把最大值放原数组末尾)
heapify(tree, i, 0);//继续把剩余的元素调整为堆
}
}
int main() {
int tree[] = {2, 5, 1, 3, 10, 4};
print_array(tree, 6);
heap_sort(tree, 6);
print_array(tree, 6);
return 0;
}