912.排序数组--堆排序
1.题目介绍
给你一个整数数组 nums,请你将该数组升序排列。
示例 1:
输入:nums = [5,2,3,1]
输出:[1,2,3,5]
示例 2:
输入:nums = [5,1,1,2,0,0]
输出:[0,0,1,1,2,5]
2.题解
补充-建堆过程的时间复杂度为O(n)而不是\(O(nlogn)\)!!!(建堆是自底向上的)
从下图可以看出,我们从底层开始解析:
1.对于叶子结点,有2^(h-1)个节点元素,但是我们不需要也没有下层进行比较交换,故乘以0
2.对于非叶子结点,有一共 2^(i-1) 个节点元素,处于第i层的位置,最坏情况还要向下遍历(h-i)层,也就是该元素需要跟其下方的每一层元素都进行交换。故为2^(i-1) * (h-i)
经过下列式子计算证明,实际上时间复杂度为O(n)!!!
假定这里的元素个数为n,那么总层数\(h = log_2n\)
堆调整的时间复杂度为O(logn),是自顶向下的。
每次重建意味着有一个节点出堆,所以需要将堆的容量减一。
adjustheap()函数的时间复杂度k=log(n),k为堆的层数。(这个顶点元素,每次只要选择走左边和右边即可,每走一次待判断数量减半!即log(n))
所以在每次重建时,随着堆的容量的减小,层数会下降,函数时间复杂度会变化。
重建堆一共需要n-1次循环,每次循环的比较次数为log(i),则相加为:log2+log3+…+log(n-1)+log(n)≈log(n!)。可以证明log(n!)和nlog(n)是同阶函数:
堆排序相当于每次选择最后一个元素(遍历数组O(n)),然后进行建堆(O(logn))
故时间复杂度为O(nlogn)!!!
2.1 堆排序
思路
题目给了我们一个vector数组,要使用堆排序,我们首先要创建一个大根堆,再在这个大根堆的基础上对数组进行排序
一.创建大根堆
1.利用好完全二叉树的性质,对于一个序号为i的节点,其父节点为 i/2, 左子结点为 2i, 右子节点为 2i+1, 但注意!!!这只是针对索引从1开始的情况
这里我们数组索引从0开始,父节点为 i/2, 左子结点为 2i+1, 右子节点为 2i+2;
对应int lson = (i << 1) + 1, rson = (i << 1) + 2;
2.创建大根堆采用自下而上的方式(有点类似递归的思想),先将节点i的左右子树都建立为大根堆,也就是两边子树最大的值刚好为节点i的左右子节点,再来跟节点i进行判断,将其中大的换上来或者保留原值即可。
3.创建大根堆时,选择从len/2开始?这是因为叶子结点在创建大根堆时先行跳过,直接从叶子结点上一层开始进行调整(叶子结点无左右子节点,无法比较)
对应for(int i = len / 2; i >= 0; i--){...}
4.注意当第一次判断中左子结点不存在或者小于根节点时,large = i;
5.注意:这里 i = large; 配合 for(; (i << 1) + 1 <= len;){}
使得当完成一次节点交换后,我们重新构建子大根堆。因为我们将较小值换到子节点,可能破坏下方的大根堆,所以需要检查!!!
if (large != i){ swap(nums[i], nums[large]); i = large; }
二.堆排序
每次将大根堆的根节点,也就是最上方的节点换到最后一个节点固定,这样最后一个最大数便已经确定
之后再调整剩余元素的大根堆,再将调整后的根节点换到倒数第二个节点固定,依次这样进行,每次将最大的节点换到待排序数组末尾即可。
代码
class Solution {
/* 调整以i为根节点的大根堆 */
void maxHeapify(vector<int>&nums, int i, int len){
for(; (i << 1) + 1 <= len;){
int lson = (i << 1) + 1, rson = (i << 1) + 2;
int large;
if (lson <= len && nums[lson] > nums[i]) large = lson;
else large = i;
if (rson <= len && nums[rson] > nums[large]) large = rson;
if (large != i){
swap(nums[i], nums[large]);
i = large;
}
// 否则说明堆已经调整完毕,直接退出
else break;
}
}
/* 创建大根堆,自下向上 */
void buildMaxHeap(vector<int>&nums, int len){
for(int i = len / 2; i >= 0; i--){
maxHeapify(nums, i, len);
}
}
/* 大根堆排序*/
void heapSort(vector<int>&nums){
int len = (int)nums.size() - 1;
buildMaxHeap(nums, len);
for(int i = len; i >= 1; i--){
swap(nums[0], nums[i]);
len--;
maxHeapify(nums, 0, len);
}
}
public:
vector<int> sortArray(vector<int>& nums) {
heapSort(nums);
return nums;
}
};