堆排序

如何手写一个堆?

  1. 插入一个数
  2. 求集合当中的最小值
  3. 删除最小值
  4. 删除任意一个元素
  5. 修改任意一个元素

堆的基本结构。

性质:

堆是一颗完全二叉树。按照序号来的

除了最后一层,其他都是满的

每一个点都是小于等于儿子

存储

用一维数组来存

x的左儿子是2x,x的右儿子是2x+1

从1开始,0的左儿子是两倍的0

一维数组可以存一个树

在STL里面堆就是优先队列

基本操作

down(x) :

往下调整

某一个点的值改变就需要往下移

up(x):

只需要和父节点比较

插入:

是插入到最后一个 位置。然后不断往上移

删除最小值:

删除头节点很麻烦,删除尾结点很简单

插入删除都是logn

时间复杂度:O(N)算法来初始化。因为最后一排不用动

从下往上down可以保证准确性

递归可以很容易改成循环。

但是这个是logn可以不用改成循环

logn是操作的上界

#include <cstdio>
#include <iostream>
using namespace std;
const int maxn = 1e5 + 10;
int h[maxn], cnt;  // size现在的堆中有几个数字

void down(int u)
{
    int t = u;  // 表示我现在要移动去的地方
    if(2 * u <= cnt && h[t] >= h[2 * u]) t = 2 * u;
    if(2 * u + 1 <= cnt && h[t] >= h[2 * u + 1]) t = 2 * u + 1;
    if(t != u)
    {
        swap(h[t], h[u]);
        down(t);
    }
}

void up(int u)
{
    while (u / 2)  // 1 的时候就进不去了
    {
        if(h[u] < h[u / 2]) swap(h[u], h[u / 2]);
        u /= 2;
    }
}

int main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ ) scanf("%d", &h[i]), cnt ++ ;
    
    for (int i = n / 2; i; i -- ) down(i);
    
    while (m -- )
    {
        printf("%d ", h[1]);
        h[1] = h[cnt];
        cnt --;
        down(1);
    }
    
}

模拟堆

存映射关系,然后再维护映射 关系

主要是第四个和第五个操作。

pointer heap

strcmp(str1, str2)

左边小就返回负数

左边大就返回正数

相等就返回0

而且char类型的数组是不能用 == 直接比较大小的。

#include <cstdio>
#include <iostream>
#include <string.h>
using namespace std;
const int maxn = 1e5 + 10;
int h[maxn], cnt;  // cnt现在的堆中有几个数字
int hp[maxn], ph[maxn];  // ph[k]指的是第k个数所在的下标,hp[k]指的是下标为k的数,存的是第几个数

void heap_swap(int u, int v)
{
    swap(h[u], h[v]); // 最后更新才对
    swap(ph[hp[u]], ph[hp[v]]);  // 下面两个怎么调换都没问题,因为他只是交换,不论是A交换B还是B交换A。效果是一样的。
    swap(hp[u], hp[v]);  // 先交换坐标比较好,因为后面都是根据坐标来改进的
}

void down(int u)
{
    int t = u;  // 表示我现在要移动去的地方
    if(2 * u <= cnt && h[t] >= h[2 * u]) t = 2 * u;
    if(2 * u + 1 <= cnt && h[t] >= h[2 * u + 1]) t = 2 * u + 1;
    if(t != u)
    {
        heap_swap(t, u);
        down(t);
    }
}

void up(int u)
{
    while (u / 2)  // 1 的时候就进不去了
    {
        if(h[u] < h[u / 2]) heap_swap(u, u / 2);
        u /= 2;
    }
}

int main()
{
    int n, m = 0;
    cin >> n;
    char op[10];
    
    while (n -- )
    {
        int k, x;
        scanf("%s", op);
        if (!strcmp(op, "I"))
        {
            scanf("%d", &x);
            cnt ++ ;
            m ++ ;
            ph[m] = cnt;
            hp[cnt] = m, h[cnt] = x;
            up(cnt);
        }
        else if (!strcmp(op, "PM")) printf("%d\n", h[1]);
        else if (!strcmp(op, "DM"))
        {
            //h[1] = h[cnt];
            heap_swap(1, cnt); // 是heap_swap交换过去,不能直接swap
            cnt --;
            down(1);
        }
        else if (!strcmp(op, "D"))
        {
            scanf("%d", &k);
            k = ph[k];
            heap_swap(k, cnt);
            cnt --;
            down(k), up(k);
        }
        else
        {
            scanf("%d%d", &k, &x);
            h[ph[k]] = x;
            down(ph[k]), up(ph[k]);
        }
        
    }
    return 0;
}
上一篇:DP无法删除失效的多路径链路方法


下一篇:题解 CF1191A 【Tokitsukaze and Enhancement】