Splay

之前一直被Splay模板题虐,但是Splay的使用方式太多了,一直没有整理出一个可靠的模板来。
后来回顾了一下自己写过的Splay,总结了一下需求,再参考了一下WJMZBMR的写法,设计出这么一个自己感觉上适应性比较强,分类比较清晰的Splay模板。
用了一下感觉还不错,于是在这里存一下。

/*
每次先调用一下Splay::init()
不同的题目注意修改newNode, pushdown, update三个函数就足够了
Splay命名空间里的函数都是对树的操作,对结点的操作都归类到Node里。
空间吃紧时可修改erase函数,增加内存池管理
*/
const int N = 130005;

struct Node {
	int key, size;
	bool rvs;
	Node *f, *ch[2];
	void set(int c, Node *x);
	void fix();
	void pushdown();
	void update();
	void rotate();
	void Splay(Node *);
}statePool[N], *null;

void Node::set(int c, Node *x) {
	ch = x;
	x->f = this;
}

void Node::fix() {
	ch[0]->f = this;
	ch[1]->f = this;
}

void Node::pushdown() {
	if (this == null) return;
	if (rvs) {
		ch[0]->rvs ^= 1;
		ch[1]->rvs ^= 1;
		rvs = 0;
		swap(ch[0], ch[1]);
	}
}

void Node::update() {
	if (this == null) return;
	size = ch[0]->size + ch[1]->size + 1;
}

void Node::rotate() {
	Node *x = f;
	bool o = f->ch[0] == this;
	x->set(!o, ch[o]);
	x->f->set(x->f->ch[1] == x, this);
	set(o, x);
	x->update();
	update();
}

void Node::Splay(Node *goal = null) {
    pushdown();
	for (; f != goal;) {
		f->f->pushdown();
		f->pushdown();
		pushdown();
		if (f->f == goal) {
			rotate();
		} else if ((f->f->ch[0] == f) ^ (f->ch[0] == this)) {
			rotate();
			rotate();
		} else {
			f->rotate();
			rotate();
		}
	}
}


namespace Splay {
	int poolCnt;

	Node *newNode() {
		Node *p = &statePool[poolCnt++];
		p->f = p->ch[0] = p->ch[1] = null;
		p->size = 1;
		p->rvs = 0;
		return p;
	}

	void init() {
		poolCnt = 0;
		null = newNode();
		null->size = 0;
	}

        // use an array a to build a balanced splay tree. return its root.
	template  Node *build(int l, int r, T a[]) {
		if (l > r) return null;
		Node *p = newNode();
		int mid = (l + r) / 2;
		p->key = a[mid];
		if (l < r) {
			p->ch[0] = build(l, mid - 1, a);
			p->ch[1] = build(mid + 1, r, a);
			p->update();
			p->fix();
		}
		return p;
	}

        // select the i-th element in the Splay tree. index start from 1.
	// take care you must splay after select
	Node *select(Node *root, int i) {
		for (Node *p = root;) {
			p->pushdown();
			if (p->ch[0]->size + 1 == i) {
				return p;
			} else if (p->ch[0]->size >= i) {
				p = p->ch[0];
			} else {
				i -= p->ch[0]->size + 1;
				p = p->ch[1];
			}
		}
	}

    // find the node which just >= a in the tree.
    // take care you must splay after lower_bound
	template  Node *lower_bound(Node *root, T a) {
	    Node *ret = null;
	    for (Node *p = root; p != null; ) {
	        p->pushdown();
	        if (a < p->key) {
	            p = p->ch[1];
	        } else {
	            ret = p;
	            p = p->ch[0];
	        }
	    }
	    return ret;
	}

        // merge two splay tree. p, q should be root. return the root
	Node *merge(Node *p, Node *q) {
	    p->f = q->f = null;
	    if (p == null) return q;
	    if (q == null) return p;
	    q = select(q, 1);
	    q->Splay();
	    q->set(0, p);
	    q->update();
	    return q;
	}

