treap平衡树练习

166 篇文章 0 订阅

平衡树就是左旋右旋的一种树,赵老师讲数据结构的时候一直没实现,觉得很简单。

结果是很简单,但是,,代码量有点不敢恭维。

下面是一个大神的板子。自己敲了一遍。

#include <bits/stdc++.h>
using namespace std;

#define maxn 100010
#define INF 0x7fffffff

struct treapNode {
	int lChild, rChild; // 左右子节点下标
	int value, weight;  // 节点关键码及权值
	int count, size;    // 副本数及子树大小
} treap[maxn];

int numNodes, root, n;

int newNode(int val) {
	numNodes++;
	treap[numNodes].value = val;
	treap[numNodes].weight = rand(); // 随机权值
	treap[numNodes].count = treap[numNodes].size = 1;
	return numNodes;
} // 建立一个新的节点

void update(int p) {
	treap[p].size = treap[treap[p].lChild].size +
		treap[treap[p].rChild].size +
		treap[p].count;
} // 更新某一个点的 size 方便获取排名

void build() {
	newNode(-INF); // 保证 BST 性质的两个点
	newNode(INF);
	root = 1;
	treap[root].rChild = 2;
	update(root);
} // 初始化

int getRankByVal(int p, int val) {
	if (p == 0) return 0;
	if (val == treap[p].value)
		return treap[treap[p].lChild].size + 1;
	if (val < treap[p].value)
		return getRankByVal(treap[p].lChild, val);
	return getRankByVal(treap[p].rChild, val) +
		treap[treap[p].lChild].size +
		treap[p].count;
}
// 上下两个函数很好理解,不作赘述
// 一切从 BST 性质出发
int getValByRank(int p, int rnk) {
	if (p == 0) return INF;
	if (treap[treap[p].lChild].size >= rnk)
		return getValByRank(treap[p].lChild, rnk);
	if (treap[treap[p].lChild].size + treap[p].count >= rnk)
		return treap[p].value;
	return getValByRank(treap[p].rChild,
		rnk - treap[treap[p].lChild].size - treap[p].count);
}

void zig(int &p) {
	int q = treap[p].lChild;
	treap[p].lChild = treap[q].rChild;
	treap[q].rChild = p;
	p = q;
	update(treap[p].rChild);
	update(p); // 别忘记更新
}

void zag(int &p) {
	int q = treap[p].rChild;
	treap[p].rChild = treap[q].lChild;
	treap[q].lChild = p;
	p = q;
	update(treap[p].lChild);
	update(p);
}

void insert(int &p, int val) {
	if (p == 0) {
		p = newNode(val);
		return;
	}
	if (val == treap[p].value) {
		treap[p].count++;
		update(p);
		return;
	}
	if (val < treap[p].value) {
		insert(treap[p].lChild, val);
		if (treap[p].weight < treap[treap[p].lChild].weight) zig(p);
	} else {
		insert(treap[p].rChild, val);
		if (treap[p].weight < treap[treap[p].rChild].weight) zag(p);
	} // zig 和 zag 操作,保证满足大根堆性质
	update(p);
} // 插入一个点

int getPre(int val) {
	int ans = 1;
	int p = root;
	while (p != 0) {
		if (val == treap[p].value) {
			if (treap[p].lChild > 0) {
				p = treap[p].lChild; // 左子树上不断向右走,获取最大的前驱
				while (treap[p].rChild > 0) p = treap[p].rChild;
				ans = p;
			}
			break;
		}
		if (treap[p].value < val &&
			treap[p].value > treap[ans].value) ans = p; // 尝试更新答案
		p = val < treap[p].value ? treap[p].lChild : treap[p].rChild;
	}
	return treap[ans].value;
} // 获取前驱

int getNext(int val) {
	int ans = 2;
	int p = root;
	while (p != 0) {
		if (val == treap[p].value) {
			if (treap[p].rChild > 0) {
				p = treap[p].rChild; // 右子树上不断向左走,获取最小的后继
				while (treap[p].lChild > 0) p = treap[p].lChild;
				ans = p;
			}
			break;
		}
		if (treap[p].value > val &&
			treap[p].value < treap[ans].value) ans = p; // 尝试更新答案
		p = val < treap[p].value ? treap[p].lChild : treap[p].rChild;
	}
	return treap[ans].value;
}

void remove(int &p, int val) {
	if (p == 0) return;
	if (val == treap[p].value) {
		if (treap[p].count > 1) {
			treap[p].count--;
			update(p);
		} else if (treap[p].lChild != 0 || treap[p].rChild != 0) {
			if (treap[p].rChild == 0 ||
				treap[treap[p].lChild].weight > treap[treap[p].rChild].weight) {
				zig(p);
				remove(treap[p].rChild, val);
			} else {
				zag(p);
				remove(treap[p].lChild, val);
			} // 通过旋转来删除节点
			update(p);
		} else p = 0;
		return;
	}
	if (val < treap[p].value) remove(treap[p].lChild, val);
	else remove(treap[p].rChild, val);
	update(p);
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	numNodes = 0;
	build();
	cin >> n;
	int opt, x;
	while (n--) {
		cin >> opt >> x;
		switch (opt) {
			case 1: { insert(root, x); break; }
			case 2: { remove(root, x); break; }
			case 3: { cout << getRankByVal(root, x) - 1 << endl; break; } // 减一
			case 4: { cout << getValByRank(root, x + 1) << endl; break; } // 加一
			case 5: { cout << getPre(x) << endl; break; }
			case 6: { cout << getNext(x) << endl; break; }
		}
	}
	return 0;
}

 

 

今天上午刚刚搞明白什么是后缀数组,只写了一个模板题,下午就搞后缀自动机。这是第二遍看后缀自动机了,感觉有点东西了,虽然还是不太明白,但是事不过三。明天再看一遍。相信就没问题了。

加油!

 

7.25晚11点更新

啪啪打脸,又是没看懂

7.26上午11点更新

啪啪打脸

7.26下午5点更新

啪啪打脸

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值