学习来源:计蒜客

平衡树


1.定义


对于每一个结点。左右两个子树的高度差的绝对值不超过1,或者叫深度差不超过1
为什么会出现这样一种树呢?

假如我们依照1-n的顺序插入到二叉排序树中,那么二叉排序树就退化成了一个有序链表,效率大大减少。

2.有关概念


全部平衡树基本由下面三个特征组成:

1.自平衡条件

2.旋转操作

3.旋转的触发

平衡树通过设置合理的自平衡条件,使得二叉排序树的查找、插入等操作的性能不至于退化到 O(n)O(n),而且在进行二叉排序树的查找、插入等操作时进行推断。假设满足当中某个旋转的触发条件,则进行相应的旋转操作。

左旋和右旋:(我的理解)

左旋:右边的深度大于左边深度2以上,把它变成平衡树,叫左旋(由于要把右边的某些元素挪到左边)

右旋:左边的深度大于右边深度2以上,把它变成平衡树。叫右旋(由于要把左边的某些元素挪到右边)

多旋:1.先左旋后右旋 2.先右旋在左旋



SB Tree


这个事实上不是很熟悉,程序是计蒜客的。大概理解吧。做点笔记,只是凝视都是自己的理解


1.定义


他也是平衡树的一种。每一个结点所在子树的结点个数不小于其兄弟的两个孩子所在子树的结点个数。
还有SBTree 仅仅有在插入时才可能触发调整。

据说他有个能够查找第k小的元素这个特殊的特点,为什么会有这个特点呢。
首先说个概念: 树的大小:所含结点的个数, 还有的话SB Tree也是属于排序树
SB Tree的结点的数据结构中用size储存了以当前结点为根构成的树的大小,那么它的父亲就排在size+1位了。详细看代码更好理解

2.实现


以下没凝视的函数请看这个(二叉排序树):http://blog.csdn.net/u012763794/article/details/50967631
#include <iostream>

using namespace std;