	//when p is root, q is p's right child(take care after pushdown), reverse from p to q, return the root of this tree.
	Node *reverse(Node *p, Node *q) {
		swap(p->ch[0], q->ch[0]);
		p->ch[1] = q->ch[1];
		q->ch[1] = p;
		q->f = null;
		p->fix();
		q->fix();
		p->update();
		q->update();
		p->ch[0]->rvs ^= 1;
		return q;
	}

	// reverse the l-th element to the r-th element in the Splay tree(inclusive), return the root of this tree, index start from 1.
	Node *reverse(Node *root, int l, int r) {
		if (l >= r) return root;
		Node *p = select(root, l);
		p->Splay();
		Node *q = select(p, r);
		q->Splay(p);
		return reverse(p, q);
	}

        //insert q before p. return the root of this tree.
	Node *insert(Node *p, Node *q) {
	    p->Splay();
	    if (p->ch[0] == null) {
	        p->set(0, q);
        } else {
            Node *t = select(p, p->ch[0]->size);
            t->Splay(p);
            t->set(1, q);
            t->update();
        }
        p->update();
        return p;
	}

        //insert q before the i-th node. return the root of this tree. index start from 1.
	Node *insert(Node *root, Node *q, int i) {
	    if (i > root->size) {
	        Node *p = select(root, root->size);
	        p->Splay();
	        p->set(1, q);
	        p->update();
	        return p;
	    } else {
	        Node *p = select(root, i);
	        return insert(p, q);
	    }
	}

        // erase the subtree whose root is p. return the root.
	Node *erase(Node *p) {
	    if (p->f != null) {
	        Node *q = p->f;
	        q->pushdown();
	        q->set(q->ch[1] == p, null);
	        q->update();
	        q->Splay();
	        return q;
	    } else {
	        return null;
	    }
	}

        // erase the element whose index in [l, r]. index start from 1. return the root.
	Node *erase(Node *root, int l, int r) {
	    if (l > r) return root;
	    if (l == r) {
	        Node *p = select(root, l);
	        p->Splay();
	        return merge(p->ch[0], p->ch[1]);
	    } else {
	        Node *p = select(root, l);
	        p->Splay();
	        Node *q = select(p, r);
	        q->Splay(p);
	        return merge(p->ch[0], q->ch[1]);
	    }
	}
}

加入对话

9条评论

  1. 您好,有几个问题想咨询一下:
    1. 为什么Splay 函数不写到Select, lower_bound函数的结尾处,这样调用起来就不怕忘记调用Splay()了。
    2. 关于 lower_bound 函数,是不是写错了?直觉应该是这样。
    if(a key) {
    ret = p;
    p = p->ch[0];
    } else {
    p = p->ch[1];
    }
    3. 110 行少了一个分号?

    我在试着学splay, 但是用起来跟我预期的效果不一样,希望能帮忙解答一下。谢谢咯~~

    1. 1. 你看到Splay上有个参数了吗?你怎么知道是那个参数该填啥?不是每次select完都是旋到根的。
      2. 没错。你去看一下STL的lower_bound的行为吧。
      3. 是的

      1. lower_bound, 难道不是求>=a 的最小节点指针吗?
        if (a key) p = p->ch[1];
        如果a 小于当前节点,那么应该到左子树查找啊,因为右边子树肯定都是大于a了, 有点不明白。

        你跟寒仔的头像一样啊,HOHO, 难道极客都习惯用这么萌的?

        1. 确实这里不对,想起来了……这个函数是从一个逆序的Splay上拷贝过来的,没测过就弄进去的。一直也没在这里用过,这里确实反了。

  2. 还有一个疑问,这颗树好像根本不是一颗搜索树的感觉,从build跟insert情况来看,好像没有保证 key 是 左小右大, 这样怎么能够用lower_bound? 不太清楚,希望您不闲麻烦给我答疑解惑哈。

    1. 我这样写insert是因为有些情况下大小是不能用key值来比较的。比如你想象一下用Splay来维护文本编辑器的内容。lower_bound是针对按key值大小来维护树的情况下使用的。

留下评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注