学习来源:计蒜客

平衡树


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. Mysql阿里数据源配置参考

    maven pom.xml配置 <dependency> <groupId>com.alibaba</groupId> <artifactId>drui ...

  2. BZOJ1499: [NOI2005]瑰丽华尔兹(dp)

    Description 你跳过华尔兹吗?当音乐响起,当你随着旋律滑动舞步,是不是有一种漫步仙境的惬意?众所周知,跳华尔兹时,最重要的是有好的音乐.但是很少有几个人知道,世界上最伟大的钢琴家一生都漂泊在 ...

  3. <a>标签的href、onclick属性

    链接的 onclick 事件被先执行,其次是 href 属性下的动作(页面跳转,或 javascript 伪链接): 参考:https://www.cnblogs.com/happykakeru/ar ...

  4. 联想 S5 Pro GT(L78091)免解锁BL 免rec 保数据 ROOT Magisk Xposed 救砖 ZUI5.0.047

    >>>重点介绍<<< 第一:本刷机包可卡刷可线刷,刷机包比较大的原因是采用同时兼容卡刷和线刷的格式,所以比较大第二:[卡刷方法]卡刷不要解压刷机包,直接传入手机后用 ...

  5. HDU_3732_(多重背包)

    Ahui Writes Word Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  6. HDU_2955_Robberies_01背包

    A - Robberies Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submi ...

  7. java string与byte互转

    1.string 转 byte[]byte[] midbytes=isoString.getBytes("UTF8");//为UTF8编码byte[] isoret = srt2. ...

  8. Error LNK2019: unresolved external symbol C++模板类声明与定义链接错误问题

    编译器在编译模板时,并不会生成代码,只有遇到实例化的时候才会生成代码.因此,当我们只引用模板声明文件的时候,在实例化的对象时候,模板的定义问文件是不可见的,于是出现链接错误.例如: //A.h #pr ...

  9. Linux下“任务管理器”

    也不知道linux叫不叫任务管理器. Ctrl+Alt+T打开终端,输入top,就会出现一堆东西. 如果有个东西未响应了,就可以输入k+这个进程的pid就可以杀死它. https://blog.csd ...

  10. Vue.Draggable实现拖拽效果(快速使用)

    1.下载包:npm install vuedraggable 配置:package.json "dependencies": { "element-ui": & ...