详解排序算法(三)之2种选择排序(简单选择排序、堆排序)

简单选择排序

算法步骤

  1. 遍历数列,找到最小的值,置于第1位
  2. 从第2位开始,遍历数列,找到最小的值,置于第2位
  3. 以此类推,直到开始位置变为最后一位,排序结束。

示例

我们取 3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48 来进行示范

  1. 第1轮排序,从第1位 3 开始,遍历数列,找到最小值 2,将其置于第1位,即与 3 交换位置,得 2, 44, 38, 5, 47, 15, 36, 26, 27, 3, 46, 4, 19, 50, 48

  2. 第2轮排序,从第2位 44 开始,遍历数列,找到最小值 3,将其置于第2位,即与 44 交换位置,得 2, 3, 38, 5, 47, 15, 36, 26, 27, 44, 46, 4, 19, 50, 48

  3. 第3轮排序,从第3位 38 开始,遍历数列,找到最小值 4,将其置于第3位,即与 38 交换位置,得 2, 3, 4, 5, 47, 15, 36, 26, 27, 44, 46, 38, 19, 50, 48

  4. 以此类推,直到开始位置变为最后一位,排序结束。

动态图
img

javascript代码

function selectionSort (arr) {
    let min, temp

    for (let i = 0; i < arr.length - 1; i++) {
        min = i // 最小数索引
    
        for (let j = i; j < arr.length; j++) {
            if (arr[j] < arr[min]) {
                min = j
            }
        }

        temp = arr[i]
        arr[i] = arr[min]
        arr[min] = temp
    }

    return arr
}

堆排序

在讲堆排序之前,我们先了解一些二叉树的知识。

  1. 二叉树

    简单地理解,满足以下两个条件的树就是二叉树:

    • 本身是有序树;

    • 树中包含的各个节点的度不能超过 2,即只能是 0、1 或者 2;

    二叉树示意图

  2. 满二叉树

    如果二叉树中除了叶子结点,每个结点的度都为 2,则此二叉树称为满二叉树。

    满二叉树示意图

  3. 完全二叉树

    如果二叉树中除去最后一层节点为满二叉树,且最后一层的结点依次从左到右分布,则此二叉树被称为完全二叉树。

完全二叉树示意图

​ 对于完全二叉树来说,如果记根节点的索引值为0,那么对于任意一个节点i,其左右节点如果存在的话,索引值分别为 2i + 1,2i + 2。之所以介绍二叉树,是因为堆排序就要用到我们上面所讲的完全二叉树。

算法步骤

  1. 建立大跟堆或小跟堆,我们以大根堆为例,所谓建立大跟堆,就是各个节点与其左右节点比较,将大的值置换到根部,这样一个个节点比较下去,直到将最大的值置换到根节点,大跟堆建立完毕。

  2. 将大根堆中的根节点与其尾节点进行交换

  3. 将二叉树的长度减一,即尾结点不参与下一轮排序。重复操作1,直到直到堆的尺寸为 1。

示例

我们取 91, 60, 96, 13, 35, 65, 46, 65, 10, 30, 20, 31, 77, 81, 22 进行示范。

数列对应的完全二叉树如下图

image-20220309152552974

第一轮排序

  1. 4681、22 比较,81最大,81 与 46 互换位置,得
    image-20220309152931614

  2. 6531、77 比较,77最大,77 与 65 互换位置,得
    image-20220309153200342

  3. 3530、20 比较,35最大,位置不变。

  4. 1365、10 比较,65最大,65 与 13 互换,得
    image-20220309153734642

  5. 9677、81 比较,96最大,位置不变

  6. 6065、35比较,65最大,65 与 60 互换,得
    image-20220309154706189

  7. 9165、96比较,96最大,96 与 91互换,得到最大值96
    image-20220309154753237

  8. 将根节点96与尾结点22互换,即最大值置于尾结点。数列长度减一,即尾节点不再参与堆构建,如图
    image-20220309154904473

  9. 第一轮排序结束,重复以上步骤,直到堆的尺寸为 1,排序结束。

动态图
img

javascript代码

let last // 参与堆构建的最后一个节点索引值

// 建立大根堆
function buildMaxHeap(arr) {
    for (let i = Math.floor((last + 1) / 2); i >= 0; i--) {
        heapify(arr, i)
    }
}

// 堆调整函数,此处为构建大根堆
function heapify (arr, i) {
    // 在完全二叉树中,索引值从0开始,若节点i存在左右子节点,则左右子节点索引分别为 2i + 1, 2i + 2
    let left = 2 * i + 1,
        right = 2 * i + 2,
        largest = i

    if (left <= last && arr[left] > arr[largest]) {
        largest = left
    }

    if (right <= last && arr[right] > arr[largest]) {
        largest = right
    }

    if (largest != i) {
        swap(arr, i, largest)
    }
}

function swap(arr, i, j) {
    let temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

// 堆排序
function heapSort(arr) {
    for (let i = 0; i < arr.length - 1; i++) {
        last = arr.length - 1 - i
        buildMaxHeap(arr)
        swap(arr, 0, last)
    }

    return arr
}

两种算法的复杂度及稳定性

排序算法时间复杂度(平均)时间复杂度(最坏)时间复杂度(最好)空间复杂度稳定性
简单选择排序O(n2)O(n2)O(n2)O(1)不稳定
堆排序O(nlog2n)O(nlog2n)O(nlog2n)O(1)不稳定
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值