了解堆排序并用js实现

学了一段时间了,都没有把学习心得放网上,现在才发现这样不好不好。。。因此开了博客把自己的学习点滴或者感兴趣的内容记录下来,先讲讲堆排序吧。

根据《算法导论》,理解堆排序需要了解:堆结构、保持堆性质、建堆、最后是堆排序算法。

一、堆

一种数据结构,可视为完全二叉树。如下图

了解堆排序并用js实现_第1张图片

一个父节点最多分二叉,圆圈上面数字是节点序号,可以看到一层层由左向右排列。圆圈里面数字是该节点存的值。其中序号1为整个树的根节点,2、3为其子节点。另外,序号2也是子树(将2、4、5视为一个子树)的根节点。6~10为叶节点,叶节点没有子节点。

         二叉堆分为最大堆和最小堆。最大堆指堆中最大元素放在父节点,最小堆指堆中最小元素放在父节点。堆排序中用最大堆。

另外还需要知道堆的几个结论:

1、设某节点序号为i,则其父节点为⌊i/2⌋,2*i为左子节点序号,2*i+1为右子节点序号。其中,⌊⌋为向下取整符号

2、当存储了n个元素时,⌊n/2⌋+1、⌊n/2⌋+1、···、n为叶节点。

这些可以自己举例验证。就不多说了。

 

二、保持堆性质

         保持堆性质就是保持最大堆性质,即其子节点不能大于父节点。为了达到这一性质,有以下操作:

从最后一个非叶节点(按照上面的结论2,最后一个非叶节点是⌊n/2⌋)开始,考察该节点和它的子节点的值的大小,如果子节点比该元素大,则交换;这样整个交换过后可以保证最大的值肯定交换到了整个堆的根节点上。

但是这样会产生一个问题:设i为某非叶节点序号(值为A[i]),该节点的某一个子节点值比它大时交换;交换后,值 A[i]成为了子节点的值。

但是当该子节点(交换后的A[i])又是另一个子树的根节点时,可能出现它的子节点大于根节点的值A[i]的情况。

因此,对于值A[i],要继续进行堆调整,即对值A[i]重复上面的工作。这样的结果是确保每一个子树都能符合最大堆。上图就是一个符合了最大堆特性的堆。

到这里仍然没有排列完因为:

1、同一个子树的左右两个子节点没有比较大小并排列;(如上图节点8、9)

2、同一层的不同父节点的子节点间没有进行比较。(如上图节点5、6)

三、建堆

         即从最后一个非叶节点⌊n/2⌋到1,进行保持堆性质操作。

四、堆排序算法

 1、在建好了最大堆后,交换节点1(设值为A[1])和最后一个节点;由上面知,A[1]肯定是最大值。

2、交换后,将该节点(值为A[1])移除(从堆结构中移除,不是从数组中移除);

 3、移除后,对新堆的序号1(原来最后1一个节点的值)进行堆性质调整(即比较序号1和子节点的值大小,并交换)。调整后重复步骤1、2。

最大值移除的目的在于得到最大值并放入排列结果数组中,并且缩短堆的长度,减少堆调整的时间。继续进行堆调整的目的很明显是为了得到当前堆的最大值。

js实现如下:

其中,因为上面的堆节点序号是从1开始,而数组索引是从0开始,因此有以下调整:

1、左子节点序号为i*2+1,右子节点序号为i*2+2。(如果还是以前的i*2则序号0的左子节点为0*2=0,显然不对

2、上面堆的最后一个非叶节点⌊n/2⌋,现在为⌊n/2⌋-1。

var len;    
function buildMaxHeap(arr) {   //建堆
    len = arr.length;
    // [n/2-1]表示的是最后一个有子节点 (本来是n/2(堆从1数起),但是这里arr索引是从0开始,所以-1)
    for (var i = Math.floor(len/2)-1; i>=0; i--) {
        maxHeapify(arr, i);
    }
    //对每一个节点(非叶节点),做堆调整
}
function maxHeapify(arr, i) {     //堆调整
    var left = 2*i+1,  
        right = 2*i+2, 
        largest = i;   //i为该子树的根节点

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

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

    if (largest != i) {  //即上面的if中有一个生效了
        swap(arr, i, largest);  //交换最大的为父节点
        maxHeapify(arr, largest);  //交换后,原值arr[i](往下降了)(索引保存为largest),
        //作为根时,子节点可能比它大,因此要继续调整
    }  
} 
function swap(arr, i, j) {
    var temp = arr[i];  
    arr[i] = arr[j];
    arr[j] = temp;
} 
function heapSort(arr) {
    buildMaxHeap(arr);
    for (var i = arr.length-1; i > 0; i--) {
        swap(arr, 0, i);
        len--;
        maxHeapify(arr, 0);
    }
    return arr;
}




 

 

 


你可能感兴趣的:(算法)