平衡树之替罪羊树 洛谷P3369

题目
替罪羊树大概的思路就是一棵普通的平衡树,然后当平衡树失衡的时候(失衡条件可以自己判断,一般是左右子树的大小大于等于整棵树的大小的0.7倍的时候),暴力拍扁(也就是中序遍历平衡树),重建(分治建树)
替罪羊树的节点有一个deleted属性,表示该节点是否被删除,这也就意味着替罪羊树删除节点操作的时候并不是真的删除,而是通过标记的方法,直到重建树的时候才将需要删除的节点删除。
也因为有这个deleted属性的原因,有可能该平衡树存在大量的已经被标记为删除的节点,所以在判断是否需要重建的时候也可以加上当已被标记为删除的节点过多的时候重建树的条件。
替罪羊树插入或删除节点的平摊最坏时间复杂度是O(log n),搜索节点的最坏时间复杂度是O(log n)
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<queue>
#include<string>
#include<cmath>
#define ll long long
#define ull unsigned long long
const int INF = 0x3f3f3f3f;
const double delta = 0.7; //平衡因子
struct Node {
	Node* lefSon, * rigSon;
	int value, size, cnt; //cnt是包含了被删除节点的size
	bool deleted ;  //该节点是否被删除
	Node(int v, int siz, int ct,Node *node) :lefSon(node), rigSon(node),
		value(v), size(siz), cnt(ct), deleted(false) {};
}*null; //null表示一个空节点

bool check(Node*& node) { //检查是否需要重构
	//假如左子树总大小>=树的大小*平衡因子(0.7),或者右子树总大小>=树大小*平衡因子
	//则直接拍扁(中序遍历)+重构(分治)
	return (node->lefSon->cnt > delta * node->cnt) || (node->rigSon->cnt > node->cnt * delta);
}

Node* reBuildMain(int l, int r, std::vector<Node*>& v) {//构造时含l不含r
	if (l >= r)
		return null;
	int mid = l + r >> 1;
	v[mid]->lefSon = reBuildMain(l, mid, v);
	v[mid]->rigSon = reBuildMain(mid + 1, r, v);
	//update
	v[mid]->size = !v[mid]->deleted + v[mid]->lefSon->size + v[mid]->rigSon->size;
	v[mid]->cnt = 1 + v[mid]->lefSon->cnt + v[mid]->rigSon->cnt;
	return v[mid];
}
void reBuildDfs(Node*& node, std::vector<Node*>& v) {//中序遍历平衡树
	if (node == null)
		return;
	reBuildDfs(node->lefSon, v);
	int push = 0;
	if (!node->deleted)
		v.push_back(node), push = 1;
	reBuildDfs(node->rigSon, v);
	if (push == 0)
		delete(node);
}
void reBuild(Node*& node) {
	//重构平衡树
	std::vector<Node*> v;
	reBuildDfs(node, v);
	node = reBuildMain(0, v.size(), v); //构造时含头不含尾
}
void insert(Node*& node, int val) {
	if (node == null) {
		node = new Node(val, 1, 1, null);
		return;
	}
	++node->cnt;
	++node->size;
	if (val >= node->value) //大于等于value的在右边,每个val一个节点
		insert(node->rigSon, val);
	else
		insert(node->lefSon, val);
	if (check(node))
		reBuild(node);
}
bool erase(Node*& node, int rank) {//删除排名第rank大的
	if (node == null)
		return false;
	if (!node->deleted && rank == node->lefSon->size + 1) {
		node->deleted = true;
		node->size--;
		return true;
	}
	int result = 0;
	if (rank <= node->lefSon->size)  //????????????
		result |= erase(node->lefSon, rank);
	else
		result |= erase(node->rigSon, rank - node->lefSon->size - !node->deleted);
	if (result) {
		node->size--;
		return true;
	}
	else
		return false;
}
bool rankErase(Node* &node, int rank){ //删除排名第rank大的,删除完判断是否需要重构
	//删除失败返回false,否则返回true
	if (erase(node, rank)) {
		if (check(node))
			reBuild(node);
		return true;
	}
	return false; 
}
int getRankByValue(Node* node, int val) {
	//通过值查找排名,有多个值相同返回它们的最小排名(允许值不出现在树里)
	int ans = 1; 
	while (node != null) {
		if (node->value >= val)
			node = node->lefSon;
		else {
			ans += node->lefSon->size + !node->deleted;
			node = node->rigSon;
		}
	}
	return ans;
}
int getValueByRank(Node* node, int rank) {
	//通过排名找值,排名非法返回-1
	while (node != null) {
		if (!node->deleted && node->lefSon->size + 1 == rank)
			return node->value;
		else if (node->lefSon->size >= rank)
			node = node->lefSon;
		else {
			rank -= (node->lefSon->size + !node->deleted);
			node = node->rigSon;
		}
	}
	return -1;
}
int main() {
	null = new Node(0, 0, 0, nullptr);
	Node* root = null;
	int n;
	std::cin >> n;
	while (n--) {
		int opt, x;
		std::cin >> opt >> x;
		if (opt == 1)
			insert(root, x);
		else if (opt == 2)
			rankErase(root, getRankByValue(root, x));
		else if (opt == 3)
			std::cout << getRankByValue(root, x) << std::endl;
		else if (opt == 4)
			std::cout << getValueByRank(root, x) << std::endl;
		else if (opt == 5)
			std::cout << getValueByRank(root, getRankByValue(root, x) - 1) << '\n';
		else 
			std::cout << getValueByRank(root, getRankByValue(root, x + 1 )) << '\n';

	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值