排序思路:
采用维护大根堆的方法,取堆顶(最大值)到已排序数组,同时从堆中剔除堆顶,用堆末尾元素补充到堆顶,进行下一轮堆维护。
堆是一种完整二叉树的数据结构。可以将待排序数组转化为堆结构。
最后一个非叶子节点:a[len/2-1]
若对于一个非叶子节点a[i],左孩子节点是a[2*i+1],右孩子节点是a[2*i+2]。
大根堆是指堆满足每一个非叶子节点都大于其孩子节点。
a[i]>=[2*i+1] && a[i]>=[2*i+2]
例子:
{22,35,16,9,5,20}
- 进行堆维护
22
35 16
9 5 20
最后的非叶子节点是a[2]=16
16和20交换;
22
35 20
9 5 16
重新维护;
a[1]=35和a[2]=20节点符合要求;
22和35交换;
35
22 20
9 5 16
重新维护;
满足大根堆要求,本次维护结束。
2. 拿出堆顶a[0]=35,与数组末尾a[5]=16进行交换。
3. 排除数组末尾(已排序好的数组),进入下一轮堆维护。
4. 待维护堆变成
16
22 20
9 5
后续步骤省略。。。(懒了)
起始堆的维护可以看作是找最大值的过程。思路类似于选择排序,只不过选择排序是选出待排序的最小值,放到数组起始。
c实现
void adjustHeap(int array[], int len){
/*
* 0
* 1 2
* 3 4 5 6
* 7 8 9 10 11 12 13 14
* array[i]父节点
* array[2*i+1]左孩子节点;array[2*i+2]右孩子节点;
*/
int tmp;
for(int i=len/2-1; i>=0; i--){
/*
* 从后向前遍历所有非叶子节点
* 若存在孩子节点大于父节点的情况,就进行交换
* 保持大根堆模型
*/
//非子叶节点至少含有左孩子节点
if(array[i]<array[2*i+1]) {
tmp = array[i];
array[i] = array[2 * i + 1];
array[2 * i + 1] = tmp;
adjustHeap(array,len);
}
//非子叶节点可能不含有右孩子节点,需要考虑越界情况
if(array[i]<array[2*i+2] && (2*i+2)<len) {
tmp = array[i];
array[i] = array[2 * i + 2];
array[2 * i + 2] = tmp;
adjustHeap(array,len);
}
}
}
void heapSort(int array[], int len){
int max;
int cur_len=len;
for(; cur_len>1; cur_len--) {
adjustHeap(array, cur_len);
max = array[0];
array[0] = array[cur_len - 1];
array[cur_len - 1] = max;
}
}
平均时间复杂度是O(nlogn) ,节点遍历+堆排序(递归)。