优先队列 之 堆排序实现(堆排序思想)

来源:互联网 发布:mysql 表触发器 编辑:程序博客网 时间:2024/06/17 02:33

/* 

 1、接上一篇文章,优先队列 之 插入排序 这里是用到了堆排序的思想,而非完全的堆排序的步骤;正如二分思想,他是一种思想,不要
    停留在一种方法上、一种数据结构上。

  2、poj 2833,因为优先队列是由最小(大)堆实现的,时间复杂度O(n*logn)----正好服务优先队列的要求,保证队首(栈顶)
元素的优先级最高,其他元素的优先级顺序不考虑。

  3、数据结构和算法的完美结合,第一次尝试到了两者合力是如此的完美,正如优先队列用到了:

(1):元素出队列,相当于栈顶和最后一个元素交换后,向下调整的过程;(2):元素入队列,相当于向队尾增加一个元素,向上调整的过程。

  4、下面并未体现出优先级的顺序,而是用数字的大校表示优先级,当然可以建立一个结构体struct { weight     key },再加上一个template<class T>写成模板类

*/

#include <iostream>using namespace std;const int maxn = 100;void my_swap(int &a, int &b){    int tmp;    tmp = a;    a = b;    b = tmp;}//void heap_sort(int arr[],const int &len);void heap_sort(int arr[],int &len);void heap_down_adjust(int arr[],int i,int &len);void heap_up_adjust(int arr[],int &len);void heap_init(int arr[],int &len);bool de_heap_queue(int arr[],int &len);void en_heap_queue(int arr[],int &len,const int &key);int main(){    int i,len,t_len;// 数组的有效长度    int arr[maxn],tmp;    len = 0;    cout << "请输入一个序列(-1结束):" << endl;    while(cin >> tmp)    {        if(tmp == -1)            break;        arr[len] = tmp;        len++;    }    t_len = len;    heap_init(arr,len);//调用建立初始堆    cout << "插入一个元素:" << endl;    cin >> tmp;    en_heap_queue(arr,len,tmp);    cout << "按队列的优先顺序出队:" << endl;    do    {        cout << "堆顶元素:" << arr[0] << " " << len << endl;    }while(de_heap_queue(arr,len));    return 0;}// 堆排序void heap_sort(int arr[],int &len){    int i;    for(i=len/2-1; i>=0; --i)// 至于为什么是从i = len/2 -1开始,自己建立一个完全二叉树的数组即可发现规律。    {        heap_down_adjust(arr,i,len);    }// 建立一个初试堆    //以0 和 len-1为下标的元素交换,之后从0下标开始调整堆,直到剩下一个元素    for(i=len-1; i>0; --i)    {        my_swap(arr[0],arr[i]);        heap_down_adjust(arr,0,i);// i 代表当前的数组长度    }}// 建立一个初始堆void heap_init(int arr[],int &len){    int i;    for(i=len/2-1; i>=0; --i)// 至于为什么是从i = len/2 -1开始,自己建立一个完全二叉树的数组即可发现规律,len是数组长度。    {        heap_down_adjust(arr,i,len);    }// 建立一个初试堆(大顶堆)}// 数组,调整位置,数组长度void heap_down_adjust(int arr[],int i,int &len){    int lc;// 左孩子    for(; i*2+1<len; i=lc)    {        lc = i*2+1;// 从左孩子开始        if(lc+1<len && arr[lc+1]>arr[lc])//如果右侧孩子大,加一            lc++;        if(arr[lc]>arr[i])//较大的孩子节点大于父节点            my_swap(arr[lc],arr[i]);        else            break;//父节点是最大的无需向下调整    }}// 数组,调整位置,数组长度,插入新元素,向上调整;比向下调整简单,//因为他需要判断左右孩子的最大值与最后一个值(因为是最后一个元素和零号元素交换的)void heap_up_adjust(int arr[],int i,int &len){    int parent;    for(i=len-1;i>0;i=parent)    {        parent = (i-1)/2;        if(arr[i]>arr[parent])            my_swap(arr[i],arr[parent]);        else// 无需再向上调整            break;    }}// 删除优先队列的队首元素bool de_heap_queue(int arr[],int &len){    if(len<=1)        return false;    len--;// 相当于删除了,数组从0开始,所以Len-1才是最后一个元素    my_swap(arr[0],arr[len]);    heap_down_adjust(arr,0,len);    return true;}//插入队尾元素void en_heap_queue(int arr[],int &len,const int &key){    arr[len] = key;    len++;    heap_up_adjust(arr,len,len);}

改进之后的swap()函数,异或的好处,以及 位移 之 右移 >>(相当于除以2) 左移 << ( 相当于乘以2 )。

void my_swap(int &a, int &b)
{
        a = a ^ b;
b = a ^ b;
a = a ^ b;
}


/*

基本函数说明及复杂度分析如下:

  heap_down_adjust() 从第一个不满足堆的结点s开始向下做调整,使得维护一个堆,删除栈顶元素或者建立初始堆。

  --------log(n)

heap_up_adjust() 从最后一个结点s开始向上做调整,使得维护一个堆,插入新元素的过程。

  --------log(n)

  en_heap_queue() 从尾部增加一个叶子节点到堆中,因为原本是满足堆的,所以只需要从下至上不断跟父结点进行交换,直至已满足堆退出,调用的heap_up_adjust() 

  --------log(n)

  de_heap_queue() 把start跟end做交换,然后[start,end-1]从新做一次调整,使得[start,end-1]维护成一个堆,堆大小length随之-1,调用的heap_down_adjust() 。

  --------log(n)

  heap_init() 从第一个非终端结点开始->根节点不断做heap_adjust(),使之构成一个初始堆。

  --------n / 2 * log(n)

  heap_sort()  相当于重复n次de_heap_queue()操作,但length不改变。

  --------n * log(n)

  用类模板实现,参照 :http://www.cnblogs.com/slave_wc/archive/2010/12/23/1915293.html

*/


0 0