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;
    }
};
posted @ 2024-01-19 16:39  DawnTraveler  阅读(12)  评论(0编辑  收藏  举报