堆排序算法是复杂的排序算法,是不稳定的排序算法。
1、堆排序的基本思想
堆排序定义:n个有序列A1,A2,...,An成为堆,有下面两种不同类型的堆。
大根堆:所有子节点都大于其父节点,即Ai<=A2i且Ai<=A2i+1。
小根堆:所有子节点都小于其父节点,即Ai>=A2i且Ai>=A2i+1。
若将此序列所存储的向量A[1...n]看为一颗完全二叉树的存储结构,则对实质上是满足如下性质的完全二叉树:树中任一非叶子结点的关键字均不大于(或者不小于)其左、右子节点(若存在)的关键字。
因此堆排序(HeapSort)是树形选择排序。在排序过程中,将R[1...n]看成一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲节点和孩子节点之间的内在的关系,在当前无序区中选择关键字最大的(最小的)记录。
用大根堆排序的基本思想:
(1) 先将初始A[1...n]建成一个大根堆,此堆为初始化无序区。
(2) 再将关键字最大的记录A[1](堆顶)和无序区的最后一个记录A[n]交换,由此得到新的无序区A[1...n-1]和有序区A[n],且满足A[1...n-1] <= A[n].
(3) 由于交换后新的根A[1]可能违反堆性质,所以将当前无序区A[1...n-1]调整为堆。然后再次将A[1...n-1]中关键字最大的记录A[1]和该区间的最后一个记录A[n-1]交换,由此得到新的无序区A[1...n-2]和有序区A[n-1...n],且仍满足关系A[1...n-2] <= A[n-1...n],同样要将A[1...n-2]调整为堆。
(4) 对调整的对重复进行上面的交换,直到无序区只有一个元素为止。
2、思想
利用完全二叉树中双亲节点和孩子节点之间的内在关系,在当前无序区中选择关键字最大(或者最小)的记录。也就是说,以最小堆为例,根节点为最小元素,较大的节点偏向于分布在堆底附近。
3、算法复杂度
最坏情况下,接近于最差情况下:O(N*logN),因此它是一种效果不错的排序算法。
4、稳定性
堆排序需要不断地调整堆,因此它是一种不稳定的排序!
5、代码实现
//编程实现堆排序
//堆排序是较为复杂的排序。
//1.首先是将整个数组进行一个初始化堆操作,是将所有的节点进行堆化(所有的父节点都是自己左右子节点中最大的)
//2.进行堆化之前首先是记录一下堆化的大小(heapSize=len),确定整个数组最后一个父节点(i=len>>1),从最后一个父节点进行遍历到0最顶的父节点(i>=0,i--)
//3.堆化中,首先确定左右子节点索引,largest记录的是左右节点中最大元素的索引,前提条件为:左右子节点索引小于heapSize的(不然没有意义)
//4.先比较左节点与父节点array[left] > array[index]找出最大值赋值给largest,再比较array[right] > array[largest]找到三者最大值
//5.若父节点不是最大值,则交换父节点与largest的位置,在递归进行堆化。直到整个二叉树完全符合大根堆
//6.完成堆化之后,交换最后一个数与堆顶的位置,堆大小-1,在重新在最顶端进行堆化(maxHeapify(array,0))
//7.重复3-7步骤,完成堆排序。
#include<iostream>
using namespace std;
static int heapSize = 0;
//返回左子节点索引
int Left(int index) { return (index << 1) + 1; }
//返回右子节点索引
int Right(int index) { return (index << 1) + 2; }
//交换a、b的值
static void swap(int *a, int *b) { int temp = *a; *a = *b; *b = temp; }
//array[index]与其左、右子树进行递归对比
//用最大主替换array[index],index表示堆顶索引
void maxHeapify(int array[], int index)
{
int largest = 0; //最大值索引
int left = Left(index); //左子节点索引
int right = Right(index);//右子节点索引
//把largest复位堆顶与其左子节点的较大者
if ((left <= heapSize) && (array[left] > array[index]))
{
largest = left;
}
else
{
largest = index;
}
//把largest与堆顶的柚子节点比较,去较大者
if ((right <= heapSize) && (array[right] > array[largest]))
{
largest = right;
}
//此时largest为堆顶、左子节点、右子节点三者中最大者
if (largest != index)
{
//如果堆顶不是最大者,则交换,并递归调整堆
swap(&array[index], &array[largest]);
maxHeapify(array, largest);
}
}
//初始化堆,将数组中的每一个元素只放到适当的位置
//完成之后,最顶的元素为数组的最大值
void buildMaxHeap(int array[], int len)
{
int i;
heapSize = len; //堆大小赋为数组长度
for (i = (len >> 1); i >= 0; i--)
{
maxHeapify(array, i);
}
}
void heap_sort(int array[], int len)
{
int i;
//初始化堆
buildMaxHeap(array, (len - 1));
for (i = (len - 1); i >= 1; i--)
{
//堆顶元素array[0](数组的最大值)被置换到数组的尾部array[i]
swap(&array[0], &array[i]);
heapSize--; //从堆中移除该元素
maxHeapify(array, 0); //重建堆(只有array[0]与array[i]交换,所以重建堆从0开始)
}
}
void print_array(int a[], int length)
{
for (int i = 0; i < length; i++) //打印数据
{
cout << a[i] << " ";
}
}
void main9mianshiti6()
{
int a[] = { 45, 68, 20, 39, 88, 97, 46, 59 };
cout << "before heap sort:";
print_array(a, 8);
heap_sort(a, 8);
cout << "\nafter heap sort:";
print_array(a, 8);
system("pause");
}
6、测试结果
before heap sort:45 68 20 39 88 97 46 59
after heap sort:20 39 45 46 59 58 88 97