#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.本节最容易忽略的一点,在伸展操作完成后,之前变量所记录的指针信息,早已发生变化,
* 本节犯的错误就是所谓的 /*刻舟求剑/
*/
二叉搜索树(三)---伸展树
最新推荐文章于 2024-06-15 16:46:40 发布