假期学习第一步之......学习堆排序

堆:分为小根堆和大根堆,就是说堆根的元素的关键字最小(最大);
堆排序:Heapsort;
时间复杂度:O(nlogn);
优点:当题目描述只是说输出第K大或者是最大的数据的时候比快排有优势。快排的时间复杂度为O(nLogn),但是如果是堆排序的话时间复杂度为O(klogn),如果k<<n那么还是选择堆      排序;
步骤:
1>将待排序size个数据存储在数组内。如果下标2*i<size,那么2*i为i的左子树;如果2*i+1<size,那么2*i+1为i的右子树;
2>建立初始堆。从最后一个非叶子节点开始调整,就是size/2。如果是想建小根堆的,建的初始堆堆根元素最大;如果是想建大根堆,初始堆堆根元素最小。
3>堆排序。对于第i趟堆排序,调整堆根元素和第n-i+1结点交换,保证n-i+1~n-1是有序的。
参考资料:http://www.cnblogs.com/dolphin0520/archive/2011/10/06/2199741.html

堆的应用:

(一)哈夫曼树:POJ 3253

(二)"和并项" POJ 2442 Sequence 

连接:http://poj.org/problem?id=2442

题目大意:给出m行数,每行n个数。从每行中选出一个数做和。那么有n^m个值,最后输出n个最小的和。 

解题思路:
  如果从最暴力的方法思考,枚举每一行的每一个数和接下来的每一行的任意一个数求出最小的n个和,可定会超时的。
  但是其实有用的就是最小的n个数,那么依次枚举该行和前面所有行的情况中的最小的n个数的和的话,枚举的数据量会减少很多的。

  1>第一组数据存在data1[n]中,给他排序
  2>第二组数据存在data2[n]中,给他排序
  3>求出data2[0]与data1[]中数据的和,存在数组dataq[]中。给dataq[]从小到大排序。
  4>依次枚举data2[1]~data2[n]与data1[]作和。如果比 dataq[n-1]大,break; 否则dataq[n-1]=他们的和,重新排序dataq[];
  5>将dataq[]给data1[]
  6>重复2>-5>过程
  7>输出data1[]就是想要的结果

扩展:STL中有一个heap.
  1>make_heap(heap, heap+n, cmp); 默认的情况下是将把“最大”的元素排列到首位,而其他元素看上去并非有序
  2>sort_heap(heap, heap+n, cmp); 进行堆排序
  3>pop_heap(heap, heap+n, cmp); 并不是将最大(最小元素弹出),而是将frist与last交换,在将frist与last-1做成一个堆
  4>push_heap(heap, heap+n, cmp); 将新元素加进去后做成一个堆;//感觉和1>没啥差别

代码如下:

View Code
#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
#define N 2005
int data1[N], data2[N], dataq[N];
int main()
{
    int i, j, tt, n, m, T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d", &m, &n);
        memset(data1, 0, sizeof(data1));
        memset(data2, 0, sizeof(data2));
        memset(dataq, 0, sizeof(dataq)); 
        for(i=0; i<n; i++)
            scanf("%d", &data1[i]);
        sort(data1, data1+n);
        for(tt=0; tt<m-1; tt++)
        {
            for(i=0; i<n; i++)
                scanf("%d", &data2[i]);
            int flag=0;
            sort(data2, data2+n);
            for(i=0; i<n; i++)
                dataq[i]=data2[0]+data1[i];
            sort(dataq, dataq+n);
            for(i=1; i<n; i++)
            {
                for(j=0; j<n; j++)
                {
                    if(data2[i]+data1[j]>dataq[n-1])
                        break;
                    dataq[n-1]=data2[i]+data1[j];
                    sort(dataq, dataq+n);
                }
            }
            for(i=0; i<n; i++)
                data1[i]=dataq[i];
        }
        for(i=0; i<n-1; i++)
            printf("%d ", data1[i]);
        printf("%d\n", data1[n-1);
    }
    return 0;
}
View Code
#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
#define N 2005
int data1[N], data2[N], dataq[N];
int main()
{
    int i, j, tt, n, m, T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d", &m, &n);
        memset(data1, 0, sizeof(data1));
        memset(data2, 0, sizeof(data2));
        memset(dataq, 0, sizeof(dataq)); 
        for(i=0; i<n; i++)
            scanf("%d", &data1[i]);
        
        for(tt=0; tt<m-1; tt++)
        {
            sort(data1, data1+n);
            for(i=0; i<n; i++)
                scanf("%d", &data2[i]);
            sort(data2, data2+n);
            for(i=0; i<n; i++)
                dataq[i]=data2[0]+data1[i];
            make_heap(dataq, dataq+n);
            for(i=1; i<n; i++)
            {
                for(j=0; j<n; j++)
                {
                    if(data2[i]+data1[j]>=dataq[0])
                        break;
                    dataq[0]=data2[i]+data1[j];
                    make_heap(dataq, dataq+n);
                }
            }
            for(i=0; i<n; i++)
                data1[i]=dataq[i];
        }
        sort(data1, data1+n);
        for(i=0; i<n-1; i++)
            printf("%d ", data1[i]);
        printf("%d\n", data1[n-1]);
    }
    return 0;
}

PS:此题最后不进行严格的控制格式也能ac such as:

for(i=0; i<n; i++)
  printf("%d ", data1[i]);
printf("\n");

(三)优先队列: POJ 2051 Argus

连接:http://poj.org/problem?id=2051

题目大意:描述好难翻译。就是给出每个id 的num和id出现的间隔时间数pre。也就是说此id要间隔pre才能在出现。输出前k个id出现的次序,如果相同时间now内出现多个id,按照id 的num小的先输出。

解题思路:堆的思想用优先队列实现,有两个优先级别按照now小的优先级别高,如果now的时间相同按照num小的先输出。

扩展:优先队列重载符号,定义一个friend boo Name()函数进行设置优先级;

代码如下:

View Code
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<queue>
using namespace std;
struct ID
{
    friend bool operator < (ID x, ID y)
    {
        if(x.now!=y.now)
            return y.now<x.now;
        else
            return y.num<x.num;
    }
    int num, per, now;
};
int main()
{
    int k, t=0;
    char ch[50];
    ID id[1005];
    priority_queue<ID>Q;
    while(1)
    {
        scanf("%s", ch);
        if(strcmp(ch, "Register")==0)
        {
            scanf("%d%d", &id[t].num, &id[t].per);
            id[t].now=id[t].per;
            Q.push(id[t]);
            t++;
        }
        if(strcmp(ch, "#")==0)
            break;
    }
    scanf("%d", &k);
    while(k--)
    {
        ID temp;
        temp=Q.top();
        Q.pop();
        printf("%d\n", temp.num);
        temp.now+=temp.per;
        Q.push(temp);
    }
    return 0;
}

(四)...更新中....waiting....

原文地址:https://www.cnblogs.com/Hilda/p/2848091.html