大小堆排序
堆这种数据结构定义比较简单,大根堆就是父节点的值大于左右子孩子节点的值(小根堆相反),而且利用数组下标就可以很好的表现堆(不过要注意是从0 还是 1开始)。堆常用语实现优先队列,Top K等问题。
算法导论第6章对堆的进行了详细讲解,我就不赘述了(看书是不够的,要把思路用代码实现出来才是真的懂了,争取把算法导论上常用的数据结构和算法都自己实现一般)。
大根堆
具体代码(按照算法导论中下标从1开始):
/******* 大小堆排序 & topk问题 ***********/
//调整大根堆(大堆化),数组从1下标开始
void adjust_maxheap(int a[],int size,int i)
{
int left = i*2; //左节点
int right = i*2+1; //右节点
int max_index;
//找根,左,右中最大的节点
if(left < size && a[left]>a[i])
max_index = left;
else
max_index = i;
if(right < size && a[right] > a[max_index])
max_index = right;
//如果原来的根不是最大值,则需要交换,交换后原来max值的节点变成了比较小的根
if( i != max_index )
{
SWAP(a[i],a[max_index]);
adjust_maxheap(a,size,max_index);
}
}
//建立大根堆
void build_maxheap(int a[],int size)
{
int start = size>>1; //第一个非叶子节点
int i ;
for(i = start;i>=1;i--)
{
adjust_maxheap(a,size,i);
}
}
//大堆排序
void maxheap_sort(int a[],int size)
{
int i;
for(i = size;i>=1;i--)
{
build_maxheap(a,i);
SWAP(a[1],a[i]);
}
}
测试代码与运行截图:
小根堆
具体代码(下标从0开始):
void adjust_minheap(int a[],int size,int i)
{
int left = i*2+1;
int right = i*2+2;
int min_index;
if( left < size && a[left]<a[i])
min_index = left;
else
min_index = i;
if( right < size && a[right]< a[min_index])
min_index = right;
if( min_index != i)
{
SWAP(a[i],a[min_index]);
adjust_minheap(a,size,min_index);
}
}
void build_minheap(int a[],int size)
{
int start = size>>1;
for(start;start>=0;start--)
{
adjust_minheap(a,size,start);
}
}
void minheap_sort(int a[],int size)
{
int i,j=size,last= size -1;
for(i = 0;i<size;i++)
{
build_minheap(a,j--);
SWAP(a[0],a[last]);
last -= 1;
}
}
测试代码与运行截图:
运用堆排序解决Top K问题
top k问题就是在一堆数据中选择前K大(前K小)的数据。做法有许多,可以先把所有数据排序,然后选前k个。
然后用堆排序解决Top K问题则不用先全部排序,只需维护一个大小为K的堆即可。
实现思路:
如果要选出前K大,则将数据中的前K个元素建立成一个小根堆,从第K+1个元素开始往后依次比较,如果元素大于小根堆的堆顶,那么就和堆顶交换,交换后重新调整为小根堆。这样变量一遍所有数据,最后得到的大小为K的小根堆就是前K大的树。
具体代码:
void topk_biggest(int a[],int size,int k)
{
int i;
for(i = k;i<size;i++)
{
build_minheap(a,k);
if(a[i]>a[0])
SWAP(a[i],a[0]);
}
printf("最大%d个数为:",k);
for(i =0;i<k;i++)
printf("%d ",a[i]);
}