您的位置:首页 > 其它

堆排序

2017-03-01 23:43 381 查看

先说说堆

堆分为大根堆和小根堆,都是完全二叉树。

大根堆:要求每个节点不大于其父节点

小根堆 : 要求每个节点不小于其父节点

复习一下完全二叉树

除了最后一层外,其他各层都达到该层节点的最大数,最后一层如果不满,该层所有节点都全部靠左排

一颗二叉树,深度为k,对这棵树编号,如果所有的编号都和满二叉树的编号对应,则这棵树是完全二叉树

堆排序思想

堆排序的话,最重要还是建立堆,然后调整堆,使得堆满足大根堆(或小根堆)的定义。

我们定义了一个维护堆的方法percDown,这个方法维护最大堆的性质。

建立堆的过程其实是生成了一个数组,这个数组满足最大堆,但是这个数组不满足非降序的顺序。所以我们要把最大的元素一个一个提取,然后提取一个进行一次堆调整

最后生成出来的数组就是排序好的

堆排序的实现

堆排序先建堆,也就是将输入数组A[1..N]建成最大堆,注意这里下标是1开始,A[0]我们是用来作为一个哨兵使用。

首先其实是一个建堆过程,这个过程我们让i从A.length/2开始,

因为一个堆有一半的节点都是叶节点,而且一般都是存到数组的后半部分。

所以我们的i从一半长度开始即可。

如图:



我们需要从最后一个元素开始排序,因为其实我们是每次都把最大的那个数取出来,然后减小堆的size,然后再执行一个percDown调整堆,然后产生一个新的大顶堆,然后又继续这个操作,不断直到堆已经完全转为一个数组。 所以其实我们只是对0到i-1进行percDown,i到N-1是排好序的,所以是倒着的

public void heapsort(int[] A){
if(A==null)
return;
if(A.length<1)
return;
int N=A.length;
//Build heap
for(int i=A.length/2;i>=0;i--){
percDown(A,i,A.length);
}
//swap
for(int i=N-1;i>0;i--){//A[0]是最大的元素,A[i]是当前堆的最后一个元素
swap(A,0,i);
//产生新的大顶堆,结束后堆的规模减1, i means the length of heap
percDown(A,0,i);//交换完之后重新调整一下0 和 i之间的节点,把A[0]下移到合适的位置
}
}


技巧

一般我们在堆排序的时候,二叉堆实质我们是用一个数组存储。

利用下标 childNode=parentNode*2 来访问子节点。

最核心的percDown的实现。

思想:

我们要维护一个大根堆的性质,

percDown函数传入数组和一个指定的下标i,

i表示当前的父节点,然后我们通过Child=2*i访问这个子节

点,我们比较当前这个父节点和子节点,

如果父节点比最大的子节点都大那么不需要交换。

如果子节点比父节点大而且没有超出数组,则把子节点上移到父节点的位置。

然后继续循环,比较下一级,即上一次的Child是当前的父节点,仍然是和它的子节点比较。

仍然小于子节点则继续把子节点上移,直到最后当前节点比子节点都大或者超出数组范围。

最后交换一开始的节点最后的i的child节点

public void percDown(int[] A,int i,int N){//i means parent!actually

int child;
int tmp;
for(tmp=A[i];(2*i+1)<N;i=child){//i=child 下沉到child
child=2*i+1;
if(child!=N-1&&A[child+1]>A[child])
child++;
if(tmp>=A[child]) //比最大的儿子还要大,不需要交换
break;
else//tmp 是小的,因为是大顶堆所以需要把tmp下移到合适的位置然后把child上移
A[i]=A[child]; //把最大的child上移
}
//tmp store the initial value.
A[i]=tmp;//actually here i means child,notice i==child!
}


递归思想MAX-HEAPIFY

还有一种是递归实现,仍然是比较当前节点A[i]和他的孩子节点,如果当前节点是最大的,则以该节点为根的子树已经是最大堆,结束。 否则最大元素是它的某个孩子节点,需要交换A[largest]和当前A[i]。但是交换后,以A[i]的孩子节点largest为根的子树可能又会违反最大堆的性质,所以这时候递归调用MAX-HEAPIFY

伪码描述

MAX-HEAPIFY(A,i)
l=2*i;
r=2*i+1;
if(l<=A.heap-szie && A[l]>[i])
largest=l
else
largest=i
if(r<=A.heap-size && A[r]>A[largest]
largest=r
if(largest!=i)
swap(i,largest)
MAX-HEAPIFY(A,largest)


性能分析

最坏 最好 平均 都是

O(N*logN)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  堆排序 二叉树