堆排序:
1.堆的概念
堆其实就是一颗完全二叉树,关于什么是完全二叉树在二叉树的基本概念中有说明,在这就不细说了。
2.堆的分类
堆分为小堆和大堆。
- 小堆:父节点都不大于任意直接子节点。
- 大堆:父节点都不小于任意直接子节点。
举例来说,对于n个元素的序列{R0, R1, … , Rn}当且仅当满足下列关系之一时,称之为堆:
(1) R[i] <= R[2i+1] 且 R[i]<= R[2i+2] (小根堆)
(2) R[i] >= R[2i+1] 且 R[i] >= R[2i+2] (大根堆)
其中i=1,2,…,n/2向下取整;
下图为一小堆图:
如上图所示,序列R{3, 8, 15, 31, 25}是一个典型的小根堆。
堆中有两个父结点,元素3和元素8。
元素3在数组中以R[0]表示,它的左孩子结点是R[1],右孩子结点是R[2]。
元素8在数组中以R[1]表示,它的左孩子结点是R[3],右孩子结点是R[4],它的父结点是R[0]。可以看出,它们满足以下规律:
设当前元素在数组中以R[i]表示,那么,
(1) 它的左孩子结点是:R[2*i+1];
(2) 它的右孩子结点是:R[2*i+2];
(3) 它的父结点是:R[(i-1)/2];
(4) R[i] <= R[2*i+1] 且 R[i] <= R[2i+2]。
3.实现过程
假如将堆的元素存储在一个数组中,我们可以这样理解:
- 首先,按堆的定义将数组R[0…n]调整为堆(这个过程称为创建初始堆),然后交换R[0]和R[n];
- 将R[0…n-1]重新调整为堆,交换R[0]和R[n-1];
- 如此反复,直到交换了R[0]和R[1]为止。
下面以大堆为例说明:
按以上思路可归纳为两个操作:
(1)根据初始数组去构造初始堆(构建一个完全二叉树,保证所有的父结点都不小于它的孩子结点数值)。
(2)每次交换第一个和最后一个元素,输出最后一个元素(最大值),然后把剩下元素重新调整为大根堆。
当输出完最后一个元素后,这个数组已经是按照从小到大的顺序排列了。
先通过详细的实例图来看一下,如何构建初始堆。
设有一个无序序列 { 1, 3, 4, 5, 2, 6, 9, 7, 8, 0 }。
构造了初始堆后,我们来看一下完整的堆排序处理:
还是针对前面提到的无序序列 { 1, 3, 4, 5, 2, 6, 9, 7, 8, 0 } 来加以说明。
相信通过上面两幅图大家对堆的构造和排序过程已经清楚。
4.算法实现
#include<iostream>
void swap( int* p1, int* p2)
{
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
/************************************************
function:调整堆
length:需要调整的数组元素的个数
******************************************************/
void adjustHeap(int* arr,int length)
{
if (length <= 1)
return;
for (int j = length/2; j >=0; --j)
{
//左右子节点都存在时
if (2 * j + 2 <= length-1)
{
int max_index = 0;
if (arr[2 * j + 1] < arr[2 * j + 2])//左右子节点比较
{
max_index = 2 * j + 2; //右节点的下标
}
else
max_index = 2 * j + 1; //左节点的下标
if (arr[j] < arr[max_index])
swap(&arr[j], &arr[max_index]);//交换父节点与最大子节点的值
continue;
}
//只有左子节点时
if (2 * j + 1 < length)
{
if (arr[j] < arr[2 * j + 1])
swap(&arr[j], &arr[2 * j + 1]);
}
}
}
/*********************************************************
function:排序
********************************************************/
void heap_sort(int* arr, int length)
{
if (length <= 1)
return;
adjustHeap(arr, length);//建堆
swap(&arr[0], &arr[length - 1]);//交换第一个与最后一个元素
length -= 1;
heap_sort(arr, length); //递归调用
}
//主函数
int main()
{
int arr[10] = { 1,5,85,14,56,9,45,20,30,2 };
heap_sort(arr,10);
for (int i = 0; i < 10; i++)
std::cout << arr[i] << " ";
system("pause");
return 0;
}