#include <bits/stdc++.h>
using namespace std;
typedef struct SplayNode *Tree;
struct SplayNode{
Tree parent,lchild,rchild; //双亲/左右儿
int val; //值
SplayNode(int val=0) { //构造器
parent=lchild=rchild=NULL; //三值为空
this->val=val; //赋值
}
};
Tree *search_val(Tree &root,int val,Tree &parent){ //查找:传入根,VAL,老豆,返回搜索结束位置
if (root==NULL) //如果找到到空结点
return &root; //就返回这个结点(就在这插入了)
if (root->val>val) //根的值大于当前要插入的值
return search_val(root->lchild,val,parent=root);//往左儿子深搜递归查找
else if(root->val<val) //根的值小于当前要插入的值
return search_val(root->rchild,val,parent=root);//往右儿子深搜递归查找
return &root; //等于当前结点值表示找到则直接返回
}
bool insert(Tree &root,int val){ //插入:传入树根及插入值,返回是否成功插入
Tree *temp=NULL; //开辟临时结点用来读取查找的结果
Tree parent=NULL; //开辟临时父结点用来接收查找到的插入位置的老豆
temp=search_val(root,val,parent); //查找插入位置,同时parent引用也会被更新
if (*temp==NULL){ //如果树中未有要插入的值
Tree node=new SplayNode(val); //创建新结点赋初值val
*temp=node; //把新结点赋给temp指针指向地址(见查找函数知指向插入地址)
node->parent=parent; //设置父节点。
return true; //成功插入
}
return false; //插入失败
}
Tree left_single_rotate(Tree &root,Tree node){ //单左旋:传入根与旋转结点,返回当前子树根
if (node==NULL)return NULL; //当前传入结点为空就不用操作了
Tree parent=node->parent; //读出其父结点
Tree grandparent=parent->parent; //读出其祖父结点
//首先是我的左儿跟我的老豆间的指针
parent->rchild=node->lchild; //父节点的右孩子就是我的左儿子(NULL也可赋值)
if (node->lchild)node->lchild->parent=parent;//当前结点左儿(若有)的老爸记为当前老豆
//然后是我跟我的老豆间的指针
node->lchild=parent; //它老爸变为它的左儿子
parent->parent=node; //当前结点变为原老豆的老豆
//最后是我跟我的祖父间的指针
node->parent=grandparent; //祖先变成当前结点的老豆
if (grandparent) { //如果祖父是存在的
if (grandparent->lchild==parent)//如果原老豆是祖父的左儿
grandparent->lchild=node; //当前结点更新为祖父左儿
else //如果原老豆是祖父的右儿
grandparent->rchild=node; //当前结点更新为祖父右儿
} //显然双旋操作中的root是不会变的
else root=node; //不存在祖父节点即原父节点为根,那么旋转后node为根
return node; //返回旋转后的结点
}
Tree right_single_rotate(Tree &root,Tree node){//单右旋:传入根与旋转结点,返回当前子树根
if (node==NULL)return NULL; //当前传入结点为空就不用操作了
Tree parent=node->parent; //读出其父结点
Tree grandparent=parent->parent;//读出其祖父结点
parent->lchild=node->rchild; //父节点的左孩子就是右的左儿子(NULL也可赋值)
if (node->rchild)node->rchild->parent=parent;//当前结点右儿(若有)的老爸记为当前老豆
node->rchild=parent; //它老爸变为它的左儿子
parent->parent=node; //当前结点变为原老豆的老豆
node->parent=grandparent; //祖先变成当前结点的老豆
if (grandparent){ //如果祖父是存在的
if (grandparent->rchild==parent) //如果原老豆是祖父的右儿
grandparent->rchild=node; //当前结点更新为祖父右儿
else //如果原老豆是祖父的左儿
grandparent->lchild=node; //当前结点更新为祖父左儿
}
else root=node; //不存在祖父节点即原父节点为根,那么旋转后node为根
return node; //返回旋转后的结点
}
void LR_rotate(Tree &root,Tree node){ //双旋(LR型):传入根及要变成子树根结点的结点
left_single_rotate(root,node); //先左
right_single_rotate(root,node); //后右
}
void RL_rotate(Tree&root,Tree node){ //双旋(RL型):传入根及要变成子树根结点的结点
right_single_rotate(root,node); //先右
left_single_rotate(root,node); //后左
}
void right_double_rotate(Tree &root,Tree node){ //双右旋:传入根及要变成子树根结点的结点
right_single_rotate(root,node->parent); //先提升其父节点
right_single_rotate(root,node); //最后提升自己
}
void left_double_rotate(Tree &root,Tree node){ //双左旋:传入根及要变成子树根结点的结点
left_single_rotate(root,node->parent); //先提升其父节点
left_single_rotate(root,node); //最后提升自己
}
void up(Tree &root,Tree node){ //根据情况,选择不同的旋转方式
Tree parent=node->parent; //开辟父亲结点
Tree grandparent=parent->parent; //开辟祖先结点
int i=grandparent->lchild==parent ? -1:1; //父亲是爷爷的左儿子就-1,右儿子就1
int j=parent->lchild==node ?-1:1; //当前结点是父亲的左儿子就-1,右儿子就1
if(i==-1&&j==-1)right_double_rotate(root,node); //当前结点是爷爷左儿左儿,AVL树LL型
else if(i==-1 && j==1)LR_rotate(root, node); //当前结点是爷爷左儿右儿,AVL树中的LR型
else if(i==1 && j==-1)RL_rotate(root, node); //当前结点是爷爷右儿左儿,AVL树RL型
else left_double_rotate(root, node); //当前结点是爷爷右儿右儿,AVL树RR型
}
void SplayTree(Tree &root,Tree node){ //Splay调整:传入根及要调整的结点
while (root->lchild!=node && root->rchild!=node && root!=node)//非根非根左右儿
up(root, node); //则转入up,注意双旋操作中root引用不会变
if (root->lchild==node) //当前结点为根的左孩子
root=right_single_rotate(root, node); //只需进行一次单右旋
else if(root->rchild==node) //当前结点为根的右孩子
root=left_single_rotate(root, node); //只需进行一次单左旋
}
bool search(Tree &root,int val){//查找及调整:传入根及要查的值,返回是否有进行伸展
Tree parent=NULL; //开辟临时父结点用来接收查找到的插入位置的老豆
Tree *temp=NULL; //开辟临时结点用来读取查找的结果
temp=search_val(root,val, parent);//查找所在位置,同时引用parent也会被更新
if (*temp && *temp!=root){ //读到的位置非空(即存在此值)且不是根
SplayTree(root,*temp); //全部的精华在这在里了,把temp移到根
return true; //在除去根外的结点中找到
} //下面那行要注意->要高级于*先运算所以要括号
//if((*temp)->val==root->val)return true;//改为返回是否找到就打开这注释,含根情况
return false; //除去根外的结点中没有找到,或者说没有进行旋转操作
}
Tree *Find_Min(Tree &root){ //返回当前子树其最小结点的引用(最深左儿)
if (root->lchild)return Find_Min(root->lchild);
return &root;
}
void remove(Tree &root,int val){//删除:注意传入的根是一个引用
Tree parent=NULL; //开辟临时父结点用来接收查找到的插入位置的老豆
Tree *temp; //开辟临时结点用来读取查找的结果
Tree *replace1; //开辟临时代替结点
Tree replace2; //开辟临时代替结点
temp=search_val(root,val, parent); //先进行查找操作
if(*temp){ //如果查找到就可以删除
if (*temp!=root)SplayTree(root, *temp); //不是根结点则把temp调整至根结点
if (root->rchild){ //当前根有右儿子就是有替代元素
replace1=Find_Min(root->rchild); //找到替换元素的位置(右儿子的最深左儿子)
root->val=(*replace1)->val; //替换结点值(相当于根被替,下面再删relace)
if ((*replace1)->lchild==NULL){ //左子树为空
replace2=*replace1; //预先读出结点以准备删除
*replace1=(*replace1)->rchild; //把他右孩子提上来
delete replace2; //然后删除被挤出树链的结点以释放内存
}
else if((*replace1)->rchild==NULL){ //右子树为空
replace2=*replace1; //预先读出结点以准备删除
*replace1=(*replace1)->lchild; //直接把左孩子提上来即可
delete replace2;
}//replace1没有左儿子的情况只有根的右儿没有左儿,这时Find_Min得右儿
}
else{ //右儿子就是没有替代元素
replace2=root; //把根(引用)赋给代替结点2
root=root->lchild; //根直接移向左子树,不管左子树是否为空都可以处理
delete replace2; //原来的根结点删除以释放内存
}
}
}
void PreOrder(Tree root){ //前序
if(root==NULL)return; //当前结点非空
printf("%d ",root->val); //输出结点值
PreOrder(root->lchild); //递归输出左结点值
PreOrder(root->rchild); //递归输出右结点值
}
void InOrder(Tree root){ //中序
if(root==NULL)return; //当前结点非空
InOrder(root->lchild); //递归输出左结点值
printf("%d ",root->val); //输出结点值
InOrder(root->rchild); //递归输出右结点值
}
int main(){
Tree root=NULL; //定义空根(不是指针,已在内存栈中申请空间
for(int i=11;i<=17;i++)insert(root,i); //插入1~18号点
cout<<search(root,14)<<endl; //查询树中是否有14号点,返回1是找到的
cout<<search(root,14)<<endl; //再查就返0找不到了,因为不查根结点
printf("前序:");PreOrder(root);printf("\n"); //前序
printf("中序:");InOrder(root);printf("\n"); //中序
remove(root,16); //删除16号结点
printf("前序:");PreOrder(root);printf("\n"); //前序
printf("中序:");InOrder(root);printf("\n"); //中序
return 0;
}
/***output
1
0
前序:14 11 13 12 15 16 17
中序:11 12 13 14 15 16 17
前序:17 15 14 11 13 12
中序:11 12 13 14 15 17
***/
6查找-2伸展树
最新推荐文章于 2022-04-14 17:26:31 发布