伸展树(SplayTree)的实现
优点:伸展树(splay tree)是一种能自我调整的二叉搜索树(BST)。虽然某一次的访问操作所花费的时间比较长,但是平摊(amortized) 之后的访问操作(例如旋转)时间能达到O(logn)的复杂度。对于某一个被访问的节点,在接下来的一段时间内再次频繁访问它(90%的情况下是这样的,即符合90-10规则,类似于CPU内或磁盘的cache设计原理)的应用模式来说,伸展树是一种很理想的数据结构。另外一点与其他平衡二叉树的区别是,伸展树不需要存储任何像AVL树中平衡因子(balance factor)那样的平衡信息,可以节省空间的开销。
缺点:不像其他平衡二叉树那样即使最坏情况下也能达到O(logn)访问时间,它的最坏情况下只有O(n),跟单向链表一样。另外,伸展树的查找操作会修改树的结构,这与普通意义上的查找为只读操作习惯不太一样。
实现方式:伸展树的实现有两种方式,一是自底向上(bottom-up),另外一种是自顶向下(top-down)。
考虑到实现的难易程度,自顶向下的实现方式比较简单,因为自底向上需要保存已经被访问的节点,而自顶向下可以在搜索的过程中同时完成splay操作。
虽然两者得出的树结构不太一样,但是它们的平摊时间复杂度都是O(logn)。两种实现的基本操作就是splay,splay将最后被访问到的节点提升为根节点。
在自顶向下(top-down)的实现中,需要将输入的树拆成三颗树,分别为左树L,中树M和右树R。其中M树维护当前还未被访问到的节点,L树中所有节点的值都小于M树中的任何节点值,R树中所有节点的值都大于M树中的任何节点值。L树中只需要知道当前的最大节点 (leftTreeMax),而R树中只需要知道当前的最小节点(rightTreeMin)。左右两棵树的根节点分别可以通过nullNode节点(它是leftTreeMax和rightTreeMin的初始值,而且splay过程中变量nullNode本身未变化,只改变它的左右孩子节点)的右和左孩子节点得到,因为leftTreeMax中加入一个新的节点或子树时都是将新的节点作为leftTreeMax的右孩子,而不是左孩子(注意这里的顺序),rightTreeMin跟leftTreeMax相反。自顶向下的zig-zig或zag-zag需要做旋转操作,zig-zig的旋转操作叫rotationWithLeftChild,旋转后目标节点的父节点和祖父节点加入R树,zag-zag的旋转操作叫rotationWithRightChild,旋转后目标节点的父节点和祖父节点加入L树。另外zig-zag或zag-zig可以分别简化为zig或zag操作,这样可以将zig-zag和zig合二为一,从而只需考虑一种情况,而不需要将两种情况单独考虑。zig操作将目标节点的父节点加入R树,zag操作将目标节点的父节点加入L树。注意L和R树中每次加入新节点都需更新变量leftTreeMax或rightTreeMin。自顶向下splay操作的最后一步是重组(re-assemble):将M树的左孩子设置为L树的根节点,将M树的右孩子设置为R树的根节点,然后M树原来的左孩子成为leftTreeMax的右孩子,M树原来的右孩子成为rightTreeMin的左孩子。
注:rotationWithRightChild和rotationWithLeftChild的实现省略。
以下是类定义
#ifndef _splaytree_h_
#define _splaytree_h_ template<typename Comparable>
class SplayTree
{
public:
SplayTree()
{
nullNode = new BinaryNode;
nullNode->left = nullNode->right = nullNode;
root = nullNode;
} ~SplayTree()
{
makeEmpty();
delete nullNode;
} SplayTree(const Splay& rhs);
const SplayTree& operator=(const SplayTree&rhs);
// 此处省略操作方法 private:
struct BinaryNode
{
Comparable element;
BinaryNode *left;
BinaryNode *right; BinaryNode(const Comparable& theElement, BinaryNode *lt, BinaryNode* rt) :element(theElement), left(lt), right(rt){}
}; BinaryNode *root;
BinaryNode *nullNode;
// Internal method to perform a top-down splay.
void insert(const Comparable& x, BinaryNode*&t)const;
void remove(const Comparable& x, BinaryNode*&t)const;
BinaryNode* findMin(BinaryNode* t)const;
BinaryNode* findMax(BinaryNode* t)const;
bool contains(const Comparable&x, BinaryNode*t)const;
void makeEmpty(BinaryNode*&t);
void printTree(BinaryNode*t)const;
BinaryNode* clone(BinaryNode*t)const; void rotationWithLeftChild(BinaryNode*&k2);
void rotationWithRightChild(BinaryNode*& k1);
void splay(const Comparable& x, BinaryNode *&t);
}; #endif
接着splay的实现: 1 #include"splaytree.h"
template<typename Comparable>
void SplayTree<Comparable>::splay(const Comparable& x, BinaryNode*& t)
{
BinaryNode *leftTreeMax, *rightTreeMin;
static BinaryNode header; header.left = header.right = nullNode; //nullNode逻辑上表示一个NULL指针
leftTreeMax = rightTreeMin = &header; nullNode->element = x; //guarantee a match. for (;;)
if (x < t->element)
{
if ((x < t->left->element))
rotationWithLeftChild(t);
if (t->left == nullNode)
break;
// Link Right
rightTreeMin->left = t;
rightTreeMin = t;
t = t->left;
}
else if (t->element < x)
{
if (t->right->element < x)
rotationWithRightChild(t);
if (t->right == nullNode)
break;
// Link Left
leftTreeMax->right = t;
leftTreeMax = t;
t = t->right;
}
else
break; leftTreeMax->right = t->left;
rightTreeMin->left = t->right;
t->left = header.right;
t->right = header.left;
}
header.left和header.right分别引用R和L的根。(这不是输入错误,而是遵守链的指向)
void insert(const Comparable& x)
{
static BinaryNode* newNode = NULL;
if (newNode == NULL)
newNode = new BinaryNode;
newNode->element = x; if (root == nullNode)
{
newNode->left = newNode->right = nullNode;
root = newNode;
}
else
{
splay(x, root);
if (x < root->element)
{
newNode->left = root->right;
newNode->right = root;
root->left = nullNode;
root = newNode;
}
else if (root->element < x)
{
newNode->right = root->right;
newNode.left = root;
root->right = nullNode;
root = newNode;
}
else
return;
}
newNode = NULL; // So next insert will call new.
}
remove和makeEmpty的实现:
void remove(const Comparable& x)
{
BinaryNode *newNode;
splay(x, root);
if (root->element != x)
return;
if (root->left == nullNode)
newTree = root->right;
else
{
newTree = root->left;
splay(x, newTree); // 在左子树中寻找最大的项,把它伸展到根部
newTree->right = root->right; //连接右子树
}
delete root;
root = newTree;
} void makeEmpty()
{
whiel(!isEmpty())
{
findMax();
remove(root->element);
}
}
伸展树(SplayTree)的实现的更多相关文章
- 伸展树(三)之 Java的实现
概要 前面分别通过C和C++实现了伸展树,本章给出伸展树的Java版本.基本算法和原理都与前两章一样.1. 伸展树的介绍2. 伸展树的Java实现(完整源码)3. 伸展树的Java测试程序 转载请注明 ...
- K:伸展树(splay tree)
伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(lgN)内完成插入.查找和删除操作.在伸展树上的一般操作都基于伸展操作:假设想要对一个二叉查找树执行一系列的查找操作,为了使 ...
- SplayTree伸展树的非递归实现(自底向上)
Splay Tree 是二叉查找树的一种,它与平衡二叉树.红黑树不同的是,Splay Tree从不强制地保持自身的平衡,每当查找到某个节点n的时候,在返回节点n的同时,Splay Tree会将节点n旋 ...
- [SinGuLaRiTy] SplayTree 伸展树
[SinGuLaRiTy-1010]Copyrights (c) SinGuLaRiTy 2017. All Rights Reserved. Some Method Are Reprinted Fr ...
- 伸展树(一)之 图文解析 和 C语言的实现
概要 本章介绍伸展树.它和"二叉查找树"和"AVL树"一样,都是特殊的二叉树.在了解了"二叉查找树"和"AVL树"之后, ...
- 伸展树(二)之 C++的实现
概要 上一章介绍了伸展树的基本概念,并通过C语言实现了伸展树.本章是伸展树的C++实现,后续再给出Java版本.还是那句老话,它们的原理都一样,择其一了解即可. 目录1. 伸展树的介绍2. 伸展树的C ...
- PHP算法 《树形结构》 之 伸展树(1) - 基本概念
伸展树的介绍 1.出处:http://dongxicheng.org/structure/splay-tree/ A. 概述 二叉查找树(Binary Search Tree,也叫二叉排序树,即Bin ...
- [置顶] hdu 1890 伸展树区间翻转
题意: 给你n个数,每次先输出第i大的数的位置(如果有多个,选下标小的那个),然后每次将第i个位置到第i大的数所在位置之间的数进行翻转. 思路:输入的数组可能有多个相同的值,我们可以进行两次排序把数组 ...
- Hdu3487-Play with Chain(伸展树分裂合并)
Problem Description YaoYao is fond of playing his chains. He has a chain containing n diamonds on it ...
随机推荐
- linux自学(三)之开启虚拟机
上一篇:linux自学(二)之centos7镜像安装 前面都已经铺垫好了,下面就是开始安装镜像了,真正的开启一台linux机器. 点击开启此虚拟机,如图: 之后就是等待了,然后就是开始配置,首先如下图 ...
- BootStrap FileInput 插件实现多文件上传前端功能
<!DOCTYPE html> <html> <head> <title>文件上传</title> <meta charset=&qu ...
- 51nod 1118 机器人走方格
M * N的方格,一个机器人从左上走到右下,只能向右或向下走.有多少种不同的走法?由于方法数量可能很大,只需要输出Mod 10^9 + 7的结果. 收起 输入 第1行,2个数M,N,中间用空格隔开 ...
- Flask第三篇——安装Flask
现在我们来安装Flask: Windows系统安装Flask 如果你的系统是Windows,那安装起来非常方便——pip install flask Mac系统安装Flask Mac安装Flask一般 ...
- [Luogu4233]射命丸文的笔记
luogu description 对于\(x\in[1,n]\)求\(x\)点强联通竞赛图中的哈密顿回路的期望个数膜\(998244353\). \(n\le10^5\) sol 首先\(n\)点竞 ...
- java 乐观锁CAS
乐观锁是一种思想,本身代码里并没有lock或synchronized关键字进行修饰.而是采用一种version. 即先从数据库中查询一条记录得到version值,在更新这条记录时在where条件中对这 ...
- quick3.3rc1导入工程到ADT
1.libcocos2dx 路径到/quick-3.3rc1/cocos/platform/android/java这一层 2.quick项目 路径到D:\codeide\test11\framew ...
- MySQL的安装配置教程
1. 官网下载ZIP压缩版本(本人电脑是64位的) x64bit MySQL Community 2. 解压到E:\SoftwareFiles\mysql-5.7.11-winx64 3. 在E:\S ...
- c/c++中system函数在Linux和windows下区别
windows 在windows下的system函数中命令可以不区别大小写! 功 能: 发出一个DOS命令 #include <stdlib.h> int system(char *com ...
- 编译Lichee(FridenlyARM NanoPi-M1)碰到的问题及解决办法
1. 提示libz.so.1找不到 需要在ubuntu上安装下面两个包: sudo apt-get install lib32ncurses5 ia32-libs 2. 提示xt_hl.o没有make ...