优点:伸展树(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)的实现的更多相关文章

  1. 伸展树(三)之 Java的实现

    概要 前面分别通过C和C++实现了伸展树,本章给出伸展树的Java版本.基本算法和原理都与前两章一样.1. 伸展树的介绍2. 伸展树的Java实现(完整源码)3. 伸展树的Java测试程序 转载请注明 ...

  2. K:伸展树(splay tree)

      伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(lgN)内完成插入.查找和删除操作.在伸展树上的一般操作都基于伸展操作:假设想要对一个二叉查找树执行一系列的查找操作,为了使 ...

  3. SplayTree伸展树的非递归实现(自底向上)

    Splay Tree 是二叉查找树的一种,它与平衡二叉树.红黑树不同的是,Splay Tree从不强制地保持自身的平衡,每当查找到某个节点n的时候,在返回节点n的同时,Splay Tree会将节点n旋 ...

  4. [SinGuLaRiTy] SplayTree 伸展树

    [SinGuLaRiTy-1010]Copyrights (c) SinGuLaRiTy 2017. All Rights Reserved. Some Method Are Reprinted Fr ...

  5. 伸展树(一)之 图文解析 和 C语言的实现

    概要 本章介绍伸展树.它和"二叉查找树"和"AVL树"一样,都是特殊的二叉树.在了解了"二叉查找树"和"AVL树"之后, ...

  6. 伸展树(二)之 C++的实现

    概要 上一章介绍了伸展树的基本概念,并通过C语言实现了伸展树.本章是伸展树的C++实现,后续再给出Java版本.还是那句老话,它们的原理都一样,择其一了解即可. 目录1. 伸展树的介绍2. 伸展树的C ...

  7. PHP算法 《树形结构》 之 伸展树(1) - 基本概念

    伸展树的介绍 1.出处:http://dongxicheng.org/structure/splay-tree/ A. 概述 二叉查找树(Binary Search Tree,也叫二叉排序树,即Bin ...

  8. [置顶] hdu 1890 伸展树区间翻转

    题意: 给你n个数,每次先输出第i大的数的位置(如果有多个,选下标小的那个),然后每次将第i个位置到第i大的数所在位置之间的数进行翻转. 思路:输入的数组可能有多个相同的值,我们可以进行两次排序把数组 ...

  9. Hdu3487-Play with Chain(伸展树分裂合并)

    Problem Description YaoYao is fond of playing his chains. He has a chain containing n diamonds on it ...

随机推荐

  1. Bezier曲线原理—动态解释

    公式线性公式给定点P0.P1,线性贝兹曲线只是一条两点之间的直线.且其等同于线性插值.这条线由下式给出: 一阶贝赛尔曲线上的由两个点确定 P0 和P1,当t在0--->1区间上递增时,根据此会得 ...

  2. 使用 Task.Wait()?立刻死锁(deadlock)

    最近读到一篇异步转同步的文章,发现其中没有考虑到异步转同步过程中发生的死锁问题,所以特地在本文说说异步转同步过程中的死锁问题. 文章作者 林德熙 已经修复了描述: - win10 uwp 异步转同步 ...

  3. tomcat  nginx  证书切换

    1. 导出公钥 keytool -export -alias tomcat -keystore <you jks>wsriakey.keystore -file <outputfil ...

  4. Toxiproxy 网络情况模式代理

    1. 介绍 Toxiproxy is a framework for simulating network conditions. It's made specifically to work in ...

  5. 几个开源ssg 技术方案

    1. Nanoc    2. Middle Man App   3. Hexo   4. DocPad   5. Hugo   6. Jekyll   7. Octopress   8. Harp   ...

  6. PHP---如何修改域名的指定的根目录

    如何修改域名的指定的根目录 环境:linux 使用工具:xShell 修改域名指定的文件根目录需要修改nginx的配置文件 第一步:连接xShell 第二步:进入根路径找到nginx的配置文件 cd ...

  7. (转)如何获得当前ListVIew包括下拉的所有数据?

    ListView listView = activity.getListView();获取的仅仅是当前屏幕显示的list,但是具有下拉信息,不在当前屏幕,但是下拉显示的数据无法或得到.谁知道如何获得当 ...

  8. linux误删数据恢复

    linux下数据恢复工具有: 1.通过分析文件系统的日志,解析文件的inode,可以恢复ex3 ex4的文件系统下的数据 extundelete:扫描inode和恢复数据同时进行,因此恢复速度很快.支 ...

  9. javascript系列学习----Creating objects

    在javascript语言里面,一切皆是对象,对象是它的灵魂,值得我们学习和领悟对象的概念和使用,下面我会引用实例来进行说明. 1)创建对象 方法一:js的对象方法构造 var cody = new ...

  10. mac 电脑下添加 HTMLtestrunner.py 生成 报表

    HTMLTestRunner是Python标准库unittest模块的一个扩展.它生成易于使用的HTML测试报告. 1.下载HTMLTestRunner.py模块地址 http://tungwaiyi ...