本博文内容为堆排序的实现,以及堆排序的相关笔试题,如求数组的topk元素,优先队列实现等
1.堆排序的实现
原理:堆排序是基于二叉树实现的,主要步骤为:
先建立二叉堆(堆元素从 n/2 - - 1 执行向上调整操作) (二叉堆分为小根堆与大根堆),再不断执行以下步骤:将根元素与根底元素互换,且将未排序的堆总元素减一,并执行将根顶元素向下调整操作,直到未排序堆总元素为0
具体代码如下:
//
// 实现小根堆的相关操作
/
bool isLess(int a, int b) {
return a<b;
}
void exch(int &a, int &b) {
int temp=a;
a=b;
b=temp;
}
//向上调整函数
void shiftup(int *data, int pos){
while(pos/2>=1) //pos/2>=1要注意
{
if(isLess(data[pos/2], data[pos]))
break;
exch(data[pos/2], data[pos]);
pos=pos/2;
}
}
//向下调整函数
void shiftdown(int *data, int pos, int n){
while(2*pos<=n){ //data[0]代表数组的元素个数,2*pos<=n细节要注意
int k=2*pos;
if(k<n && isLess(data[k+1], data[k])) k++; //k<n要注意
if(isLess(data[pos], data[k])) break;
exch(data[pos], data[k]);
pos=k;
}
}
//堆排序(降序排序)
void heap_sort(int *data){
int i=0;
for(i=data[0]/2; i>=1; i--)
shiftup(data, i);
int n = data[0];
while(n>=1){ //n>=1要注意
exch(data[1], data[n]);
shiftdown(data, 1, --n); //--n要注意
}
}
2. 优先队列(根顶元素为最小值)的实现
优先队列的实现原理是利用了二叉堆的性质,一般至少包括2个操作:
插入元素:insert(先判断插入后元素的个数(n+1)是否超过优先队列的最大个数,在将data[++n]=val,并将该元素进行向上调整操作)
返回队列的最小值(或最大值): extractmin(首先判断优先队列中是否有元素(n-1<0),再保存优先队列的首元素,将最后一个元素与第一个元素交换,并将元素个数减一,最后对优先队列首元素执行向下调整操作)
具体代码:
///
// 实现优先队列的相关操作
///
class prior_queue{
public:
prior_queue(int m); //构造函数
void insert(int val); //插入元素
int extractmin(); //取优先队列的最小值
~prior_queue(); //析构函数
private:
int *m_data;
int maxsize;
int n;
private:
void shiftdown(int pos); //向下调整
void shiftup(int pos); //向上调整
void exch(int &a, int &b) { int temp=a; a=b; b=temp; }
};
prior_queue::prior_queue(int m){
m_data = new int[m+1];
maxsize=m;
n=0;
}
prior_queue::~prior_queue(){
delete[] m_data;
}
void prior_queue::shiftdown(int pos){
while(2*pos<=n){
int k=2*pos;
if(k<n && m_data[k+1]<m_data[k]) k++;
if(m_data[pos]<m_data[k]) break;
exch(m_data[pos], m_data[k]);
pos=k;
}
}
void prior_queue::shiftup(int pos){
while(pos/2>=1){
if(m_data[pos/2]<m_data[pos]) break;
exch(m_data[pos], m_data[pos/2]);
pos=pos/2;
}
}
void prior_queue::insert(int val){
n = n+1;
if(n>maxsize) return ;
m_data[n]=val;
shiftup(n);
}
int prior_queue::extractmin(){
if(n-1<0) return 0;
int min=m_data[1];
exch(m_data[1], m_data[n--]);
shiftdown(1);
return min;
}
3.输出数组中的top_max_k元素(或者top_max_k)
实现原理:建立一个k个元素的小根堆,来输出数组中的最大的k和元素(或者大根堆,来输出数组中的最小的k的元素)
先对已知数组前k个元素建立一个k小根堆,再将数组的元素(下标从k+1 - - n)与小根堆根顶元素比较,大则交换,并执行向下调整操作,直至遍历完数组中的全部元素
具体代码:
void top_max_k(int *data, int k){
int i=0;
for(i=k/2; i>=1; i--)
shiftup(data, i);
i=k+1;
while(i<=data[0]){
if(data[i]>data[1]){
exch(data[i], data[1]);
shiftdown(data, 1, k); //shitdown见堆排序中的shiftdown函数
}
i++;
}
}