二叉搜索树(三)---伸展树

#include"BST.h"
#include<iostream>
#include<stdbool.h>
#include<time.h>
using namespace std;
#pragma once
template<typename T> class Splay :public BST<T>
{
protected:
	bin_node_posi<T> splay(bin_node_posi<T> v)     // 节点 v 伸展到树根,双层伸展
	{
		if (v == this->root) return v;
		//if (p == this->root)
		//{
		//	if (IS_LCHILD(*v))                 // zig
		//	{
		//		attach_as_lchild(v->rchild, p);
		//		v->parent = p->parent;
		//		attach_as_rchild(v, p);
		//		FROM_PARENT_TO(*p) = v;
		//	}
		//	else                              // zag
		//	{
		//		//  待定...
		//		attach_as_rchild(p, v->lchild);
		//		attach_as_lchild(p, v);
		//	}
		//}
		//bin_node_posi<T> g = p->parent;
		//while (v != this->root)       // 这样写只能判断是否调整完,而不能判断能不能进入逐层伸展!!!
		//{
		//	if(IS_LCHILD(*p))        // 这里也是有点问题的,应该以 v 作为优先的判断,与AVL树的旋转还是有点不一样的
		//		if (IS_LCHILD(*v))       // zig-zig
		//		{
		//			g == this->root ? v = root : FROM_PARENT_TO(*g) = v;
		//			v->parent = g->parent;
					/*attach_as_lchild(v->rchild, p);
					attach_as_rchild(v, p);
					attach_as_lchild(p->rchild, g);
					attach_as_rchild(p, g);*/
		//		}
		//}
		// 改进后...
		bin_node_posi<T> p; bin_node_posi<T> g;
		while ((p = v->parent) && (g = p->parent))   // p,g 同时不为null,双层伸展(此步为进入双层伸展的点睛之笔!)
		{
			bin_node_posi<T> gg = g->parent;         // 最后把调整后的节点接回树中,伸展调整画好图对照
			bin_node_posi<T>& to_g = FROM_PARENT_TO(*g);
			if (IS_LCHILD(*v))          // zig
				if (IS_LCHILD(*p)) {   // zig-zig
					attach_as_lchild(p->rchild, g);
					attach_as_lchild(v->rchild, p);
					attach_as_rchild(v, p);
					attach_as_rchild(p, g);
					/*attach_as_rchild(p, g);
					attach_as_rchild(v, p);*/

				}
				else {                 // zig-zag
					attach_as_lchild(v->rchild, p);
					attach_as_rchild(g, v->lchild);
					attach_as_lchild(g, v);
					attach_as_rchild(v, p);
				}
			else                      // zag
				if (IS_RCHILD(*p))     // zag-zag
				{
					attach_as_rchild(g, p->lchild);
					attach_as_rchild(p, v->lchild);
					attach_as_lchild(g, p);
					attach_as_lchild(p, v);
				}
				else {
					attach_as_rchild(p, v->lchild);
					attach_as_lchild(v->rchild, g);
					attach_as_lchild(p, v);
					attach_as_rchild(v, g);
					/*attach_as_rchild(v, g);
					attach_as_lchild(p, v);*/

				}
			if (gg == nullptr) v->parent = nullptr;    // 如果v的祖父g开始的时候是root节点,则经过调整后v变成root
			else {
				// 把旋转之后的v接入到子树中
				/*v->parent = gg;
				IS_LCHILD(*v) ? gg->lchild = v : gg->rchild = v;*/      // 这一步应该是与之前的g的位置作为标准的
				//(g == gg->lchild) ? attach_as_lchild(v, gg) : attach_as_rchild(gg, v);
				//FROM_PARENT_TO(*g) = v; v->parent = gg;              // 等价85行?在这个位置怕是不等价!
				to_g = v; v->parent = gg;                              // 在前面保留from_parent_to(*g)就行!!
			}
			// 注意一个细节问题:update_height是自底向上,也就是说如果底部的高度没有先得到相应的更新,顶部就是错了信息
			// this->update_height(v); this->update_height(p); this->update_height(g);
			this->update_height(g); this->update_height(p); this->update_height(v);
		}
		// 双层伸展之后才有可能有一次 单旋
		if (v->parent == this->root)
		{
			if (IS_LCHILD(*v)) {                 // 双层伸展过后v不确定是左孩子还是右孩子
				attach_as_lchild(v->rchild, p);
				attach_as_rchild(v, p);
			}
			else {
				attach_as_rchild(p, v->lchild);
				attach_as_lchild(p, v);
			}
			v->parent = nullptr;
			this->update_height(p); this->update_height(v);      // 与81行同理
		}
		return v;
	}
public:
	bin_node_posi<T>& search(const T& e);          // 返回是引用类型呵(因为这个错了好多次)
	bin_node_posi<T> insert(const T& e);
	bool remove(const T& e);
	void inline attach_as_rchild(bin_node_posi<T> p, bin_node_posi<T> v)
	{
		p->rchild = v;  if (v) v->parent = p;
	}
	void inline attach_as_lchild(bin_node_posi<T> v, bin_node_posi<T> p)
	{
		p->lchild = v;  if (v) v->parent = p;
	}
};

