使用堆排序之前要知道堆是什么
堆分为 大根堆 和 小根堆
大根堆:父节点一定比孩子节点大的完全二叉树
小根堆:父节点一定比孩子节点小的完全二叉树
堆排序的时间复杂度:O(nlogn)
堆排序的空间复杂度:O(1)
堆排序的稳定性:不稳定
核心思想:
首先要建堆: 从下向上,从右向左,依次调整父亲节点
建完堆后找最值,大根堆的最大值就是根,把根(数组第0个元素)与数组最后一个元素交换也就是放在数组尾部,数组长度-1(放在后面的不参与下次的构建,-1也方便下次根与最后元素的交换操作),然后调整那个新的根,得到新的大根堆,重复之前的操作(求下一个最大值树有几层就有几次操作)。
堆排序步骤
调整: 找孩子中的最大值max与父亲比较
max<父 不动
max>父 交换,调整原max孩子位置
1.构建初始堆
1.1从n/2-1到0依次调整各父节点
2.sort
2.1最值的放入后面,数组长度-1
2.2调整堆顶(放入后面的不参与,数组长度-1就是把那个元素除去要调整的范围了)
//调整堆
void adjust(vector<int>& arr,int len,int index) {
int max;//标记一下最大孩子节点的下标
//初始化最大孩子节点为左孩子
for (max = 2 * index + 1; max < len; max = 2 * index + 1) {
//判断有无右孩子,如果有右孩子其右孩子比左孩子大就将最大孩子节点的下标更新为右孩子的坐标
if (2 * index + 2 < len && arr[max] < arr[2 * index + 2]) max = 2 * index + 2;
//如果最大孩子节点的值比父节点的值大就进行交换,然后调整交换的孩子的位置
if (arr[max] > arr[index]) {
//交换
arr[max] ^= arr[index];
arr[index] ^= arr[max];
arr[max] ^= arr[index];
//设置index为原来最大孩子的坐标,此次循环结束的时候会更新max为(2 * index + 1)
index = max;
}
else break;//如果小于等于父亲节点就说明符合大根堆的规则退出
}
}
void heapSort(vector<int>& arr) {
//从后向前调整父节点,构建大根堆
for (int i = arr.size() / 2 - 1; i >= 0; i--) {
adjust(arr,arr.size(), i);
}
for (int i = arr.size() - 1; i > 0; i--) {
//构建完已经是一个大根堆
//将根节点也就是第0个元素和最后一个元素交换
//想当于把最大的元素放在后面,那么第0个元素就不符合大根堆的特性
//在对他进行调整,但是后面已经是有序的就调整前i个元素就可以了
arr[0] ^= arr[i];
arr[i] ^= arr[0];
arr[0] ^= arr[i];
//调整有序元素前面数组的第0个元素
adjust(arr,i, 0);
}
}