6查找-2伸展树

#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
***/

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值