template<typename T> bin_node_posi<T>& Splay<T>::search(const T& e)
{
	bin_node_posi<T> searched = BST<T>::search(e);
	this->root = splay(searched ? searched : this->phit);     // searched==null 时返回phit,此时对phit进行伸展
	return this->root;
}
template<typename T> bin_node_posi<T> Splay<T>::insert(const T& e)
{
	bin_node_posi<T> ptr = search(e);
	bin_node_posi<T> temp = new bin_node<T>(e);
	if (ptr == nullptr) {                       // 树本身为空树
		this->root = temp;
		return this->root;
	}
	if (e == ptr->data) return this->root;
	// 处理节点查找失败情况
	if (e > this->root->data)
	{
		this->root->parent = temp;
		temp->lchild = this->root;
		bin_node_posi<T> rtree = this->root->rchild;
		this->root->rchild = nullptr;
		if (rtree) rtree->parent = nullptr;
		temp->rchild = rtree;
		if (rtree != nullptr) rtree->parent = temp;
	}
	else {
		this->root->parent = temp;
		temp->rchild = this->root;
		bin_node_posi<T> ltree = this->root->lchild;
		this->root->lchild = nullptr;
		if (ltree) ltree->parent = nullptr;
		temp->lchild = ltree;
		if (ltree != nullptr) ltree->parent = temp;
	}
	this->root = temp;
	//this->update_height(this->root);
	this->update_height_above(ptr);                    // 见笔记本对此种情况的分析
	return this->root;
}
// 总结: 以phit 作为分类讨论无疑增加了负担(情况非常多!!!)
//template<typename T> bool Splay<T>::remove(const T& e)
//{
//	bin_node_posi<T> ptr = search(e);                // ------------这两句其实可以合并的
//	if (ptr == nullptr) return false;                // 空树
//	if (ptr->data != e) return false;                // 树中没有相应元素
//	else {
//		if (this->phit == nullptr)                 // 只有一个节点(root) 或 根本就没有伸展(search找的就是原来的根节点)
//		{
//			if (!(this->root->lchild || this->root->rchild)) {
//				release(ptr->data);
//				release(ptr);
//				this->root = nullptr;
//				return true;
//			}
//			else
//		}
//		bin_node_posi<T> left_t1 = this->root->lchild;
//		bin_node_posi<T> right_t2 = this->root->rchild;
//		if(left_t1) left_t1->parent = nullptr;
//		if(right_t2) right_t2->parent = nullptr;
//		if (ptr->data < this->phit->data )
//		{
//			this->root = splay(this->phit);
//		}
//	}
//}
template<typename T> bool Splay<T>::remove(const T& e)
{
	bin_node_posi<T> ptr = search(e);
	if (ptr == nullptr || ptr->data != e) return false;       // 空树 或者 本身就没找到(此时是ptr==phit)
	if (!HAS_LCHILD(*this->root))                             // 单分支情况
	{
		this->root = this->root->rchild;
		if (this->root) this->root->parent = nullptr;          // 逻辑还是出了点问题,新的root有可能为null呀
	}
	else if (!HAS_RCHILD(*this->root))
	{
		this->root = this->root->lchild;
		if (this->root) this->root->parent = nullptr;                         // 同184
	}
	else {                                                    // 双分支情况
		bin_node_posi<T> ltree = this->root->lchild;
		bin_node_posi<T> rtree = this->root->rchild;
		this->root->lchild = nullptr;                   // 这步别忘了,摘除根节点
		this->root->rchild = nullptr;
		ltree->parent = nullptr;
		rtree->parent = nullptr;
		/*this->root = rtree->search(e) */              // 再来一次失败的查找,这里有问题呀,rtree 不能调用search()
		this->root = rtree;
		search(e);
		ltree->parent = this->root;
		this->root->lchild = ltree;                // 这里为什么可以直接这样接入左子树,  /*请思考.........
	}
	if (this->root) this->update_height(this->root);        // 同184,只要更新根节点的高度即可
	release(ptr->data);
	release(ptr);
	return true;
}
int main()
{
	srand((unsigned)time(nullptr));                       // 以时间作为种子,初始化随机数
	Splay<int> splay;
	Visit<int> visit;
	for (int i = 0; i < 20; ++i)
	{
		int num = i + rand() % 32;
		cout << num << " ";
		splay.insert(num);
	}
	cout << endl;
	splay.inorder_tra(visit);
	for (int i = 0; i < 10; i++)
	{
		int num = rand() % 32 + 1;
		cout << endl << "the num to remove is : " << num << endl;
		splay.remove(num);
		splay.inorder_tra(visit);
	}
}
/**
* 番外篇:
* 01.// spaly 是protected 成员,不能这样写
//template<typename T> bin_node_posi<T> Splay<T>::splay(bin_node_posi<T> v)
//{
//
//}
* 02.本节最容易忽略的一点,在伸展操作完成后,之前变量所记录的指针信息,早已发生变化,
* 本节犯的错误就是所谓的  /*刻舟求剑/
*/

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值