堆排序思想
----雨竹清风
堆排序是一种树形选择排序方法,它是将待排序列看做成一棵完全二叉树,是利用完全二叉树中双亲与孩子结点之间的关系,来处理当前的无序序列中元素。
堆的定义:n个元素的无序序列{k1,k2,k3…kn};当前仅当满足下面关系时称为堆:
小顶堆:ki ≤k2i && ki ≤k2i+1
大顶堆:ki ≥k2i && ki ≥k2i+1
例如:大顶堆 和 小顶堆
![堆排序 - 雨竹清风 - 雨竹清风的博客 堆排序 - 雨竹清风 - 雨竹清风的博客](http://img1.ph.126.net/6rh4ilDsLci5ijSneq8adw==/6619294195189580029.png)
大体思路:(以大顶堆为例)先将无序序列调整成大顶堆。堆排序过程中将最大值输出,即与最后一个元素交换,交换后就已经不是一个大顶堆,所以需要调整成大顶堆。
以无序序列{49,38,65,97,76,13,27,49}为例,对其进行堆排序。
1.调整成大顶堆
我们首先将这个无序序列看成一棵完全二叉树,如下图所示:
![堆排序 - 雨竹清风 - 雨竹清风的博客 堆排序 - 雨竹清风 - 雨竹清风的博客](http://img1.ph.126.net/q7UUIysM6_Ta0u9l1Sf98A==/6631306359723246928.png)
然后将其调整成大顶堆,从第一个非叶子节点开始调整,第一个非叶子节点是第n/2个结点(元素),判断以它为根的子树是否是一个大顶堆,若不是大顶堆,则进行调整。直到所有的元素组成的是一棵大顶堆为止。过程如下图所示:
![堆排序 - 雨竹清风 - 雨竹清风的博客 堆排序 - 雨竹清风 - 雨竹清风的博客](http://img1.ph.126.net/yaEtR5M4_XvaOXlmvpzgCg==/6619075392375651814.png)
![堆排序 - 雨竹清风 - 雨竹清风的博客 堆排序 - 雨竹清风 - 雨竹清风的博客](http://img1.ph.126.net/FAAkR2T5dYG_CV9O4lL7gA==/6630066110607645808.png)
![堆排序 - 雨竹清风 - 雨竹清风的博客 堆排序 - 雨竹清风 - 雨竹清风的博客](http://img0.ph.126.net/X5GTQ6J0aHeK4BAOPm4KqQ==/6619395350259336873.png)
![堆排序 - 雨竹清风 - 雨竹清风的博客 堆排序 - 雨竹清风 - 雨竹清风的博客](http://img0.ph.126.net/nQdEIGA_0NTM2wa0ixnSiw==/6630104593514618430.png)
![堆排序 - 雨竹清风 - 雨竹清风的博客 堆排序 - 雨竹清风 - 雨竹清风的博客](http://img0.ph.126.net/cgkRkWQGjegl6DurFwWfrQ==/6630193653956469915.png)
![堆排序 - 雨竹清风 - 雨竹清风的博客 堆排序 - 雨竹清风 - 雨竹清风的博客](http://img2.ph.126.net/87sYTiqVVD_ZQxT52FLWEQ==/6630884147259411152.png)
2.堆排序
将第一个元素和最后一个元素交换,然后调整剩余的元素,调整为大顶堆。
将38与97交换,调整除97之外的元素,调整成大顶堆。调整后结果为:
![堆排序 - 雨竹清风 - 雨竹清风的博客 堆排序 - 雨竹清风 - 雨竹清风的博客](http://img0.ph.126.net/HWcZBoO6VpynY4fUvNrMWA==/1181913427226305245.png)
将76与27交换,然后调整,结果如下:
![堆排序 - 雨竹清风 - 雨竹清风的博客 堆排序 - 雨竹清风 - 雨竹清风的博客](http://img2.ph.126.net/IYwWlWXkGDtyAo_5ATTsQA==/37999121874302405.png)
然后交换65与13,调整为:
![堆排序 - 雨竹清风 - 雨竹清风的博客 堆排序 - 雨竹清风 - 雨竹清风的博客](http://img1.ph.126.net/oypFfZtybHwD9ms0vIt9bg==/6619072093840768537.png)
交换13与49,调整后的结果:
![堆排序 - 雨竹清风 - 雨竹清风的博客 堆排序 - 雨竹清风 - 雨竹清风的博客](http://img1.ph.126.net/Bub5_jN3Z3mJegyN6l7dmA==/6630518009886659740.png)
交换13与49,调整后的结果:
![堆排序 - 雨竹清风 - 雨竹清风的博客 堆排序 - 雨竹清风 - 雨竹清风的博客](http://img2.ph.126.net/rN3tNSTfPo-z3NIz8aMMMA==/6631428405513925781.png)
交换38与27,调整后的结果:
![堆排序 - 雨竹清风 - 雨竹清风的博客 堆排序 - 雨竹清风 - 雨竹清风的博客](http://img0.ph.126.net/_1GI9PsOp_2p_z_DVOqpkQ==/6630757703422220649.png)
交换27与13,调整后的结果:
![堆排序 - 雨竹清风 - 雨竹清风的博客 堆排序 - 雨竹清风 - 雨竹清风的博客](http://img0.ph.126.net/fLV8GkKPbToghRnXefP0Tw==/6630118887165779592.png)
最终的结果为:13 27 38 49 49 65 76 97
/堆排序的代码/
堆排序主要由4个函数组成:
void BulidHeap(int A[], int len);//建初始堆,A是一个数组,len是数组的长度
void AdjustDown(int A[],int k,int len);//开始调整,k是从哪个地方开始调整
void Swap(int A[],int i,int j);//将第i个位置的元素和第1个元素进行交换
void HeapSortPro(int A[], int len);//堆排序
///
HeapSortPro()是堆排序的大体步骤,首先创建一个大顶堆,第二步交换,调整。创建大顶堆调用BulidHeap()函数,交换调用Swap()函数,调整调用AdjustDown()函数。
在BulidHeap()中调用的是AdjustDown()函数,对无序序列建立一个大顶堆。
建立大顶堆时候,从最后一个非叶子结点开始调整。
在调整时候是最关键点:
建立一个大顶堆,首先找到对应的左右孩子结点,判断哪个大,若右孩子大则将右孩子与该结点交换,否则左孩子与该结点交换。
直到调整到最后一个结点为止。
代码如下:
void AdjustDown1(int A[],int k,int len)//向下调整,开始调整,k是从哪个地方开始调整,
{
int i = k * 2;
for(i ; i < len ; i = i * 2)
{
//技巧
if (A[i] < A[i + 1])//找到其左右孩子的最大的那个进行比较,若根比孩子最大的还大,就不用调整
{
++i;
}
//先判断不成立的情况,以便直接跳出循环,节省时间
if (A[k] > A[i])//若大于则不用调整
{
break;
}
else
{
Swap(A, k, i);
k = i;//为了继续向下调整
}
}
//将i==len时候单独拿出来,节省了遍历for循环的时间
if(i == len && A[k] < A[i])
{
Swap(A, k, i);
}
}
//堆排序时间复杂度/
堆排序方法对于记录数比较少的情况不值得提倡,但对于较大的文件还是比较有效。堆排序的时间复杂度为O(nlogn);