堆排序算法详解

261 阅读3分钟

「这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战

前言

堆排序是利用树结构所设计的一种排序算法,在排序过程中把n[0...n-1]看成一颗完全二叉树,利用双亲节点和孩子节点之间的关系,构造出大根堆或者小根堆。大根堆也就是每个双亲节点都比孩子节点大,所以根是最大的数字。小根堆意思一样,只不过每个双亲节点都比孩子节点小。我们这里只利用大根堆来排序。堆排序其实就是一个持续构建大根堆的过程,每一趟排序把构建完的大根堆,取出最大值也就是根,放在最末尾的有序区内,然后重复把无序区构建成大根堆,再把根放入有序区内,直到排序完成。

堆排序的时间复杂度为O(nlog2n),空间复杂度为O(1)

过程

堆排序是利用树结构所设计的一种排序算法,在排序过程中把n[0...n-1]看成一颗完全二叉树,利用双亲节点和孩子节点之间的关系,构造出大根堆或者小根堆。大根堆也就是每个双亲节点都比孩子节点大,所以根是最大的数字。小根堆意思一样,只不过每个双亲节点都比孩子节点小。我们这里只利用大根堆来排序。堆排序其实就是一个持续构建大根堆的过程,每一趟排序把构建完的大根堆,取出最大值也就是根,放在最末尾的有序区内,然后重复把无序区构建成大根堆,再把根放入有序区内,直到排序完成。

构建大根堆的代码:

//  比较current节点和他的孩子节点大小,并调整为大的值在前
function HeapAdjust(arr, current, size) {
    var temp = arr[current-1];
    // 完全二叉树由一个特点,左孩子的位置是双亲节点位置的两倍,则右孩子是两倍加1,所以这里的2*current代表 current 节点的左孩子
    for(var j = 2*current; j <= size; j*=2) {
        if(j<size && arr[j] < arr[j+1]) ++j;
        if(temp > arr[j]) break;
        arr[current-1]=arr[j];
        current = j;
    }
    arr[current-1] = temp;
    return arr;
}
// 建大根堆
function CreatHeap(arr) {
    const len = arr.length;
    for(var i = len; i >0; i--) {
        HeapAdjust(arr, i, len);
    }
}

我们现在知道了大根堆的创建,就可以很容易的写出堆排序算法

function HeapSort(arr) {
    // 建初始堆
    CreatHeap(arr)
    var temp;
    console.log('初始堆', arr)
    for(let i = arr.length;i>1;i--) {
        temp = arr[0];
        arr[0] = arr[i-1];
        arr[i-1] = temp;
        console.log(`第${arr.length-i+1}趟:`, arr)
        HeapAdjust(arr, 1, i-1);
    }
    return arr;
}

时间复杂度:堆排序运行时间主要消耗在建初始堆和调整堆时反复“筛选”上。它的时间复杂度是O(nlog2n)。
空间复杂度:仅需要一个temp临时变量,O(1);堆排序不稳定,数组记录比较高时使用堆排序会更有效率。