版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012194956/article/details/79407588
首先需要说明一下“堆”这个数据结构:
完全二叉树(Complete Binary Tree):若设二叉树的深度为h,除第h层外,其他各层(1~h-1)的结点都达到最大个数,第h层所有的结点都连续集中在最左边,这就是完全二叉树。
堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
堆的示意图:
上图中,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子:
该数组从逻辑上讲就是一个堆结构,我们可以用简单的公式来描述一下对的定义:
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
堆排序
堆排序是利用堆这种数据结构而设计的一种排序算法,它是一种选择排序。
堆排序的思想:
(1)构造初始堆。将给定无序序列构造成一个大顶堆(一半升序采用大顶堆,降序采用小顶堆)。假设将给定序列按照上图进行按层绘制成一个完全二叉树,则从最后一个非叶子(对应序列为arr.length/2-1)节点开始,从左至右,从上至下。
(2)将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整剩下的数组元素,使其成为一个大顶堆,得到第二大元素。
(3)重复上述步骤,直到形成一个有序序列
堆排序的时间复杂度:最好、最坏、平均都为O(nlogn)
实现代码:
//处理一个无序数组,将其构造成一个大顶堆
function buildHeap(arr){
var count=Math.floor(arr.length/2); //非叶子结点的个数(对应最后一个非叶子节点的序列号为count-1)
//调整顺序,调整次数为count,第i次调整,对应确定最终大顶堆的非叶子节点i的值
for(var i=count-1;i>=0;i--){
adjustHeap(arr,i,arr.length); //调整以使序列号为i的非叶子节点及其所有子节点构成一个大顶堆
}
}
function adjustHeap(arr,parentInd,length){
//假设数组第一个索引值为0(默认),则父节点下标为i时,其左孩子为2*i+1,右孩子为2*i+2
var tmp=arr[parentInd];
var leftInd=parentInd*2+1; //父节点必定有一个左孩子,需要判断是否有右孩子
var ind=leftInd;
while(ind<length){
if((ind+1)<length && arr[ind]<arr[ind+1]){//如果右孩子存在且大于左孩子,则将索引定位到右孩子
ind++;
}
//ind表示的是子节点中数值较大的那一个孩子对应的索引值
if(arr[parentInd]<arr[ind]){ //如果父节点小于左右孩子的最大值,则交换
arr[parentInd]=arr[ind];
arr[ind]=tmp;
parentInd=ind; //将父节点定位到子节点中较大的那一个(针对子节点同时也是非叶子节点的情况)
ind=parentInd*2+1; //将ind定位到父节点的左孩子(此时需要同上判断ind<arr.length是否成立,也是判断重定位的parentInd是否为非叶子节点)
}else{
break;
}
}
}
function heapSort(arr){
//构建大顶堆
buildHeap(arr);
//堆顶元素交换
var tmpe;
for(var i=arr.length-1;i>0;i--){
tmpe=arr[0];
arr[0]=arr[i];
arr[i]=tmpe;
adjustHeap(arr,0,i); //调整剩余数组,使其中最大的数调整到堆顶
}
return arr; //对排序后生成的有序数组
}
结果测试:
本文参考: