您的位置:首页 > 编程语言 > C语言/C++

排序算法之堆排序

2017-02-10 19:38 204 查看
堆排序比一般的排序算法略为复杂。

1.什么是二叉堆?

2.如何用数组描述一个二叉堆?

3.如果构建最大堆或最小堆

4.构建好最大堆或最小堆后,对堆的排序

1.什么是二叉堆?

二叉堆是完全二叉树或近似完全二叉树。而且任何父节点的键值都大于等于(或小于等于)任何一点子节点的兼职。

父节点键值大等于他的子节点的键值为最大堆,父节点键值小于等于他的子节点的键值为最小堆。

如下图,是一个二叉堆:



2.用数组描述一个二叉堆

如上面的二叉堆,用数组表示就是A[ 6 ] = {16,7,3,20,17,8};

这个方法跟描述树的方法是一样的。

3.构建最小堆(最大堆)

(以最大堆为例)

从堆 最后一个有叶子结点的结点 开始,设堆有 n 个元素,则该节点序号为:(n/2) -1,上图中该节点就是 3,也就是 A[ 6/2 -1 ] = A[2 ]

构建最大堆的过程:


原始堆。


先从 最后一个有叶子结点的结点(A[ 2 ]) 开始,8比3大,因此 位置互换。


然后到A[ 1 ] ,首先选出比较大的孩子结点,再跟父节点比,看哪个大。大的跟父节点换位置


然后到A[ 0 ].同上


把16换下来后,会发现红色圈的三个里面是 17 最大,因此把 17和 16的位置互换。

这就完成了最大堆了。可以开始排序

下面给出最大堆的构建代码:(最小堆就是找最小的)

/*若有结点 i,它的父结点为 (i/2) -1,其左结点是 2*i +1,右结点是 2*i +2*/
void MaxHeapFixdown(int a[], int i, int n)
{
int j, temp;

temp = a[i];  			//a[i] 相当于当前要处理的值
j = 2 * i + 1;  		//左子结点
while (j < n)  			//若 i 为叶子结点则退出循环
{
if (j + 1 < n && a[j + 1] > a[j]) //在左右孩子中找最小的  ,若是构建最小堆,第二个大于号变成小于号
j++;

if (a[j] <= temp)  		//若是构建最小堆,小于号变成大于号
break;

a[i] = a[j];     //把较小的子结点往上移动,替换它的父结点
i = j;
j = 2 * i + 1;  //继续往下找出结点i 的左子节点
}
a[i] = temp;
}

void MakeMaxHeap(int a[], int n)
{
int i;
for( i = n / 2 - 1; i >= 0; i--)  //从最后一个有叶子的结点 开始往根结点A[0],调整
{
MaxHeapFixdown(a, i, n);
}
}


4.对堆的排序

最小堆排序后得到的数组是 从大到小的。

最大堆排序后得到的数组是 从小到大的。

思路:若堆有 n 个元素。从A[0]入手,把A[0] 跟堆的最后一个元素A[n-1]互换,然后不管A[0]了,(因为构成了最大堆后,A[0]一定是堆中最大的),然后堆的总长度减去一,变成 n-1,然后继续A[0]跟最后一个元素(A[n-2])互换, 跟插入排序有点相似。

代码:

void Sort(int a[], int n)
{
int i;
for ( i = n - 1; i >= 1; i--)
{
swap(&a[i], &a[0]);    //a[0]和最后一个元素交换
MaxHeapFixdown(a, 0, i);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息