【算法分析与设计】第三章-伸展树

伸展树

概念

英文名: SplayTree,首先属于二叉搜索树, 一种自调节树。适用于二八法则场景。
伸展 :将一个元素移动至根部,称为一次伸展。
注意:伸展树不一定是二叉平衡树。

操作

  • 插入
    如果结点不存在则插入新结点,如果存在则返回查找结果。插入返回时将结点伸展到根。
  • 查找
    如果结点不存在,将与查找结点相邻的结点调整到根。
  • 删除
    找到删除结点,将结点调整到根,删除结点,将删除结点的最大下界结点伸展到根。

逻辑与实现

为了方便描述,我们规定 p, q, r 分别为爷爷,父亲,儿子三个结点的指针。旋转对爷爷进行操作。

  • 单一旋转
    一次旋转交换两个结点的位置, 这里只需要考虑爷爷和父亲两个结点。旋转目的将父亲调整至根。
    函数的参数指向爷爷
    • LRot(zag)
      void LRot(BTNode *&p){	//这里需要传递引用,因为要修改p的实际的值
      	BTNode *r = p->rChild;
      	p->rChild = r->lChild;
      	r->lChild = p;
      	p = r;
      }
      
    • RRot(zig)
      void RRot(BTNode *&p){		//左旋的对称操作
      	BTNode *r = p->lChild;
      	p->lChild = r->rChild;
      	r->rChild = p;
      	p = r; 
      }
      

观察容易发现记忆规律:RRot,LRot代码呈现出顶针句式

  • 双重旋转
    要考虑爷爷,父亲,儿子三个结点。旋转目的是将儿子调整置根。注意旋转顺序。
    zigzig / zagzag称为一字旋转, zigzag/zagzig 称为之子旋转。

    • zigzig
      顾名思义:两次zig旋转: RRot( p ), RRot( q )。
    • zagzag
      顾名思义:两次zag旋转:LRot( p ), LRot( r )。
    • zigzag
      not顾名思义:先zag旋转父亲,再zig旋转爷爷。
    • zagzig
      not顾名思义:先zag旋转父亲,再zig旋转爷爷。
      ![在这里插入图片描述](https://img-blog.csdnimg.cn/c74be1347a564904a98ec5aa3572ecc1.png

    有点反人性,这里之字旋转的名称不代表旋转的先后顺序。(其它图后期看心情吧,没带鼠标)

  • Insert操作的代码
    (其他操作大同小异)
    代码巨长,但是基本对称,逻辑理清楚即可,伸展是通过递归自底向上伸展的。
    代码中p代表当前结点,r代表p的孩子。(本质上爷爷父亲,父亲儿子还是父与子的关系)

ResultCode Insert(BTNode *& p, int x){
	ResultCode result = Success;
	//讨论爷爷
	if(p == NULL){
		p = new BTNode;
		p->element = x;
		return result;
	}
	if(p->element == x){
		result = Duplicate;
		return result;
	}
	if(x < p->element){
		//讨论父亲
		BTNode *r = p->lChild;
		if(r == NULL){	
			r = new BTNode;
			r->element = x;
			r->rChild = p;
			p = r;
			return result;
		}
		if (r->element == x){
			result = Duplicate;
			RRot(p);	// zig
			return result;
		}
		//下面通过递归讨论儿子
		if(x < r->element){
			result = Insert(r->lChild, x);	// zigzig
			RRot(p);
		}
		else {
			result = Insert(r->rChild, x);	//zigzag	
			LRot(r);
			p->lChild = r;
		}
		RRot(p);
	}
	else{	//跟上面代码对称
		BTNode *r = p->rChild;
		if(r == NULL){
			r = new BTNode;
			r->element = x;
			r->lChild = p;
			p = r;
			return result;
		}
		if(r->element == x){	//zag
			result = Duplicate;	
			LRot(p);
			return result;
		}
		if(x > r->element){		//zagzag
			result = Insert(r->rChild, x);
			LRot(p); 
		}
		else{	//zagzig
			result = Insert(r->lChild, x);
			RRot(r);
			p->rChild = r;
		}
		LRot(p);
	}
	return result;
}
  • 对于删除操作为啥将最大下界结点伸展到根?
            根据二叉搜索树的性质,小于根结点的最大下界结点一定在根结点左子树的最右侧(可以方便的找到),而且其右孩子必为空。因为此时删除结点已经伸展到根,此时可以直接将根结点的右子树接到该结点的右侧,并将根结点的左子树接到该结点的左侧(在操作前摘下该结点)。如此操作后,得到的仍然是一棵二叉搜索树。

关于二八法则

有人说获得80%的知识只需要花费20%的精力,这样你100%的精力可以获取总和为400%的知识。
而不是100%的精力获得不一定100%的单一知识。
这对时间、精力有限的我们也是一种启迪。
伸展树应用于二八场景:指的是一件事情发生后有极大的概率再次发生。
比如百度某样知识,点击次数多的优先排在前方等等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值