重学数据结构系列之——平衡树之SB Tree(Size Blanced Tree)

学习来源:计蒜客

平衡树

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.运行结果





  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值