前面講解了平衡二叉查找樹,注意到一個結點被訪問後,接下來很有可能被再次訪問,所以可以採取把最近訪問的結點上移(比如說移動到根結點),方便以下訪問。這次要講解的伸展樹就是來解決該問題的。
伸展樹又稱自適應查找樹,它的各種操作平均複雜度爲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/