【刷题】数据结构——堆【模板】

堆的原理

  • 堆是一个完全二叉树

  • 小根堆:父亲比左右儿子小,根节点是最小值
    小根堆:父亲比左右儿子大,根节点是最大值

  • 编号 x x x的左儿子: 2 x 2x 2x,右儿子: 2 x + 1 2x+1 2x+1
    下标从1开始


堆的操作

堆的操作主要有两种:down、up

以小根堆为例:

down:如果编号 x x x如果变得比儿子大,则和较小的儿子进行交换,直到放到合适的位置(两儿子都比它大)。
up:如果编号 x x x如果变得比父亲小,则和父亲交换,直到放到合适的位置(比父亲大)。

// 注意输入的u要合法,不能大于size
void up(int u) {
	while (u > 1 && heap[u] < heap[u / 2]) swap(heap[u], heap[u / 2]), u /= 2;
} 
// 注意输入的u要合法,不能等于0
void down(int u) {
	int t = u;
	while (true) {
		if (u * 2 <= size && heap[u] > heap[u * 2]) t = u * 2;
		if (u * 2 + 1 <= size && heap[t] > heap[u * 2 + 1]) t = u * 2 + 1;
		if (u != t) {
			swap(heap[u], heap[t]);
			u = t;
		}
		else break;
	}
}
  1. 插入一个数
    堆末尾添加一个数,并调整
    heap[ ++ size ] = x;
    up(size);
  2. 求集合中最小值
    就是堆顶
    head[1]
  3. 删除最小值
    用堆末尾替换堆顶,再调整
    head[1] = head[ size -- ];
    down(1);
  4. 删除任意一个元素
    head[k] = head[size -- ]
    down(k)
    up(k)
  5. 修改任意一个元素
    heap[k] = x
    down(k)
    up(k)
  6. 建立堆
    for (int i = 1; i <= n ; i ++ ) head[i] = a[i]; 先给haep随便赋值
    for (int i = n / 2; i ; i -- ) down(i); O ( n ) O(n) O(n)时间进行建堆
    因为最后一层的 n / 2 n / 2 n/2个点都没有孩子,所以不需要down,故 i i i n / 2 n / 2 n/2开始
    倒数第二层有 n / 4 n / 4 n/4个点,最多需要down 1次
    倒数第三层有 n / 8 n / 8 n/8个点,最多需要down 2次
    倒数第四层有 n / 16 n / 16 n/16个点,最多需要down 3次


    1 ∗ n 4 + 2 ∗ n 8 + 3 ∗ n 16 + . . . 1 * \frac{n}{4} + 2 * \frac{n}{8} + 3 *\frac{n}{16} + ... 14n+28n+316n+...
    = n ( 1 2 2 + 2 2 3 + 3 2 4 + 4 2 5 + . . . ) =n(\frac{1}{2^2} +\frac{2}{2^3}+ \frac{3}{2^4} + \frac{4}{2^5} + ... ) =n(221+232+243+254+...)
    令S
    S = 1 2 2 + 2 2 3 + 3 2 4 + 4 2 5 + . . . S=\frac{1}{2^2} +\frac{2}{2^3}+ \frac{3}{2^4} + \frac{4}{2^5} + ... S=221+232+243+254+...
    左右乘2
    2 S = 1 2 + 2 2 2 + 3 2 3 + 4 2 4 + 5 2 5 + . . . 2S=\frac{1}{2} +\frac{2}{2^2}+ \frac{3}{2^3} + \frac{4}{2^4} + \frac{5}{2^5} + ... 2S=21+222+233+244+255+...
    上面两式相减
    2 S − S = 1 2 + 1 2 2 + 1 2 3 + 1 2 4 + 1 2 5 + . . . 2S-S=\frac{1}{2} +\frac{1}{2^2}+ \frac{1}{2^3} + \frac{1}{2^4} + \frac{1}{2^5} + ... 2SS=21+221+231+241+251+...
    S = 1 2 + 1 2 2 + 1 2 3 + 1 2 4 + 1 2 5 + . . . < 1 S=\frac{1}{2} +\frac{1}{2^2}+ \frac{1}{2^3} + \frac{1}{2^4} + \frac{1}{2^5} + ... < 1 S=21+221+231+241+251+...<1
    因此该操作次数小于n,是 O ( n ) O(n) O(n)的。

基本的堆

在这里插入图片描述题目链接

#include <iostream>
#include <cstring>
using namespace std;

const int N = 1000005;
int heap[N], size;
int n;

void up(int u) {
	while (u > 1 && heap[u] < heap[u / 2]) swap(heap[u], heap[u / 2]), u /= 2;
} 

void down(int u) {
	int t = u;
	while (true) {
		if (u * 2 <= size && heap[u] > heap[u * 2]) t = u * 2;
		if (u * 2 + 1 <= size && heap[t] > heap[u * 2 + 1]) t = u * 2 + 1;
		if (u != t) {
			swap(heap[u], heap[t]);
			u = t;
		}
		else break;
	}
}

int main() {
	scanf("%d", &n);
	int op;
	int k, x;
	while (n -- ) {
		scanf("%d", &op);
		if (op== 1) {
			scanf("%d", &x);
			heap[ ++ size] = x;
			up(size);
		}
		else if (op == 2) {
			printf("%d\n", heap[1]);
		}
		else {
			heap[1] = heap[size -- ];
			down(1);
		}
	}
	return  0;
}

带映射堆(可对第k个插入元素操作)

在这里插入图片描述
注意这题需要保存第k个插入的数在堆中的映射,同时也要保存堆到第k个插入的数的映射。
因此交换操作和删除操作,也要对这个映射进行操作。

#include <iostream>
#include <cstring>
using namespace std;

const int N = 100005;
int heap[N], size_heap;
int n;
int hp[N]; // 堆 到 第k个插入元素 的 映射 
int ph[N]; // 第k个插入元素 到 堆 的映射 

void heap_swap(int a, int b) {
    swap(heap[a], heap[b]);
    swap(ph[hp[a]], ph[hp[b]]);
    swap(hp[a], hp[b]);
}

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

void down(int u) {
    int t = u;
    while(true) {
        if (u * 2 <= size_heap && heap[u] > heap[u * 2]) t = u * 2;
        if (u * 2 + 1 <= size_heap && heap[t] > heap[u * 2 + 1]) t = u * 2 + 1;
        if (u != t) {
            heap_swap(u, t);
            u = t;
        }
        else break;
    }
}

int main() {
    scanf("%d", &n);
    char op[2];
    int k, x, cnt = 0;
    while(n -- ) {
        scanf("%s", op);
        if (op[0] == 'I') {
            scanf("%d", &x);
            heap[ ++ size_heap] = x;
            hp[size_heap] = ++ cnt;
            ph[cnt] = size_heap;
            up(size_heap);
        }
        else if (!strcmp(op, "D")) {
            scanf("%d", &k);
            k = ph[k];
            //heap[k] = heap[size_heap -- ]; // 错的,还要对映射操作
            heap_swap(k, size_heap -- );
            up(k);
            down(k);
        }
        else if (!strcmp(op, "C")) {
            scanf("%d%d", &k, &x);
            k = ph[k];
            heap[k] = x;
            up(k); down(k);
        }
        else if (!strcmp(op, "PM")) {
            printf("%d\n", heap[1]);
        }
        else if (!strcmp(op, "DM")) {
            //heap[1] = heap[size_heap -- ]; // 错的,还要对映射操作
            heap_swap(1, size_heap -- );
            down(1);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值