堆排序
堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排
序的一种。可以利用数组的特点快速定位制定索引的元素。堆分为大根堆和小根堆,是完全二叉
树。大根堆的要求是每个结点的值都不大于其父节点的值。在数组的非降序排序排序中,需要使
用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。
特点:
堆排序(HeapSort)是一树形选择排序。堆排序的特点是:在排序过程中,将R[l...n]看成是
一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系,在当
前无序中选择关键字最大(或最小)的记录。
1.小根堆
若根结点存在左子女则根结点的值小于左子女的值;若根结点存在右子女则根结点的值小于
右子女的值。
2.大根堆
若根结点存在左子女则根结点的值大于左子女的值;若根结点存在右子女则根结点的值大于
右子女的值。
3.结论
1)堆是一棵完全二叉树
2)小根堆的根结点的值是最小值,大根堆的根结点的值是最大值。
3)堆适合与采用顺序存储。
4.堆的插入算法
将一个数据元素插入到堆中,使之依然称为一个堆。
算法描述:先将结点插入到堆的尾部,再将该结点逐层向上调整,直到依次构成一个堆,
调整方法是看每个子树是否符合大(小)根堆的特点,不符合的话则调整叶子和根的位置。
5.堆的删除算法
堆在删除元素时,只可以删除根结点。
算法描述:将根结点删除后用堆尾结点进行填补,调整二叉树,使之依然成为堆。
6.基本思想
1)根据初始数组去构造初始堆(构建一个完全二叉树,保证所有的父节点都比它的孩子结点
数值大)。
2)每次交换第一个和最后一个元素,输出最后一个元素(最大值),然后把剩下的元素重新
调整为大根堆。当输出到最后一个元素后,这个元素已经是按照从大到小的顺序排列了。
大根堆的实现:
#include<string>
#include<map>
#include<iostream>
#include<sstream>
using namespace std;
void PercDown(int A[],int i,int N) //i:每个非叶子结点,N数组长度
{
int child = 0;
int Tmp;// 用于保存当前根结点
for(Tmp = A[i];(i*2+1)<N;i=child)//一个循环是因为更新完当前节点后还要看之后的节点是否满足要求
{
child = i*2+1;
if(child != N-1 && A[child+1]>A[child])
{
child++;//选择出子结点中最大的
}
if(Tmp <A[child])
{
A[i] = A[child];//将当前节点由比较大的子节点进行替换
}
else
break;
}
A[i] = Tmp;//i 是对应的最后的孩子节点的位置
}
int main()
{
int A[9] = {3,8,2,7,9,26,28,54,20};
int N = 9;
int i;
for(i=N/2;i>=0;i--)//每个非叶子结点,只需要验证非叶子结点,从下面层进行比较
{
PercDown( A, i, N);//i 表示当前需要验证的根结点,N 表示数组长度
}
for(i=N-1;i>0;i--)//这里的i表示数组的长度,因为每一次输出一个数据到vector最后面,树就减少1
{
swap( A[0], A[i]);
PercDown(A,0,i);//因为每次从堆顶端进行输出,所以每一次验证需要从0开始
}
for(i=0;i<N;i++)
cout<<A[i]<<" ";
cout<<endl;
}
小根堆实现:
#include<string>
#include<map>
#include<iostream>
#include<sstream>
using namespace std;
void PercDown(int A[],int i,int N)//i:每个非叶子结点,N数组长度
{
int child = 0;
int tmp = A[i];
for(;(i*2+1)<N;i = child)
{
child = 2*i +1;
if((child<(N-1))&&(A[child]>A[child+1]))//N-1 是因为还有结点
{
child++;
}
if(tmp >A[child])
{
A[i] = A[child];
}
else
break;
}
A[i] = tmp;
}
int main()
{
int A[9] = {3,8,2,7,9,26,28,54,20};
int N=9;
int i;
for(i=N/2;i>=0;i--)//每个非叶子结点,只需要验证非叶子结点
{
PercDown(A,i,N);//表示当前需要验证的根结点,N表示数组长度
}
for(i=N-1;i>0;i--)//这里的i表示数组的长度,因为每一次输出一个数据到vector最后面,树就减少1
{
swap(A[0],A[i]);//注意传入的是i是数组的长度,所以后面的判断是N-1
PercDown(A,0,i);//因为每次从堆顶端进行输出,所以每一次验证需要从0开始
}
for(i=0;i<N;i++)
cout<<A[i]<<" ";
cout<<endl;
return 0;
}
其他网友实现大根堆:
#include <stdio.h>
/*
注意:这个函数只会在调整被交换的位置为大根堆,未交换的分支不会处理,
所以不能将一个非大根堆二叉树的根结点传递过来让这个函数为大根堆
*/
void heap_ajust(int *a,int i,int size)
{
int lchild = 2*i +1;//i的左孩子结点序号
int rchild = 2*i +2;//i的右孩子结点序号
int max = i;/* 存放三个顶点中最大的数的下标*/
int temp;
if(i <= (size -1)/2)//如果i是叶节点就不用进行调整
{
if(lchild<size && a[lchild]>a[max])
{
max = lchild;
}
if(rchild<size && a[rchild]>a[max])
{
max = rchild;
}
if(max != i)
{
temp = a[i];/*交换a[i]和a[max]的值*/
a[i] = a[max];
a[max] = temp;
heap_ajust(a, max, size);
/*被交换的位置以前是大根堆,现在可能不是大根堆,所以需要重新调整使其成为大根堆
结构 */
}
}
}
void build_bheap(int *a,int size)//建立大根堆
{
int i;
for(i=(size-1)/2; i>=0; i--)//非叶子点最大序号值size/2
{
heap_ajust(a,i,size);//每个非叶子结点都需要调用这个函数
}
}
void heap_sort(int *a,int size)
{
int i;
int temp;
build_bheap(a, size);//创建大根堆
for(i=size; i>=1;i--)//以数组长度为单位
{
temp = a[0];
a[0] = a[i-1];
a[i-1] = temp;//交换堆顶和最后一个元素,即每次将剩余元素中的最大者放到最后面
heap_ajust(a,0,i-1);//重新调整堆结点称为大根堆,只有被交换的分支才有可能不是大根堆
}
}
int main(int argc,char **argv)
{
int a[] ={0,16,20,3,11,17,8};
int size = sizeof(a)/sizeof(int);
int i;
printf("size = %d
",size);
heap_sort(a,size);
printf("Sort over: ");
for(i=0;i<size;i++)
printf("%d ",a[i]);
printf("
");
return 0;
}