堆排序
相关知识:
1、堆是完全二叉树,这意味着除了最后一层的右边元素可能有空缺外,树的每一层都是满的。
2、大顶堆:树中的每一个非叶节点都要大于或等于其子节点,即(arr[i]>=arr[2i+1]&&arr[i]>=arr[2i+2],其中i<=Math.floor(arr.length/2)-1)
3、小顶堆:树中的每一个非叶节点都要小于于或等于其子节点,即(arr[i]<=arr[2i+1]&&arr[i]<=arr[2i+2],其中0<= i <=Math.floor(arr.length/2)-1)
步骤(大顶堆):
堆调整:将节点和其子节点的值进行比较,如果子节点中的较大的一个值比其值大,则交换这两个节点。
1、构造大顶堆:从最后一个非叶节点开始,依次向前对每一个非叶节点进行堆调整,如果其子节点中较大的一个值比这个叶节点的值大,则交换这两个节点并对这个子节点进行堆调整
2、大顶堆构造好之后,交换堆的第一个节点(此时为最大值)和最后一个节点,并移除最后一个节点
3、在经过步骤2之后,大顶堆的结构被破坏,所以需要对堆进行调整,但是此时除了第一个节点和其子节点不满足大顶堆的条件,其余节点都满足,所以只需要传入第一个节点,对其进行递归堆调整。
4、调整完之后,依次进行步骤2,3。直到剩下最后一个元素。
堆排序代码:
/**
* 遍历非叶子节点
*/
function traverseNonLeafNodes(){
//初始化堆时从最后一个非叶节点开始
for(let i=Math.floor(length/2)-1;i>=0;i--){
adjustHeap(i);
}
}
/**
* 对堆进行调整,如果非叶节点的子节点中的较大的一个值比这个节点的值大,就交换,
* 并且如果被交换的这个子节点为非叶节点,就对它和它的子节点进行比较,依次进行下去,直到节点为叶节点
*/
function adjustHeap(i){
let largest = i;
let left = largest*2+1;
let right = largest*2+2;
if(right<length&&arr[right]>arr[largest]){
largest = right;
}
if(left<length&&arr[left]>arr[largest]){
largest = left;
}
if(largest != i){
[arr[largest],arr[i]] = [arr[i],arr[largest]];
if(largest<=Math.floor(length/2)-1){
adjustHeap(largest);
}
}
}
/**
* 堆排序
*/
function heapSort(){
//初始化堆
traverseNonLeafNodes();
//交换堆中的第一个节点的值和最后一个节点的值,并移除交换后的最后一个节点
while(length>0){
[arr[0],arr[length-1]] = [arr[length-1],arr[0]]
length--;
//第一次的堆调整从最后一个非叶节点从后往前调整,之后的调整从头节点开始从前往后调整
adjustHeap(0);
}
}
let arr = [1,2,3,4,5,6]
let length = arr.length;
heapSort()
console.log(arr);