class SBTNode {
public:
//size:子树大小(就是以当前结点为根构成的树有多少结点)
//data:权值。就是树上的结点储存的值
//value:应该是暂时储存权值的
int data, size, value;
SBTNode * lchild, * rchild, * father;
//构造函数,參数分别为 权值,以当前结点为根的树的大小,父亲结点
SBTNode(int init_data, int init_size = 0, SBTNode * init_father = NULL);
~SBTNode();
//以下依次是
//二叉排序树的插入,搜索,找前驱,找后继,移除某个度为0或1的结点,移除某个权值的点。找出第k大的元素
void insert(int value);
SBTNode * search(int value);
SBTNode * predecessor();
SBTNode * successor();
void remove_node(SBTNode * delete_node);
bool remove(int value);
int select(int k);
}; class BinaryTree {
private:
SBTNode * root;
public:
BinaryTree();
~BinaryTree();
//以下依次是
//二叉树的插入 查找 删除结点 找出第k大的树。都是以上面的结点类的函数为基础的
void insert(int value);
bool find(int value);
bool remove(int value);
int select(int k);
}; //这里搞了个权值为0的结点,避免在边界情况时对空指针(NULL)进行特判,所以将全部原本指向空指针的情况都改为指向一个 ZPTR,并将其 size 设置为 0,从而减少代码复杂度。
SBTNode ZERO(0);
SBTNode * ZPTR = &ZERO; SBTNode::SBTNode(int init_data, int init_size, SBTNode * init_father) {
data = init_data;
size = init_size;
lchild = ZPTR;
rchild = ZPTR;
father = init_father;
} SBTNode::~SBTNode() {
if (lchild != ZPTR) {
delete lchild;
}
if (rchild != ZPTR) {
delete rchild;
}
} //左旋:将右孩子变为“根结点”(当前子树的根结点)。右孩子的左孩子就变成原来的根结点的右孩子
//以下凝视中:node(原来的根结点)的右孩子用“根结点”来说
SBTNode * left_rotate(SBTNode * node) {
//用temp保存“根结点”
SBTNode * temp = node->rchild;
//“根结点”的左孩子 变成node(原来的根结点)的右孩子
node->rchild = temp->lchild;
//更新“根结点”原来的左孩子的父亲为node(原来的根结点)
temp->lchild->father = node;
//node(原来的根结点) 就变成“根结点”的左孩子
temp->lchild = node;
//“根结点”的父亲更新为node(原来的根结点)的父亲
temp->father = node->father;
//node(原来的根结点)的父亲更新为“根结点”
node->father = temp;
//“根结点”的大小更新为node(原来的根结点)的大小(这里的大小是以该结点为根构成的树的结点的个数)
temp->size = node->size;
//node(原来的根结点)的大小更新为 它左孩子和右孩子的大小再在上本身1
node->size = node->lchild->size + node->rchild->size + 1;
//返回左旋后的根结点
return temp;
} //右旋:将左孩子变为“根结点”(当前子树的根结点)。左孩子的右孩子就变成原来的根结点的左孩子
//以下凝视中:node(原来的根结点)的左孩子用“根结点”来说
//反正这里跟上差点儿相反
SBTNode * right_rotate(SBTNode * node) {
//用temp保存“根结点”
SBTNode * temp = node->lchild;
//“根结点”的右孩子 变成node(原来的根结点)的左孩子
node->lchild = temp->rchild;
//更新“根结点”原来的右孩子的父亲为node(原来的根结点)
temp->rchild->father = node;
//node(原来的根结点) 就变成“根结点”的右孩子
temp->rchild = node;
//“根结点”的父亲更新为node(原来的根结点)的父亲
temp->father = node->father;
//node(原来的根结点)的父亲更新为“根结点”
node->father = temp;
//“根结点”的大小更新为node(原来的根结点)的大小(这里的大小是以该结点为根构成的树的结点的个数)
temp->size = node->size;
//node(原来的根结点)的大小更新为 它左孩子和右孩子的大小再在上本身1
node->size = node->lchild->size + node->rchild->size + 1;
//返回右旋后的根结点
return temp;
} //利用上面的左右旋进行调整的函数
//flag为false:处理左子树更高的情况,否则处理右子树更高的情况
//node:要调整的子树的根结点
SBTNode * maintain(SBTNode * node, bool flag) {
//左子树比右子树高(或者叫深度要深)
if (flag == false) {
//LL型:左子树的左子树的元素个数大于右子树的元素个数。应进行右旋
if (node->lchild->lchild->size > node->rchild->size) {
//右旋并更新子树的根结点
node = right_rotate(node);
}
//LR型:左子树的右子树的元素个数大于右子树的元素个数
//那么先对左子树进行左旋,就变成LL型。再右旋就可以
else if (node->lchild->rchild->size > node->rchild->size) {
//左旋并更新左子树的根结点
node->lchild = left_rotate(node->lchild);
//右旋并更新根节点
node = right_rotate(node);
} else {
//说明平衡了,返回根节点
return node;
}
//右子树比左子树高(或者叫深度要深)
} else {
//RR型:右子树的右子树的元素个数大于左子树的元素个数,应进行左旋
if (node->rchild->rchild->size > node->lchild->size) {
//左旋并更新根节点
node = left_rotate(node);
}
//RL型: 右子树的左子树的元素个数大于左子树的元素个数
//那么先对右子树进行右旋,变成RR型,在左旋就可以
else if (node->rchild->lchild->size > node->lchild->size) {
//右旋并更新左子树的根结点
node->rchild = right_rotate(node->rchild);
//左旋并更新根节点
node = left_rotate(node);
} else {
//说明平衡了。返回根节点
return node;
}
}
//以下为递归调用。由于有时上面的调整过后。左子树和右子树的某个结点还是不平衡 //递归调用,处理可能左子树的左子树高度更高的情况
//false表示左子树较高
node->lchild = maintain(node->lchild, false);
//其右子树的右子树高度更高的情况
node->rchild = maintain(node->rchild, true);
//最后再对子树根结点的左右子树递归进行调整
node = maintain(node, false);
node = maintain(node, true);
//返回调整后的子树的根结点
return node;
} SBTNode * insert(SBTNode * node, int value) {
if (value == node->data) {
return node;
} else {
node->size++;
if (value > node->data) {
if (node->rchild == ZPTR) {
node->rchild = new SBTNode(value, 1, node);
} else {
node->rchild = insert(node->rchild, value);
}
} else {
if (node->lchild == ZPTR) {
node->lchild = new SBTNode(value, 1, node);
} else {
node->lchild = insert(node->lchild, value);
}
}
}
return maintain(node, value > node->data);
} SBTNode * SBTNode::search(int value) {
if (data == value) {
return this;
} else if (value > data) {
if (rchild == ZPTR) {
return ZPTR;
} else {
return rchild->search(value);
}
} else {
if (lchild == ZPTR) {
return ZPTR;
} else {
return lchild->search(value);
}
}
} SBTNode * SBTNode::predecessor() {
SBTNode * temp = lchild;
while (temp != ZPTR && temp->rchild != ZPTR) {
temp = temp->rchild;
}
return temp;
} SBTNode * SBTNode::successor() {
SBTNode * temp = rchild;
while (temp != ZPTR && temp->lchild != ZPTR) {
temp = temp->lchild;
}
return temp;
} void SBTNode::remove_node(SBTNode * delete_node) {
SBTNode * temp = ZPTR;
if (delete_node->lchild != ZPTR) {
temp = delete_node->lchild;
temp->father = delete_node->father;
delete_node->lchild = ZPTR;
} if (delete_node->rchild != ZPTR) {
temp = delete_node->rchild;
temp->father = delete_node->father;
delete_node->rchild = ZPTR;
}
if (delete_node->father->lchild == delete_node) {
delete_node->father->lchild = temp;
} else {
delete_node->father->rchild = temp;
}
temp = delete_node;
while (temp != NULL) {
temp->size--;
temp = temp->father;
}
delete delete_node;
} bool SBTNode::remove(int value) {
SBTNode * delete_node, * current_node;
current_node = search(value);
if (current_node == ZPTR) {
return false;
}
size--;
if (current_node->lchild != ZPTR) {
delete_node = current_node->predecessor();
} else if (current_node->rchild != ZPTR) {
delete_node = current_node->successor();
} else {
delete_node = current_node;
}
current_node->data = delete_node->data;
remove_node(delete_node);
return true;
} int SBTNode::select(int k) {
//rank表示当前结点在子树的排位
int rank = lchild->size + 1;
//若rank等于第k小的k。说明就是要找的值。直接返回权值就可以
if (rank == k) {
return data;
}else if (k < rank) {
//小于rank。就表明要找比当前结点更小的。就在左边查找
return lchild->select(k);
}else{
//大于就在右边咯
//这里为什么看k - rank呢。由于我们已经把前rank排除了。
//相当于我们要在右子树(把他当做一颗新的树去查找),所以排位当然要减去rank了
return rchild->select(k - rank);
}
} BinaryTree::BinaryTree() {
root = NULL;
} BinaryTree::~BinaryTree() {
if (root != NULL) {
delete root;
}
} void BinaryTree::insert(int value) {
if (root == NULL) {
//初始化时仅仅有根结点,所以子树大小为1
root = new SBTNode(value, 1);
} else {
root = ::insert(root, value);
}
} bool BinaryTree::find(int value) {
if (root->search(value) == NULL) {
return false;
} else {
return true;
}
} bool BinaryTree::remove(int value) {
return root->remove(value);
} int BinaryTree::select(int k) {
return root->select(k);
} int main() {
BinaryTree binarytree;
int arr[10] = { 16, 9, 20, 3, 18, 15, 6, 30, 7, 25 };
for (int i = 0; i < 10; i++) {
binarytree.insert(arr[i]);
}
cout<<"请输入第k小的元素:"<<endl;
int k;
cin >> k;
cout<<endl<<"第"<<k<<"小的元素为:"<<endl;
cout << binarytree.select(k) << endl;
return 0;
}

