关于完全二叉树最后一个非叶子节点的索引求解思路为:
如果父节点的索引为x,则其子节点为2x + 1 和 2x + 2,
假设某数组有n个元素,则最后一个元素的索引为n-1,
设父节点索引为m,分2种情况讨论:
- 最后一个节点位于左子树,则n-1=2*m+1, 则m=(n-2)/2
- 最后一个节点位于右子树,则n-1=2*m+2,则m=(n-3)/2,最终得到的都是m=n/2-1
思路直接参考源码注释
/*
* 1、先将当前的数组建成大顶堆
* 2、依次交换index 0 的元素和当前的最大索引的元素,比如第一次就是index 0 和 最后一个交换
* 因为第一个已经是最大的了,所以交换之后,最后一个即为最大元素,交换之后,不一定满足大顶堆
* 的特性,进入第三步
* 3、重新针对index 0 再调整堆 为大顶堆
* 4、再交换index 0 和 当前的最大索引交换,直至结束,就完成了数组的排序
*/
#include <iostream>
using namespace std;
void swap(int* arr, int a, int b)
{
int tmp = arr[a];
arr[a] = arr[b];
arr[b] = tmp;
}
void adjust(int* arr, int i, int max_index)
{
// 针对每一个非叶子节点,先找出左右子节点中较大的一个值
// 然后拿较大的值与当前的节点对比,如果当前节点小,则将
// 当前节点与较大的值交换(因为要得到一个大顶堆),交换
// 之后可能会导致后面的子树不平衡了,继续往下处理,即i = larger_index这一步
while(i * 2 + 1 <= max_index) {
int j = i * 2 + 1;
int larger_index = j;
if (j + 1 <= max_index && arr[j + 1] > arr[j]) {
larger_index = j + 1;
}
if (arr[larger_index] > arr[i]) {
swap(arr, i, larger_index);
} else {
break;
}
i = larger_index;
}
}
void heap_sort(int* arr, int num)
{
// 这一步是将当前的数组建成大顶堆,具体就是从最后一个非叶子节点开始往上回溯
// 最后一个非叶子节点为元素个数num/2 - 1
for (int i = num / 2 - 1; i >= 0; i--) {
adjust(arr, i, num - 1);
}
num--;
while(num>0) {
// 每次都将index 0的元素与当前的最大index值交换,这样当前的最大值就下沉到最后
swap(arr, 0, num);
// 交换之后,大顶堆的特性可能被破坏,重新调整回来
adjust(arr, 0, --num);
}
}
int main()
{
int arr[] = {2, 4, 1, 99, 8, 10};
heap_sort(arr, sizeof(arr) / sizeof(int));
for (int i = 0; i < sizeof(arr) / sizeof(int); i++) {
cout << arr[i] << "\t";
}
cout << endl;
return 0;
}