问题:
说明:
来 写 一 个 Treap。
问题链接:https://loj.ac/problem/104
提交记录:
https://loj.ac/submission/860484
输入案例:
传了一份测试数据:https://download.csdn.net/download/qq_28033719/12609664
我的代码:
知识准备
1、树堆:
是一种平衡树,由二叉搜索树(BST)分化而来,而且结合了堆排序特征(最大堆,最小堆),通过权值把最大或最小权值提升到根节点,那么树进行打结点时候,可以不用形成一条链表的情况,因此操作时间稳定在(Log n),具体有证明的。
2、左旋转 右旋转
左旋转:左手方向旋转,也叫做逆时针旋转,比如根节点3,右节点6, 3 -> 6 旋转就像是逆时针旋转。
左旋
3 3 6 6
/ \ / \ / \ / \
2 6 => 2 4 7 => 3 7
/ \ / \
4 7 2 4
右旋转:和左旋转相反
右旋反过来
6 3 6 3
/ \ / \ / \ / \
3 7 => 2 4 7 => 2 6
/ \ / \
2 4 4 7
3、前驱 后继
前驱:小于 target 的最大那个数
后继:大于 target 的最小那个数
4、具体代码:
我看了模板然后自己弄了下,还是弄成对象比较适合,也可以用数组(但数组的逻辑太反人类,我弄了好久没通过)
import java.util.Scanner;
public class Main {
class TreapNode {
// 容量记录,该节点下面存放节点数量
int size;
// 元素本身值
int value;
// 随机值
int random;
// 权值一样的节点重复数
int num;
// 存子节点
TreapNode left;
TreapNode right;
}
private static boolean LEFT = true;
private static boolean RIGHT = false;
int sum = 0;
TreapNode ROOT = null;
// 更新节点数
public void pushup(TreapNode r) {
// 左右子节点数 + 自己重复数
int left = r.left == null ? 0 : r.left.size;
int right = r.right == null ? 0 : r.right.size;
r.size = left + right + r.num;
}
// true - 左旋转 false - 右旋转
// 左旋 是 逆时针旋转,以根节点为中心,将右边逆时针转上去
/**
* 左旋
* 3 3 6 6
* / \ / \ / \ / \
* 2 6 => 2 4 7 => 3 7
* / \ / \
* 4 7 2 4
* 右旋反过来
* 6 3 6 3
* / \ / \ / \ / \
* 3 7 => 2 4 7 => 2 6
* / \ / \
* 2 4 4 7
*/
public TreapNode rotate(TreapNode r, boolean dir) {
// 暂存 旋转点
TreapNode op = dir ? r.right : r.left;
// 将根连接旋转点枝的对立枝,改为旋转点下面同向枝
if(dir) r.right = op.left;
else r.left = op.right;
// 旋转点的同向枝,变为根节点
if(dir) op.left = r;
else op.right = r;
// 重新统计 r 节点的节点数
pushup(r);
// 重新统计 op 节点节点数
pushup(op);
return op;
}
/**
* 索引 r 根节点,插入 x值 节点
* 每次 insert 需要统计一次 size
* sum 具体没用上,可以暴露一个 size() 的方法,不过不必要
**/
public TreapNode insert(TreapNode r, int x) {
if(r == null) {
r = new TreapNode();
++ sum;
r.size = r.num = 1;
r.value = x;
r.random = (int)(Math.random() * 10000);
return r;
}
// 发现重复
if(r.value == x) {
r.num ++;
r.size ++;
return r;
}
TreapNode d;
if(x > r.value) {
r.right = insert(r.right, x);
d = r.right;
} else {
r.left = insert(r.left, x);
d = r.left;
}
if(r.random < d.random) r = rotate(r,x > r.value);
pushup(r);
return r;
}
public TreapNode delete(TreapNode r, int x) {
if(r == null) return r;
// 二分递归
if(x < r.value) r.left = delete(r.left, x);
else if(x > r.value) r.right = delete(r.right, x);
// 命中
else {
boolean lf = r.left == null;
boolean rf = r.right == null;
if(lf && rf) {
// 已经末尾节点
r.num --;
if(r.num == 0) return null;
} else if(lf) {
r = rotate(r,LEFT);
r.left = delete(r.left, x);
} else if(rf) {
r = rotate(r,RIGHT);
r.right = delete(r.right, x);
} else {
boolean dir = r.left.random < r.right.random;
r = rotate(r,dir);
if(dir) r.left = delete(r.left, x);
else r.right = delete(r.right, x);
}
}
// 如果是删除,就需要把所有节点数统计减去 1
-- r.size;
return r;
}
public int find(TreapNode r, int x) {
if(r == null) return 0;
int left = r.left == null ? 0 : r.left.size;
if(left >= x) return find(r.left, x);
else if(left + r.num < x) return find(r.right,x - r.num - left);
else return r.value;
}
/**
* 前驱,小于x最大数
* @param r
* @param x
* @return
*/
public int pre(TreapNode r, int x) {
if(r == null) return Integer.MIN_VALUE;
if(r.value >= x) return pre(r.left,x);
else return Math.max(r.value, pre(r.right, x));
}
public int suc(TreapNode r, int x) {
if(r == null) return Integer.MAX_VALUE;
if(r.value <= x) return suc(r.right, x);
else return Math.min(suc(r.left, x), r.value);
}
public int rank(TreapNode r, int x) {
if(r == null) return 0;
else if(r.value == x) return r.left == null ? 1 : r.left.size + 1;
else if(r.value < x) return r.num + rank(r.right,x) + (r.left == null ? 0 : r.left.size);
else return rank(r.left, x);
}
public static void main(String[] args) {
Main treap2 = new Main();
Scanner sc = new Scanner(System.in);
int nn = sc.nextInt();
for (int i = 0; i < nn; i++) {
int opt, x;
opt = sc.nextInt();
x = sc.nextInt();
switch (opt) {
case 1:
treap2.ROOT = treap2.insert(treap2.ROOT, x);
break;
case 2:
treap2.ROOT = treap2.delete(treap2.ROOT, x);
sum --;
break;
case 3:
System.out.println(treap2.rank(treap2.ROOT, x));
break;
case 4:
System.out.println(treap2.find(treap2.ROOT, x));
break;
case 5:
System.out.println(treap2.pre(treap2.ROOT, x));
break;
case 6:
System.out.println(treap2.suc(treap2.ROOT, x));
}
}
}
}