3.执行结果






重学数据结构系列之——平衡树之SB Tree(Size Blanced Tree)的更多相关文章

  1. 重学c#系列——字典(十一)

    前言 重学c#系列继续更新,简单看一下字典的源码. 看源码主要是解释一下江湖中的两个传言: 字典foreach 顺序是字典添加的顺序 字典删除元素后,字典顺序将会改变 正文 那么就从实例化开始看起,这 ...

  2. 重学c#系列——对c#粗浅的认识(一)

    前言 什么是c#呢? 首先你是如何读c#的呢?c sharp?或者c 井? 官方读法是:see sharp. 有没有发现开发多年,然后感觉名字不对. tip:为个人重新整理,如学习还是看官网,c# 文 ...

  3. 重学ES系列之新型数据结构Map应用

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. D&F学数据结构系列——B树(B-树和B+树)介绍

    B树 定义:一棵B树T是具有如下性质的有根树: 1)每个节点X有以下域: a)n[x],当前存储在X节点中的关键字数, b)n[x]个关键字本身,以非降序存放,因此key1[x]<=key2[x ...

  5. D&F学数据结构系列——二叉排序树

    二叉排序树(Binary Sort Tree) 定义:对于树中的每个结点X,它的左子树中所有关键字值小于X的关键字值,而它的右子树中所有关键字值大于X的关键字值. 二叉查找树声明: #ifndef _ ...

  6. D&F学数据结构系列——二叉堆

    二叉堆(binary heap) 二叉堆数据结构是一种数组对象,它可以被视为一棵完全二叉树.同二叉查找树一样,堆也有两个性质,即结构性和堆序性.对于数组中任意位置i上的元素,其左儿子在位置2i上,右儿 ...

  7. D&F学数据结构系列——红黑树

    红黑树 定义:一棵二叉查找树如果满足下面的红黑性质,则为一棵红黑树: 1)每个结点不是红的就是黑的 2)根结点是黑的 3)每个叶结点是黑的 4)如果一个结点是红的,它的两个儿子都是黑的(即不可能有两个 ...

  8. 重学Golang系列(一): 深入理解 interface和reflect

    前言 interface(即接口),是Go语言中一个重要的概念和知识点,而功能强大的reflect正是基于interface.本文即是对Go语言中的interface和reflect基础概念和用法的一 ...

  9. 重学c#系列——c# 托管和非托管资源(三)

    前言 c# 托管和非托管比较重要,因为这涉及到资源的释放. 现在只要在计算机上运行的,无论玩出什么花来,整个什么概念,逃不过输入数据修改数据输出数据(计算机本质),这里面有个数据的输入,那么我们的内存 ...

随机推荐

  1. Spring Boot (32) Lock 本地锁

    平时开发中,有时会双击提交表单造成重复提交,或者网速比较慢时还没有响应又点击了按钮,我们在开发中必须防止重复提交 一般在前台进行处理,定义个变量,发送请求前判断变量值为true,然后把变量设置为fal ...

  2. Intellij使用心得(四) -- 导入Eclipse的代码格式化文件

    https://my.oschina.net/flashsword/blog/137598

  3. Objective-C设计模式——中介者Mediator(对象去耦)

    中介者模式 中介者模式很好的诠释了迪米特法则,任意两个不相关的对象之间如果需要关联,那么需要通过第三个类来进行.中介者就是把一组对象进行封装,屏蔽了类之间的交互细节,使不同的类直接不需要持有对方引用也 ...

  4. taskctl命令行类(sh、exe、python新增scp)插件升级扩展

    转载自: http://www.taskctl.com/forum/detail_129.html 上次写了一个帖子 TASKCTL中不使用代理,通过ssh免密连接执行远程脚本配置(SSH插件扩展)h ...

  5. 基于C++的多态性动态判断函数

    这里先有一个问题: 问题描述:函数int getVertexCount(Shape * b)计算b的顶点数目,若b指向Shape类型,返回值为0:若b指向Triangle类型,返回值为3:若b指向Re ...

  6. x86汇编之十(使用字符串)

    x86汇编之十(使用字符串) 转自网络,出处不详 一.传送字符串 Intel提供了完整的字符串传送指令,就像是MOV指令一样. 1.MOVS指令 1)movs指令格式 把字符串从一个位内存位置传送到另 ...

  7. Eigen库笔记整理(一)

    首先熟悉Eigen库的用途,自行百度. 引入头文件: // Eigen 部分 #include <Eigen/Core> // 稠密矩阵的代数运算(逆,特征值等) #include < ...

  8. Eureka组件、Eureka自我保护模式

    Eureka包含两个组件:Eureka Server和Eureka Client   Eureka Server提供服务发现的能力,各个微服务启动时,会向Eureka Server注册自己的信息(例如 ...

  9. layui 动态表格之合并单元格

    需求: 下面用excel表格大概模拟下需求,左边是原来的,要改成右边这样的: ①第一步:再生成表格后调用此方法,以合并重复的单元格 done : function(res, curr, count) ...

  10. 验证DNS解析失败:解决办法之一

    今天晚上练习简单的DNS解析验证: 环境是在一台虚拟机上搭建,另一台虚拟机验证,步骤如下: 虚拟机A: 1.安装软件包 bind  和bind-chroot[root@svr7 ~]# yum -y ...