排序算法 --- 堆排序

根据大顶堆的描述, 父节点的值始终大于子节点(如果有的话)的值, 再加上堆是完全二叉树, 可以用数组表示, 那么就可以用来进行排序.

具体做法就是, 对于随机排列的数组:

1. 首先将其构建成一个大顶堆, 根据堆的性质, 此时堆顶就是最大值.

2. 把堆顶元素与数组最后一个元素进行交换, 也就是把最大值换至最后一个元素

3. 把元素个数减一, 重复上述两个步骤

可以看出最复杂的, 也就是构造大顶堆的过程, 对于一个随机排列的数组, 其构建大顶堆的步骤如下:

假设有一个数组A, 共有n(14)个元素(如图).

1. 首次构建时, 需要遍历所有的子树, 让所有的子树满足大顶堆性质, 而只有一个节点的子树则不需要遍历(因为不需要调整), 所以要从第一个非叶子节点的子树开始遍历, 构建大顶堆. 而第一个非叶子节点的子树根节点, 在数组中的下标为 n / 2 - 1(也就是6, 目前6不需要调整), 从此开始一直至0(注意当上层树发生变化时, 需要递归调整下层的树, 为什么要从下至上调整, 因为下层调整完成后, 已经把下层最大的调整至根节点了, 这样当根节点发生变化时, 只需要从上向下再调整一遍, 因为下层已经不可能有比上面更大的值了).

例子中的步骤为:

1. 首先遍历第一个非叶子节点, 14 / 2 - 1 = 6, 发现不用调整, 然后遍历5, 发现也不用调整, 然后遍历4, 交换4 9的值:

 2. 遍历3, 交换3 8:

3.  遍历2, 不用动, 遍历1, 交换1 3, 然后发现68交换到3后, 3 7 8子树不需要动(就算需要动, 也不需要再动1 3 4了, 因为之前3 7 8已经调整过, 新上来的不可能取代已经上去的了):

4. 遍历0, 交换0 1, 交换1 3, :

 

(3 7 8不再需要调整, 原因同上一步)

2. 首次构建完大顶堆后, 交换首个元素与最后一个元素的值, 然后重新构建大顶堆

1. 交换0 13:

2. 数组长度少一:

3. 从根节点开始重新构建大顶堆, 交换0 2,  交换2 5:

4. 这样又重新构建成了大顶堆, 重复上述步骤

代码:

leetcode链接(第912题): https://leetcode-cn.com/problems/sort-an-array/submissions/

(第215题)

void heapify(vector<int>& nums, int root, int max_index) {
    int left = 2 * root + 1;
    if (left > max_index)
        return;
    int right = 2 * root + 2;
    int exchange_index = left;
    if (right <= max_index && nums[right] > nums[left])
        exchange_index = right;
    if (nums[root] < nums[exchange_index]) {
        swap(nums[root], nums[exchange_index]);
        heapify(nums, exchange_index, max_index);
    }
}
vector<int> sortArray(vector<int>& nums) {
    int total = nums.size();
    for (int i = total / 2 - 1; i >= 0; i --) {
        heapify(nums, i, total - 1);
    }
    int target_index = total - 1;
    while (target_index > 0) {
        swap(nums[0], nums[target_index]);
        target_index --;
        heapify(nums, 0, target_index);
    }
    return nums;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
排序是计机科学中常见的操作,它将一组元素按照特定的顺序重新排列。排序算法的目标通常是将元素按照升序或降序排列。 常见的排序算法有很多种,每种法都有不同的时间复杂度和空间复杂度。以下是几种常见的排序算法: 1. 冒泡排序(Bubble Sort):比较相邻的两个元素,如果顺序不正确就交换位置,每次遍历将一个最大(最小)的元素移到最后(最前)。时间复杂度为O(n^2)。 2. 插入排序(Insertion Sort):将数组分为已排序和未排序两部分,每次从未排序部分取出一个元素,插入已排序部分的适当位置。时间复杂度为O(n^2)。 3. 选择排序(Selection Sort):每次从未排序部分选择一个最小(最大)的元素放到已排序部分的末尾。时间复杂度为O(n^2)。 4. 快速排序(Quick Sort):选取一个基准元素,将数组划分为两个子数组,小于基准元素的放在左边,大于基准元素的放在右边,然后对子数组进行递归排序。时间复杂度平均情况下为O(nlogn),最坏情况下为O(n^2)。 5. 归并排序(Merge Sort):将数组递归分成两个子数组,然后对子数组进行排序,最后将两个已排序的子数组合并成一个有序数组。时间复杂度为O(nlogn)。 6. 堆排序(Heap Sort):将数组构建成一个最大(最小)堆,每次从堆顶取出最大(最小)元素放到已排序部分的末尾,然后调整堆使其满足堆的性质。时间复杂度为O(nlogn)。 这里只介绍了几种常见的排序算法,每种法都有其适用的场景和优缺点。在实际应用中,根据数据规模和性能要求选择合适的排序算法非常重要。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值