数据结构——堆

堆支持的操作

维护一个数据集合

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

堆本质上是一棵完全二叉树,最后一层节点从左到右依次排布的

以小根堆为例,每一个点都是小于等于左右儿子的


堆的存储

用一维数组

1为根节点,x节点的左儿子是2x2x2x右儿子是2x+12x+12x+1


操作

基本操作

O(logN)O(logN)O(logN)N是层数

// up 上移一个节点
void up(int u){
    while(u / 2 && h[u / 2] > h[u]){
   	    h_swap(u / 2, u);
       	u /= 2;
    }
}
// down 下放一个节点
void down(int u){
    int t = u;
    if(u << 1 <= size && h[u << 1] < h[t]) t = (u << 1);
    if(u << 1 | 1 <= size && h[u << 1 | 1] < h[t]) t = (u << 1 | 1);
    
    if(t != u){ 
        // 如果t不相等意味着u还不满足小堆的条件于最小儿子交换后继续down,这里要结合定义去理解,
        //这说明我们现在的这个元素,比两边都大,需要把他往下放,放到两节点最小的那个上去
        //又因为他比较大,也得看看是不是能把他再往下放放
        h_swap(t, u);
        down(t);
    }
}

扩展实现

插入一个元素

在二叉树的最底端插入这个数,然后不断往上移

void push(int x){
    heap[++ size] = x;
    up(size);
}
求当前集合当中的最小值
int top(){
    return heap[1];
}
删除一个最小值

用最后一个元素来覆盖到根,然后再down一遍

void pop(){
    heap[1] = heap[size];
    size--;	
    down(1);
}
删除任意一个元素
void pop_any(int k){
    heap[k] = heap[size];
    size--;
    
    down(k); // 只有变大了才会往下,只有变小的才会往上,所以这两种操作实际上有一个是不会执行的
    up(k);
}
修改任意一个元素
void modify_any(int k, int x){
    heap[k] = x;
    down(k); // 只有变大了才会往下,只有变小的才会往上,所以这两种操作实际上有一个是不会执行的
    up(k);
}

结构体版

struct heap{
	int h[100000 + 10];
	int ph[100000 + 10]; // 记录第k次插入的数在堆的哪里
	int hp[100000 + 10]; // 找到当前这个数对应的ph是哪里,就是找到这个数是第几次插入的
	int size;
	int idx; // 第几个插入的元素
	
	void h_swap(int a, int b){
		swap(ph[hp[a]], ph[hp[b]]); // 通过hp[a]找回去ph
		swap(hp[a], hp[b]);
		swap(h[a], h[b]);
	}
	
	void down(int u){
		int t = u;
		
		if(u * 2 <= size && h[u * 2] < h[t]) t = (u * 2);
		if(u * 2 + 1 <= size && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
		
		if(t != u){
			h_swap(t, u);
			down(t);
		}
	}
	
	void up(int u){
	    while(u / 2 && h[u / 2] > h[u]){
    	    h_swap(u / 2, u);
        	u /= 2;
	    }
	}
	
	void init(){
		for(int i = n / 2; i; i--){
			down(i);
		}
	}
	
	void push(int x){
		h[++size] = x;
		idx++; // 看是第几次插入的
		ph[idx] = size;
		hp[size] = idx;
		up(size);
	}
	
	void pop(){
		if(size){
			h_swap(1, size);
			size--;
			down(1);
		}
	}
	
	int top(){
		return h[1];
	}
	
	void pop_any(int k){ // 删除第k次的元素
		k = ph[k];
		h_swap(k, size);
		size--;
		down(k);
		up(k);
	}
	
	void modify_any(int k, int x){ // 修改第k次插入的元素
		k = ph[k];
		h[k] = x;
		down(k);
		up(k);
	}
}h;
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值