伸展樹(splay tree)實現

前面講解了平衡二叉查找樹,注意到一個結點被訪問後,接下來很有可能被再次訪問,所以可以採取把最近訪問的結點上移(比如說移動到根結點),方便以下訪問。這次要講解的伸展樹就是來解決該問題的。

伸展樹又稱自適應查找樹,它的各種操作平均複雜度爲O(lgn),其複雜度邊界是均攤的。雖然某次操作可能需要代價O(n),但是連續M次操作的代價爲O(M*lgn)。

伸展樹的最核心的操作就是伸展,插入、刪除、查找都要用到伸展操作。一般來說,伸展操作可以從搜索路徑上最後一個結點開始,自底向上進行左旋和右旋操作,但是該方式需要知道父節點信息,而計算父節點或存儲父節點需要較大的開銷。於是一般採用自頂向下的實現方式。從根結點開始搜索某一結點x,不斷進行左旋和右旋,同時把大於x的結點接到樹R的左子樹中,把小於x的結點接到樹L的右子樹上,直到查找路徑的最後一個結點。然後把樹進行重組,就得到了伸展後的樹。

詳細原理請看參考資料,參考資料[1][2]都對原理進行了詳盡的闡述,並提供了實現。下面直接給出代碼,代碼中有詳細註釋。

#include <cstdlib>
#include <iostream>

using namespace std;

typedef struct BinNode
{
	int data;
	struct BinNode* left;
	struct BinNode* right;
}BinNode, *BiTree;
 
BinNode* splay(int i, BinNode* t)
{
	BinNode N;				//哨位結點 
	BinNode* L;
	BinNode* R;
	BinNode* y;
	
//	if(t == NULL)		//空樹 
//		return t;
	N.left = N.right = NULL;
	L = R = &N;				//指針L和R初始指向哨位結點N
	
	while(true)
	{
		if(i < t->data)
		{
			if(t->left != NULL && i < t->left->data)//右旋轉 
			{
				y = t->left;
				t->left = y->right;
				y->right = t;
				t = y;
			}
			if(t->left == NULL)
			{
				break;
			}
			R->left = t;	//右鏈接,把t掛接爲R的左子樹
			R = t;			//t爲新的R
			t = t->left;	//在t的左子樹中繼續查找	
		}
		else if(i > t->data)
		{
			if(t->right != NULL && i > t->right->data)//左旋轉 
			{
				y = t->right;
				t->right = y->left;
				y->left = t;
				t = y;
			}
			if(t->right == NULL)
			{
				break;
			}
			L->right = t;	//左鏈接,把t掛接爲L的右子樹
			L = t;			//t爲新的L
			t = t->right;	//在t右子樹中繼續查找
		}
		else				//與當前根結點元素相同,重複 
		{
			break;
		}
	}
	
	L->right = t->left;		//重新組合 
	R->left = t->right;
	t->left = N.right;		//N.right爲實際左子樹的根 
	t->right = N.left;		//N.left爲實際右子樹的根 
	return t;
}

BinNode* insert(int i, BinNode* t)
{
	BinNode* newNode = new BinNode;//開闢一個新結點 
	newNode->data = i;
	newNode->left = newNode->right = NULL;
	
	if(t == NULL)			//t爲空樹 
	{
		return newNode;
	}
	
	t = splay(i,t);			//返回伸展後的新根結點 
	if(i < t->data)			//欲插入元素在樹中不存在,且小於根結點元素值 
	{
		newNode->left = t->left;
		newNode->right = t;
		t->left = NULL;
		return newNode;
	}
	else if(i > t->data)	//欲插入元素在樹中不存在,且小於根結點元素值 
	{
		newNode->right = t->right;
		newNode->left = t;
		t->right = NULL;
	}
	else					//欲插入元素在樹中存在,即等於根結點元素值 
	{
		delete newNode;
		return t;
	}
}

BinNode* remove(int i, BinNode* t)
{
	BinNode* newRoot = NULL;
	
	if(t == NULL)			//空樹 
	{
		return t;
	}
	
	t = splay(i,t);
	
	if(i == t->data)		//找到該元素結點 
	{
		if(t->left == NULL)	//根結點左子樹爲空 
		{
			newRoot = t->right;
			
		}
		else
		{
			newRoot = splay(i, t->left);//newRoot必然指向t的左結點中最大的結點,
								//也就是伸展前t的前驅結點 
			newRoot->right= t->right;
		}
		delete t;
		return newRoot;
	}
	return t;				//樹中不存在該元素 
}

BinNode* search(int i, BinNode* t, bool& exist)
{
	exist = false;
	if(t == NULL)
		return NULL;
	t = splay(i, t);
	if(i == t->data)
	{
		exist = true;
	}
	return t;
}

void inOrder(BinNode* t)
{
	if(t)
	{
		inOrder(t->left);
		cout<<t->data<<" ";
		inOrder(t->right);
	}
}
void preOrder(BinNode* t)
{
	if(t)
	{
		cout<<t->data<<" ";
		preOrder(t->left);
		preOrder(t->right);
	}
}

void printTree(BinNode* t)
{
	cout<<"preOrder: "<<endl;
	preOrder(t);
	cout<<endl;
	cout<<"inOrder: "<<endl;
	inOrder(t);
	cout<<endl<<endl;
}

int main(int argc, char *argv[])
{
	BinNode* root = NULL;
	bool res = false;
	//insert
	for(int i=20; i>0; i--)
		root = insert(i,root);
	cout<<"after insert: "<<endl;
	printTree(root);
	
	//remove
	for(int i= 1; i<20; i += 2)
		root = remove(i,root);
	cout<<"after remove :"<<endl;
	printTree(root);
	
	//search
	cout<<"search elements: "<<endl;
	for(int i=1; i<20; i++)
	{
		root = search(i,root,res);
		if(res)
		{
			cout<<"("<<i<<","<<"yes"<<")"<<endl;;
		}
		else
		{
			cout<<"("<<i<<","<<"no"<<")"<<endl;
		}
	}
	cout<<"after search: "<<endl;
	printTree(root);
		
    system("PAUSE");
    return EXIT_SUCCESS;
}

參考資料:

[1]Data Structures and Algorithm Analysis in C++(third editon) (數據結構與算法分析C++描述,第3版 )

[2一篇博客文章:http://www.cnblogs.com/kernel_hcy/archive/2010/03/17/1688360.html

[3]詳細C和java實現:http://www.link.cs.cmu.edu/link/ftp-site/splaying/

發佈了38 篇原創文章 · 獲贊 10 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章