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

C语言算法系列:堆排序

2013-10-29 21:45 211 查看
我发现自己把数据结构和算法里的很多内容都忘了,今天面试的时候面试官问我堆排序,我居然没有完整地回答出来。看来这些基础知识还是很重要的,所以我打算从今天开始复习,为以后找工作做准备。

总结一下算法的主要思想,不能下次别人问的时候又不知道了。以从小到大排序为例,堆排序的重点分为两步:

1)建立一个初始的大顶堆;

2)现在最大的元素在堆顶arr[0],将它和最末尾的元素arr[size]交换位置,这样最大的元素arr[0]就移动到了数组的最末尾。再将大顶堆的规模size减一,对前size个元素继续建大顶堆,那么第二大的元素到了堆顶arr[0],将它与arr[size]交换位置,这样第二大的元素就移动到了数组的倒数第二末尾...重复这个过程,直到所有元素都排好序。

那么难点就在怎么样建造这个大顶堆呢?我们构造maxheapify这个函数,它的作用就是:对于i∈[size/2-1, 0],比较某个元素i和它的两个子节点2i+1, 2i+2的关系,在这三个节点不满足大顶堆的性质时进行上下节点的位置调整,以保持大顶堆的性质。如果发生了元素位置的调换,那么可能调换部分的子树也需要重新调整,所以递归地调用maxheapify,从上往下逐步调整为大顶堆。

本算法用C语言根据《算法导论(第二版)》中的思想写成。

#include "iostream"

using namespace std;

void swap(int *a, int *b);
void maxheapify(int arr[], int root, int size);
void heapsort(int arr[], int size);
void heapbuild(int arr[], int root, int size);

int arrsize = 11;

int main()
{
int  arr[11] = {-4,4,27,-10,5,9,61,90,354,87,3};
heapsort(arr, arrsize);
for(int i = 0; i < arrsize; i++)
cout << arr[i] << " ";
getchar();

}

void swap(int &a, int &b)
{
int temp;
temp=a;
a=b;
b=temp;
}

void maxheapify(int arr[], int root, int size)
{
int lchild = root * 2 + 1;
int rchild = root * 2 + 2;
int largest = root;

if(arr[root] < arr[lchild] && lchild <= size)
{
largest = lchild;
}
if(arr[largest] < arr[rchild] && rchild <= size)
{
largest = rchild;
}
if(largest != root)
{
swap(arr[largest], arr[root]);
maxheapify(arr, largest, size);
}

}

void heapbuild(int arr[], int root, int size)
{
for(int i = size/2 -1; i >= 0; i--)
{
maxheapify(arr, i, size);
}
}

void heapsort(int arr[], int size)
{
heapbuild(arr, 0, size);
for(int i = size-1; i >= 2; i--)
{
swap(arr[0],arr[i]);
heapbuild(arr, 0 , i-1);
}
swap(arr[0],arr[1]);
}


进阶知识:一道常见的面试题,在n个数中找出最大的m个数。当m<<n时,这个问题就可以用堆排序来做。

先对前m个数建一个小顶堆,对于剩下的数i(i∈[m+1,n])进行遍历:如果i大于堆顶元素,那么将i与堆顶的元素互换,重新调整小顶堆。

在遍历完成后,这个堆中剩下的m个数就是最大的m个数啦。

算法的关键就是维护这个小顶堆,找出当前较大的m个数,且保持“最危险的优势”的那个元素,即堆里最小的那个元素在堆顶,便于下一步的比较。

算法复杂度是O(nlgm)。遍历O(n),建堆O(lgm)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: