堆排序

堆:

#include <cstdio>
#include <iostream>
using namespace std;
const int N = 100010;
int n,m,h[N],s;
void down(int u)
{
    int t = u;
    if(u * 2 <= s && h[u*2] < h[t]) t = 2 * u;
    if(u * 2 + 1 <= s && h[u * 2 + 1] < h[t]) t = 2 * u + 1;
    if(u != t)
    {
        swap(h[u],h[t]);
        down(t);
    }
}

int main()
{
    cin>>n>>m;
    for(int i = 1;i <= n;i++) cin>>h[i];
    s = n;
    for(int i = n/2;i;i--) down(i);
    
    while(m--)
    {
        cout<<h[1]<<" ";
        h[1] = h[s--];
        down(1);
    }
}

STL当中有优先队列:priority_queue。

本题:输入一个长度为n的整数数列,从小到大输出前m小的数。是用小根堆,手写堆,根小于等于左右子结点,堆顶是最小值。

存储:放在一个一维数组里面,根节点为1(x),左子结点为2(2 * x),右子结点为3(2 * x + 1). 1 2 3 4 5 6 7 8 9 ……

down(x):当把值变大时,需要将他的位置移动到合适的地方,下移。

up(x):当把值变小时,需要将他的位置移动到合适的地方,上移。

插入一个数:heap[++size] = x, up(x);

求出最小值:heap(1);

删除最小数:因为存储方式是一维数组,删最后的方便,只需要size--即可,头中间不好删,所以先将最后的数替换到第1个结点,heap(1) = heap[size]; size--;down(1);

删除任意一个元素: heap[k] = heap[size--]; down(k);

修改任意元素:heap[k] = x; up(x) OR down(x);

本题是默认先把数组里面所有的元素放在一维数组,然后对应到二叉堆里面,然后递归循环通过down操作来使得每个元素归位放在合适的位置。

for(int i = n/2 ;i ; i--) down(i); 因为对于一个满二叉树来说,最后一层个数为n/2,根节点到倒数第二层的总数也为n/2,下移的层数为第一层到倒数第二层,个数为n/2。

这里操作的次数小于n: S = n/4 + 2 * n/ 8 + 3 * n/16 + ……  2S = n/2 + 2 * n /4 + 3 * n / 8 …… 后面减去前面的S = n/2 + n/4 +n/8 + …… = n * 1/2 (1 - 1/(2^n)/(1-1/2) = n * (1 - 1 / 2 ^ n) < n; 所以总的down次数为小于O(n)。

 此题还可以用STL的有限队列来写:

priority_queue<int , vector<int>, greater<int> > q;

#include <iostream>
#include <queue>
using namespace std;
const int N = 100010;
int a[N];
priority_queue<int, vector<int>, greater<int> >q;//小根堆,less是大根堆,可以认识是根以下的更小,根最大所以是大根堆,greater同理
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i = 0;i<n;i++) {
        cin>>a[i];
        q.push(a[i]);
    }
    while(m--)
    {
        cout<<q.top()<<" ";
        q.pop();
    }
    return 0;
}

 模拟堆:

维护一个集合,初始时集合为空,支持如下几种操作:

  1. “I x”,插入一个数x;
  2. “PM”,输出当前集合中的最小值;
  3. “DM”,删除当前集合中的最小值(数据保证此时的最小值唯一);
  4. “D k”,删除第k个插入的数;
  5. “C k x”,修改第k个插入的数,将其变为x;
#include <cstdio>
#include <iostream>
#include <string.h>

using namespace std;
const int N = 100010;
int h[N],ph[N],hp[N],s;//ph[k]第k个插入的点,hp[k]堆上对应的ph[]数组。 ph[j] = k; hp[k] = j;
void heap_swap(int a, int b)
{
    swap(ph[hp[a]], ph[hp[b]]);
    swap(hp[a],hp[b]);
    swap(h[a],h[b]);
}
void down(int u)
{
    int t = u;
    if(u * 2 <= s && h[u*2] < h[t]) t = 2 * u;
    if(u * 2 + 1 <= s && h[u * 2 + 1] < h[t]) t = 2 * u + 1;
    if(u != t)
    {
        heap_swap(u,t);
        down(t);
    }
}

void up(int u)
{
    while(u / 2 && h[u / 2] > h[u])
    {
        heap_swap(u/2, u);
        u /= 2;
    }
}

int main()
{
    int n, m = 0;
    cin>>n;
    while(n--)
    {
        char op[10];
        int k,x;
        scanf("%s",op);
        if(!strcmp(op,"I"))
        {
            cin>>x;
            s++;
            m++;
            ph[m] = s;
            hp[s] = m;
            h[s] = x;
            up(s);
        }
        else if(!strcmp(op,"PM"))
        {
            cout<<h[1]<<endl;
        }
        else if(!strcmp(op, "DM"))
        {
            heap_swap(1, s);
            s--;
            down(1);
        }
        else if(!strcmp(op, "D"))
        {
            cin>>k;
            k = ph[k];
            heap_swap(k, s);
            s--;
            down(k), up(k);
        }
        else{
            cin>>k>>x;
            k = ph[k];
            h[k] = x;
            down(k), up(k);
        }
    }
}

 

posted @ 2020-04-13 17:51  龙雪可可  阅读(153)  评论(0编辑  收藏  举报
****************************************** 页脚Html代码 ******************************************