非旋转Treap

Treap是一种平衡二叉树,同时也是一个堆。它既具有二叉查找树的性质,也具有堆的性质。在对数据的查找、插入、删除、求第k大等操作上具有期望O(log2n)的复杂度。 
    Treap可以通过节点的旋转来实现其维持平衡的操作,详见旋转式Treap. 而旋转式Treap在对区间数据的操作上无能为力,这就需要非旋转式Treap来解决这些区间问题。

非旋转式Treap支持的操作

基本操作:

操作说明实现复杂度
Build 构造Treap O(n)
Merge 合并Treap O(log2n)
Split 拆分Treap O(log2n)
NewNode 新建节点 O(1)

可支持操作:

操作说明(实现)实现复杂度
Insert NewNode + Merge O(log2n)
Delete Split + Split + Merge O(log2n)
FindKth Split + Split O(log2n)
Query Split + Split O(log2n)
Cover Split + Split + Merge O(log2n)

基本操作

1. Build

    类似于笛卡尔树的构造(详细见笛卡尔树),非旋转式Treap也可以通过与栈的集合来达到平摊O(n)的复杂度。具体为:

(1) 通过快排等排序手段对原始数据序列进行排序,这主要是为了使Treap满足二叉查找特性 
(2) 当前Treap中的从根开始的右子节点、右子节点的右子节点...链上的所有节点push到一个栈中,栈底为根节点 
(3) 对排序后的序列,从头开始,执行: 
(3.1) 生成一个新的Treap节点,节点中会有随机生成的priority值用来实现堆的结构 
(3.2) 从栈顶向栈底查找,同时栈顶元素出栈,直到栈顶元素的priority小于当前节点的priority,记录下当前栈顶节点为P 
(3.3) 将P之前出栈的那个节点置为待插入节点的左子结点,同时将待插入节点置为P的右子节点,再将带插入节点入栈

2. Merge

    类似于左倾堆的Merge操作,可以在O(log2n)的时间复杂度内完成Merge操作。具体为:

(1) 如果一个Treap为空,则返回另外的Treap 
(2) 如果选择两个Treap中堆顶元素的priority值最小为的堆,“较小堆”的堆顶成为新堆的堆顶,然后递归调用 Merge,对较小堆的堆顶元素的右子节点和较大堆进行合并操作,并将返回的结果置为“较小堆”堆顶元素的右子节点 
(3) 对新堆的顶点进行维护,即维护堆顶节点的size等信息

3. Split

    对于一个Treap,按照他的第k位进行拆分,则可以按照类似快排算法寻找第k大元素的步骤,返回结果为一个pair,即最大元素为全局第k大的子树根节点和最小元素为全局第k+1大的子树根节点构成的pair。具体为:

(1) 若当前节点x的左子树中的size大于等于k,则进入左子树进行拆分,返回结果y(为一个pair)。 
(1.1) 然后将第k+1大及其之后的节点构成的子树挂在到x的左子树上。即将x的左子结点置为y的second,然后更新x节点的size等信息。 
(1.2) 然后将y的second指向x节点,即最小元素为全局第k+1大的子树的根节点 
(2) 若当前节点x的左子树的size小于k,则进入右子树进行拆分,返回结果y 
(2.1) 然后将第k大及其之前的节点构成的子树挂在到x的右子树上。即将x的右子节点置为y的first,然后更新x节点的size信息。 
(2.2) 然后将y的first指向x节点,即最大元素为全局第k大的子树的根节点 
(3) 返回y

实现(c++)

#include<iostream>
#include<algorithm>
using namespace std;
#define MAX_NUM 100
struct TreapNode{
	int key;
	int priority;
	int size;
	TreapNode* left;
	TreapNode* right;
	TreapNode(int k){
		key = k;
		size = 1;
		priority = rand();
		left = right = NULL;
	}
	void Update(){
		size = 1;
		if (left)
			size += left->size;
		if (right)
			size += right->size;
	}
};

int GetSize(TreapNode* node){
	if (node)
		return node->size;
	return 0;
}
void Build(int *arr, int n){
	sort(arr, arr + n); //先进行排序
	TreapNode* stack[MAX_NUM];
	TreapNode* last;
	int p = 0;
	TreapNode* new_node;
	for (int i = 0; i < n; i++){
		last = NULL;
		new_node = new TreapNode(arr[i]);
		//直到栈为空,或者栈顶元素priority小于当前节点的priority
		while (p && stack[p]->priority > new_node->priority){
			last = stack[p--];
		}
		stack[p]->right = new_node;	//新节点作为p的右子节点
		new_node->left = last;		//前一次出栈的节点作为 新节点的左子结点
		stack[++p] = new_node; //新插入元素入栈
	}
}

TreapNode* Merge(TreapNode* treap1, TreapNode* treap2){
	if (!treap1)
		return treap2;
	if (!treap2)
		return treap1;

	if (treap1->priority < treap2->priority){
		treap1->right = Merge(treap1->right, treap2);
		treap1->Update();
		return treap1;
	}
	else{
		treap2->left = Merge(treap1, treap2->left);
		treap2->Update();
		return treap2;
	}
}

typedef pair<TreapNode*, TreapNode*> TreapPair;
TreapPair Split(TreapNode* treap, int k){
	if (!treap){
		return TreapPair(NULL, NULL);
	}
	TreapPair result;
	if (treap->left->size >= k){
		result = Split(treap->left, k);
		treap->left = result.second;
		treap->Update();
		result.second = treap;
	}
	else{
		result = Split(treap->right, k - treap->left->size - 1);
		treap->right = result.first;
		treap->Update();
		result.first = treap;
	}
	return result;
}

//注意为treapNode指针引用
int FindKth(TreapNode*& treap, int k){
	TreapPair x = Split(treap, k - 1);
	TreapPair y = Split(x.second, 1);
	int result = y.first->key;
	treap = Merge(x.first, y.first);
	treap = Merge(treap, y.second);
}

//询问一个数是第几大
int GetKth(TreapNode* treap, int v){
	if (!treap)
		return 0;
	if (v < treap->key)
		return GetKth(treap->left, v);
	else
		return GetKth(treap->right, v) + GetSize(treap->left) + 1;
}

void Insert(TreapNode*& treap, int v){
	int k = GetKth(treap, v);
	TreapPair result = Split(treap, k);
	TreapNode* new_treap = new TreapNode(v);
	treap = Merge(result.first, new_treap);
	treap = Merge(treap, result.second);
}

void Delete(TreapNode*& treap, int k){
	TreapPair x = Split(treap, k - 1);
	TreapPair y = Split(x.second, 1);
	treap = Merge(x.first, y.second);
}

 

参考

非旋转Treap

posted @ 2015-08-12 22:11  农民伯伯-Coding  阅读(672)  评论(0编辑  收藏  举报