- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
1.SBT树的概念及其相关性质
2.SBT树的四种违规类型
3.BST树的插入
4.SBT的删除
5.代码汇总
Size Balanced Tree(SBT)是一种平衡二叉查找树。它的论文由中国广东中山记念中学的陈启峰于2006年末完成,php
并在Winter Camp 2007中发表。因为SBT的拼写很容易找到中文谐音, 它常被中国的Oler们戏称为 “傻X树”、html
“Super BT”等。但它的性能并不SB,编写起来也并不BT。偏偏相反,SBT易于实现,且据陈启峰论文中所言 ,node
“这是目前为止速度最快的高级二叉搜索树”。它能在O(logn)的时间内完成全部BST的相关操做。并且因为SBT赖数组
以保持平衡的是Size域而不是其余“无用”的域,它能够很方便地实现动态顺序统计中的select和rank。数据结构
1.2SBT树相关性质:
任何一个叔叔节点的个数不能少于侄子节点的个数,举个例子如图所示:
大叔为T3和T4的叔叔节点,二叔为T1和T2的叔叔节点在SBT树种大叔节点的个数必须大于等于T3和T4任何一个节点的个数,二叔的节点个数必须大于T1和T2之中任何一个节点个数。
SBT树不像AVL树一样有这么严格的平衡性,SBT树这样规定的目的是为了保证左边的高度最大不超过右树的两倍或者右树的高度不会超过左树高度的两倍,具体证明请看大佬的文章
1.3SBT树节点的定义:
struct SBTNode
{
SBTNode(const pair<K, V>kv = pair<K, V>())
:_kv(kv)
,_left(nullptr)
,_right(nullptr)
,_size(1)
{}
pair<K, V>_kv;
SBTNode<K, V>* _left;
SBTNode<K, V>* _right;
int _size;//节点的个数
};
SBT树和AVL树一样一共有四种违规类型:
1.LL型违规
2.LR型违规
3.RR型违规
4.RL型违规
LL型违规
LL型违规是指大叔的左子树的节点个数大于了二叔的节点个数成为LL型违规和AVL树类似需要进行右旋 但与AVL树也有点不一样,具体请看下面的解释:
我们对根节点进行右旋之后,我们会发现大叔的孩子节点发生了变化,还有根节点的孩子也发生了变化,此时需要递归检查大叔和根节点是否满足SBT树的定义,这一点与AVL树有所不同。为什么孩子节点发生变化之后需要对其进行检查呢?这是因为之前它比较的对象发生了改变。
对应代码:
Node* rightRotate(Node* cur) {
Node* leftNode = cur->_left;
cur->_left = leftNode->_right;
leftNode->_right = cur;
leftNode->_size = cur->_size;//重新调整节点个数
cur->_size = (cur->_left ? cur->_left->_size : 0) + (cur->_right ? cur->_right->_size : 0)+1;//重新计算节点个数
return leftNode;//返回调整后的新头部
}
注意:递归调整放到maintain方法里面
RR型违规:
RR型违规是指T4节点的个数大于大叔的节点个数,调整方案和AVL树一样对根节点进行一个左旋。
同样的根节点和二叔这两个节点孩子节点发生改变,递归对这个两个节点进行检查看是否满足SBT树的定义。
对应代码:
Node* leftRotate(Node* cur) {
Node* rightNode = cur->_right;
cur->_right = rightNode->_left;
rightNode->_left = cur;
rightNode->_size = cur->_size;//重新调整节点个数
cur->_size = (cur->_left ? cur->_left->_size : 0) + (cur->_right ? cur->_right->_size : 0) + 1;//重新计算节点的个数
return rightNode;//返回新的头部
}
LR型违规:
LR型是指B节点的个数大于二叔节点的个数,其调整策略和AVL树一样先对大叔进行一个左旋,在对根进行一个右旋。
经过旋转之后我们发现根,大叔,还B这三个节点的左右孩子发生了变化,因此需要对其进行检查看是否满足SBT树的定义
RL型同理老铁们可以下去自己画一下。
对应调整代码:
Node* maintain(Node* cur) {
if (cur == nullptr) {
return nullptr;
}
//将节点的个数拿出来
int leftSize = cur->_left != nullptr ? cur->_left->_size : 0;
int leftLeftSize = cur->_left && cur->_left->_left ? cur->_left->_left->_size : 0;
int leftRightSize = cur->_left && cur->_left->_right ? cur->_left->_right->_size : 0;
int rightSize = cur->_right? cur->_right->_size : 0;
int rightLeftSize = cur->_right && cur->_right->_left ? cur->_right->_left->_size : 0;
int rightRightSize = cur->_right && cur->_right->_right ? cur->_right->_right->_size : 0;
if (leftLeftSize > rightSize) {//LL型
cur = rightRotate(cur);//右旋
cur->_right = maintain(cur->_right);//递归检查
cur = maintain(cur);
}
else if (leftRightSize > rightSize) {//LR型,调整完成之后递归检查
cur->_left = leftRotate(cur->_left);
cur = rightRotate(cur);
cur->_left = maintain(cur->_left);
cur->_right = maintain(cur->_right);
cur = maintain(cur);
}
else if (rightRightSize > leftSize) {//RR型,调整后并且检查
cur = leftRotate(cur);
cur->_left = maintain(cur->_left);
cur = maintain(cur);
}
else if (rightLeftSize > leftSize) {//RL型,调整后并检查
cur->_right = rightRotate(cur->_right);
cur = leftRotate(cur);
cur->_left = maintain(cur->_left);
cur->_right = maintain(cur->_right);
cur = maintain(cur);
}
return cur;//返回调整后的头部
}
BST树的插入和搜索二叉树的插入基本一样只不过多了调整部分如果不太清楚的老铁可以去看我之前的博客
对应代码:
// 现在,以cur为头的树上,新增,加(key, value)这样的记录
// 加完之后,会对cur做检查,该调整调整
// 返回,调整完之后,整棵树的新头部
Node* add(Node* cur, const pair<K, V>& kv) {
if (cur == nullptr) {
return new Node(kv);
}
else {
cur->_size++;
if (cur->_kv.first > kv.first) {
cur->_left = add(cur->_left, kv);//去左边插入
}
else {
cur->_right = add(cur->_right, kv);//去右边插入
}
}
return maintain(cur);//返回插入好的新头部
}
bool Insert(const pair<K, V>& kv) {
Node* lastNode = findLastIndex(kv.first);
if (lastNode && lastNode->_kv.first == kv.first) {//已经存在
return false;
}
_root = add(_root, kv);
return true;
}
SBT的删除和AVL树删除的情况基本一样至少调整方式不一样,如果不太清楚可以看一下我的哪篇文章。
Node* Delete(Node* cur, K key) {
cur->_size--;
if (cur->_kv.first > key) {
cur->_left = Delete(cur->_left, key);
}
else if (cur->_kv.first < key) {
cur->_right = Delete(cur->_right, key);//左边删并将新的头部返回
}
else {
if (!cur->_left && !cur->_right) {//左为空并且右为空
delete cur;
cur = nullptr;
}
else if (!cur->_left && cur->_right) {//左为空但右不为空
Node* subR = cur->_right;
delete cur;
cur = subR;
}
else if (cur->_left && !cur->_right) {//左不为空但右为空
Node* subL = cur->_left;
delete cur;
cur = subL;
}
else {//左右都不为空找后继节点
Node* pre = nullptr;
Node* des = cur->_right;
des->_size--;
while (des->_left) {
pre = des;
des = des->_left;
des->_size--;
}
if (pre) {//链接cur的左右孩子
pre->_left = des->_right;
des->_right = cur->_right;
}
des->_left = cur->_left;
des->_size = des->_left->_size + (des->_right ? des->_right->_size:0) + 1;//更新size
delete cur;
cur = des;
}
}
cur = maintain(cur);//平衡
return cur;//返回新头部
}
void Erase(K key) {
Node* lastNode = findLastIndex(key);
if(lastNode)
_root = Delete(_root,key);
else {
return;
}
}
#pragma once
#include<math.h>
#include<iostream>
#include<vector>
using namespace std;
template<class K,class V>
struct SBTNode
{
SBTNode(const pair<K, V>kv = pair<K, V>())
:_kv(kv)
,_left(nullptr)
,_right(nullptr)
,_size(1)
{}
pair<K, V>_kv;
SBTNode<K, V>* _left;
SBTNode<K, V>* _right;
int _size;//节点的个数
};
template<class K,class V>
class SizeBalancedTreeMap {
typedef SBTNode<K, V> Node;
public:
Node* rightRotate(Node* cur) {
Node* leftNode = cur->_left;
cur->_left = leftNode->_right;
leftNode->_right = cur;
leftNode->_size = cur->_size;//重新调整节点个数
cur->_size = (cur->_left ? cur->_left->_size : 0) + (cur->_right ? cur->_right->_size : 0)+1;//重新计算节点个数
return leftNode;//返回调整后的新头部
}
Node* leftRotate(Node* cur) {
Node* rightNode = cur->_right;
cur->_right = rightNode->_left;
rightNode->_left = cur;
rightNode->_size = cur->_size;//重新调整节点个数
cur->_size = (cur->_left ? cur->_left->_size : 0) + (cur->_right ? cur->_right->_size : 0) + 1;//重新计算节点的个数
return rightNode;//返回新的头部
}
Node* maintain(Node* cur) {
if (cur == nullptr) {
return nullptr;
}
//将节点的个数拿出来
int leftSize = cur->_left != nullptr ? cur->_left->_size : 0;
int leftLeftSize = cur->_left && cur->_left->_left ? cur->_left->_left->_size : 0;
int leftRightSize = cur->_left && cur->_left->_right ? cur->_left->_right->_size : 0;
int rightSize = cur->_right? cur->_right->_size : 0;
int rightLeftSize = cur->_right && cur->_right->_left ? cur->_right->_left->_size : 0;
int rightRightSize = cur->_right && cur->_right->_right ? cur->_right->_right->_size : 0;
if (leftLeftSize > rightSize) {//LL型
cur = rightRotate(cur);//右旋
cur->_right = maintain(cur->_right);//递归检查
cur = maintain(cur);
}
else if (leftRightSize > rightSize) {//LR型,调整完成之后递归检查
cur->_left = leftRotate(cur->_left);
cur = rightRotate(cur);
cur->_left = maintain(cur->_left);
cur->_right = maintain(cur->_right);
cur = maintain(cur);
}
else if (rightRightSize > leftSize) {//RR型,调整后并且检查
cur = leftRotate(cur);
cur->_left = maintain(cur->_left);
cur = maintain(cur);
}
else if (rightLeftSize > leftSize) {//RL型,调整后并检查
cur->_right = rightRotate(cur->_right);
cur = leftRotate(cur);
cur->_left = maintain(cur->_left);
cur->_right = maintain(cur->_right);
cur = maintain(cur);
}
return cur;//返回调整后的头部
}
// 现在,以cur为头的树上,新增,加(key, value)这样的记录
// 加完之后,会对cur做检查,该调整调整
// 返回,调整完之后,整棵树的新头部
Node* add(Node* cur, const pair<K, V>& kv) {
if (cur == nullptr) {
return new Node(kv);
}
else {
cur->_size++;
if (cur->_kv.first > kv.first) {
cur->_left = add(cur->_left, kv);//去左边插入
}
else {
cur->_right = add(cur->_right, kv);//去右边插入
}
}
return maintain(cur);//返回插入好的新头部
}
bool Insert(const pair<K, V>& kv) {
Node* lastNode = findLastIndex(kv.first);
if (lastNode && lastNode->_kv.first == kv.first) {//已经存在
return false;
}
_root = add(_root, kv);
return true;
}
Node* Delete(Node* cur, K key) {
cur->_size--;
if (cur->_kv.first > key) {
cur->_left = Delete(cur->_left, key);
}
else if (cur->_kv.first < key) {
cur->_right = Delete(cur->_right, key);//左边删并将新的头部返回
}
else {
if (!cur->_left && !cur->_right) {//左为空并且右为空
delete cur;
cur = nullptr;
}
else if (!cur->_left && cur->_right) {//左为空但右不为空
Node* subR = cur->_right;
delete cur;
cur = subR;
}
else if (cur->_left && !cur->_right) {//左不为空但右为空
Node* subL = cur->_left;
delete cur;
cur = subL;
}
else {//左右都不为空找后继节点
Node* pre = nullptr;
Node* des = cur->_right;
des->_size--;
while (des->_left) {
pre = des;
des = des->_left;
des->_size--;
}
if (pre) {//链接cur的左右孩子
pre->_left = des->_right;
des->_right = cur->_right;
}
des->_left = cur->_left;
des->_size = des->_left->_size + (des->_right ? des->_right->_size:0) + 1;//更新size
delete cur;
cur = des;
}
}
cur = maintain(cur);//平衡
return cur;//返回新头部
}
void Erase(K key) {
Node* lastNode = findLastIndex(key);
if(lastNode)
_root = Delete(_root,key);
else {
return;
}
}
Node* findLastIndex(K key) {
Node* pre = _root;
Node* cur = _root;
while (cur != nullptr) {
pre = cur;
if (cur->_kv.first==key) {
break;
}
else if (cur->_kv.first>key) {
cur = cur->_left;
}
else {
cur = cur->_right;
}
}
return pre;
}
Node* findLastNoSmallIndex(K key) {
Node* ans = nullptr;
Node* cur = _root;
while (cur != nullptr) {
if (cur->_kv.first==key) {
ans = cur;
break;
}
else if (cur->_kv.first>key) {
ans = cur;
cur = cur->_left;
}
else {
cur = cur->_right;
}
}
return ans;
}
Node* findLastNoBigIndex(K key) {
SBTNode<K, V> ans = nullptr;
Node* cur = _root;
while (cur != nullptr) {
if (cur->_kv.first==key) {
ans = cur;
break;
}
else if (cur->_kv.first>key) {
cur = cur->_left;
}
else {
ans = cur;
cur = cur->_right;
}
}
return ans;
}
bool containsKey(K key) {
Node* lastNode = findLastIndex(key);
return lastNode && lastNode->_kv.first==key? true : false;
}
void _Inorder(Node* root) {
if (!root)return;
_Inorder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_Inorder(root->_right);
}
void Inorder() {
_Inorder(_root);
}
private:
Node* _root = nullptr;
};
private boolean validateSno(double inSno) { //sno is the serial number int firstThree=(int)inSno
这个问题已经有答案了: How can I determine if a date is between two dates in Java? [duplicate] (11 个回答) 已关闭 6 年
我目前正在通过一本名为 Alex Allain - Jumping into c++ 的书学习 C++,但我卡在了第 21 章。它详细介绍了 C++ 构建过程,我明白了,除了 2 个部分: 首先: “
我有以下 Oracle 查询 SELECT * FROM table WHERE date_opened BETWEEN ((TO_DATE('2011-08-01', 'yyyy-mm-dd') -
use rand::Rng; fn main() { let mut zz = rand::thread_rng(); let mut a: [i32; 4096] = [0; 409
我正在使用 flatPickr(日历插件)来完成此任务。我将 minDate 和 maxDate(始终是星期日)从 PHP 函数发送到 JavaScript: $("#weeklySelector")
从 Eclipse 运行时,以下代码可以正常工作: System.setProperty("webdriver.gecko.driver", pathToGeckoDriver); FirefoxOp
我正在编写一个 Iphone 应用程序并使用 sqlite 作为我的数据库。如果对象在某个范围(MIN 和 MAX)之间,我有折扣,但问题是当我查询时,折扣似乎也适用于超出该范围的对象。不确定如何正确
Brunch 几乎适用于所有模板语言,并且有适用于它们的插件,但我无法使用 vanilla HTML。我只是希望在每次构建早午餐时只复制我的 html 文件(无论它们位于何处)并粘贴到公共(publi
首先我必须输入N,N成为第一个要检查的数字。 输入:79 输出应为:537.70。 int sum=0; while(1) { scanf("%d", &n
aa: { one: "hello", two: "good", three: "bye", four: "tomorrow",
我尝试解决方案,我知道这是不对的,因为程序的输出不正确。我做错了什么? 我有一个内部节点类,每个节点都有值字段。此方法应返回具有介于 int min 和 max 之间的值字段的节点数。 //-----
我正在尝试弄清楚如何执行 mysql 查询,在该查询中我返回以(例如)A-D 开头的结果,又名将返回: - 动物 - 银行 - 可乐 - 狗但不是:冰屋 看起来很简单,但找不到有效的方法。 我的意思是
我正在开发一个程序,我必须打印出 1 到 239 之间的素数,包括 1 和 239(我知道一和二可能不是素数,但我们会认为它们是素数这个程序)它一定是一个非常简单的程序,因为我们只讨论了一些基础知识。
我有一个错误,我在 jsbin 中复制了这个错误:https://jsbin.com/micinalacu/1/edit?html,console,output 铁形式,提交时serialize方法返
我正在尝试为 UIColor 定义扩展 import UIKit extension UIColor { convenience init(rgb:UInt){ let red
通过对 A 和 B(含)之间的一个或多个整数进行按位或运算,可以生成多少个不同的数字? 解释: 在这种情况下,A=7 和 B=9。对 {7, 8, 9} 的非空子集进行按位或运算可以生成四个整数:7,
在尝试将ignore_malformed添加到索引设置时,我需要帮助。 我有: from elasticsearch import Elasticsearch es = Elasticsearch(
我希望生成介于 1500 和 1650 之间的随机整数。 我已成功生成介于 25 和 55 之间的随机值(包括以下代码)。但是,我遇到的问题是,如果我修改代码以生成 1500 到 1650 之间的值(
背景 我正在使用 cx_Freeze 构建我的应用程序的 Windows 和 Mac 包;构建在两个平台上都成功执行,在 Windows 上生成 msi,在 Mac 上生成 dmg/app,我可以安装
我是一名优秀的程序员,十分优秀!