rbtree作为平衡二叉树在linux内核中的使用很广泛,如前文讲到的cfs调度类组织调度实体se,以及内存子系统中用来管理内存。
定义:
红黑树是接近平衡的二叉树,因为它的最大高度最大是最短高度的二倍。所谓的黑高相同的性质。每个节点不是黑色就是红色,且父节点和字节点不能都是红色。
特性:
- 父节点和子节点(任一个)不能同为红色。
- 根节点是黑色;
- 所有叶子节点和NIL节点是黑色。
- ”黑高“相同,任一节点通过右子树和左子树到达叶子节点所有路径的黑色节点数是相同的。(确保没有一条路径会比其他路径长出2倍)
- 最大高度:2log(N+1)
- 查找/删除/插入 :时间复杂度为O(logN), 插入最多需要2次旋转,删除最多需要3次旋转。
操作:
旋转操作见后面的AVL树中。
插入操作:
规定插入的节点为红色,因为红色节点不增加树的“黑高”,如果是黑色势必会增加“黑高”,打破平衡的几率更高些。
现在来分析插入操作的几种情况:
如果父节点是黑色或树为空,那么直接插入就好。依旧保持平衡。
如果父亲是红色,则势必打破rbtree的平衡性,需要自平衡操作,rbtree的自平衡操作是通过染色+旋转来实现的。
来分析具体情境,由于对称性,我们之分析,一只情况,比如是父亲的左孩子和右孩子是对称的,只是左旋便右旋而已。
情景1. 叔叔为红:,只需染色,然后再向上传递。
由于红黑树红色互斥特性,则祖父(PP) 为黑色。只需将父亲和叔叔染成黑色,然后将祖父染红,在把祖父当成当前节点,向上再调整。
情景二:叔叔为黑色(黑色节点或NIL),我们需要再细分,再分插入节点是父亲的左孩子还是有孩子,左孩子则直接旋转,恢复平衡;若是右孩子则需先右旋再左旋恢复平衡。具体分析:
我们之分析父亲是祖父的左孩子的情况,因为右孩子的情况是对称的。
7. 1 叔叔为黑色,我是父亲的左孩子。
先染色,将父亲和曾祖父的颜色互换,为右旋做准备,之后右旋操作,恢复平衡。
2.2 叔叔为黑色,我是父亲的右孩子,比2.2多了一步操作,调整思路,将右孩子通过左旋操作,变为情景2.2 "我是父亲的左孩子"的情况。
结论:插入操作的旋转,最多只需要两步旋转操作,可能需要多步染色操作。
rbtree是自底向上添加层级的,如果旋转染色后根节点变成红色,则要把根节点染成黑色,这样就增加了rbtree的“黑高”
删除操作:
二叉树的删除操作都可以转换为删除叶子节点的操作,只是将叶子节点的值替换到删除节点上,然后删除叶子节点,该叶子节点我们称它为替代节点。然后情况就简单了许多,只讨论删除叶子节点。
由于对称性,我们也只讨论一侧的情况。
情景1. 替代节点为红色,则直接删除,不会影响树的“黑高”。
情景2: 替代节点是黑色,删除会影响“黑高”,所以需要做染色旋转操作。再细分:
先来看可以直接恢复平衡的情况:
情景2.1.2.1
我(替代节点,即将删除)是黑色,兄弟是黑 + 兄弟的左孩子为黑,右孩子为红。
其他情况最后都可以转换成2.1.2.1的情况。
情景2.1.2.2:
我(替代节点,即将删除)是黑色,兄弟是黑+ 兄弟的左孩子为红,右孩子为黑。 通过染色右旋转换成2.1.2.1的情况。
情景2.1.2.3:
我(替代节点,即将删除)是黑色,兄弟是黑+ 兄弟的两孩子都为黑。 只给兄弟染色,然后将父亲当作当前节点,再向上调整。
情景2.1.1:
我(替代节点,即将删除)是黑色,兄弟是红色,由于对称性只讨论我是父亲的左孩子的情况。
转换成2.2.1.3的对称侧,兄弟节点是红色,兄的两个儿子是黑色,但是我是父亲的右孩子,解法是同理。
结论:所以删除操作最多需要三次旋转。
使用实例:
java集合中的TreeSet和TreeMap,C++ STL中的set、map,以及Linux虚拟内存的管理,
二叉树知识
二叉树python实现:
#-*- coding: UTF-8 -*-
class bNode:
def __init__(self, dat):
self.data = dat
self.lchild = None
self.rchild = None
class bTree:
def __init__(self):
self.root = None
# from left to right to insert
def insert(self, node): #广度优先插入
if self.root == None:
self.root = node
return
cur_node = self.root #board travel
queue = [cur_node]
while queue :
node_itr = queue.pop(0)
if node_itr.lchild:
queue.append(node_itr.lchild)
else:
node_itr.lchild = node
return
if node_itr.rchild:
queue.append(node_itr.lchild)
else:
node_itr.rchild = node
return
def first_travel(self, root, func):
if not root:
return
if func:
func(root.data)
self.first_travel(root.lchild, func)
self.first_travel(root.rchild, func)
def middle_travel(self, root, func):
if not root:
return
self.middle_travel(root.lchild, func)
if func:
func(root.data)
self.middle_travel(root.rchild, func)
def last_travel(self, root, func):
if not root:
return
self.last_travel(root.lchild, func)
self.last_travel(root.rchild, func)
if func:
func(root.data)
def broad_travel(self, root, func):
queue = [root]
while queue:
node = queue.pop(0)
func(node.data)
if node.lchild:
queue.append(node.lchild)
if node.rchild:
queue.append(node.rchild)
def printf(data):
print("%d " % data)
def main():
root_tree = bTree()
root_tree.insert(bNode(8))
root_tree.insert(bNode(10))
root_tree.insert(bNode(12))
root_tree.insert(bNode(0))
root_tree.insert(bNode(100))
root_tree.insert(bNode(101))
print("first_travel:")
root_tree.first_travel(root_tree.root, printf)
print("middle_travel:")
root_tree.middle_travel(root_tree.root, printf)
print("last_travel:")
root_tree.last_travel(root_tree.root, printf)
print("broad_travel:")
root_tree.broad_travel(root_tree.root, printf)
if __name__ == "__main__":
main()
二叉搜索树
二叉搜索树,又名二叉排序树,是按顺序插入,左子树都比根节点小,右子树都比根节点大。
# a value is only once
class bsTree(bTree):
def insert(self, node):
if self.root == None:
self.root = node
return
cur_node = self.root
while cur_node:
if cur_node.data < node.data:
if cur_node.rchild:
cur_node = cur_node.rchild
else:
cur_node.rchild = node
elif cur_node.data > node.data:
if cur_node.lchild:
cur_node = cur_node.lchild
else:
cur_node.lchild = node
else: #already have
return
def delete(self, data):
if self.root == None:
return
cur_node = self.root
parent_node = None
b_lchild = True # 0:left child 1:right child
while cur_node:
if cur_node.data < data:
if cur_node.rchild:
parent_node = cur_node
b_lchild = False
cur_node = cur_node.rchild
elif cur_node.data > data:
if cur_node.lchild:
parent_node = cur_node
cur_node = cur_node.lchild
else: #find
if not cur_node.lchild and not cur_node.rchild: ## no child
if parent_node:
if b_lchild:
parent_node.lchild = None
else :
parent_node.rchild = None
else : #root
self.root = None
return
else :
if not cur_node.lchild or not cur_node.rchild: ## with one child
if cur_node.lchild:
if parent_node:
if b_lchild:
parent_node.lchild = cur_node.lchild
else :
parent_node.rchild = cur_node.lchild
else:
self.root = cur_node.lchild
return
if cur_node.rchild:
if parent_node:
if b_lchild:
parent_node.lchild = cur_node.rchild
else :
parent_node.rchild = cur_node.rchild
else:
self.root = cur_node.rchild
return
else: ## with two child
tmp_node = cur_node.rchild
if not tmp_node.lchild:
if parent_node:
if b_lchild:
parent_node.lchild = tmp_node
else :
parent_node.rchild = tmp_node
else:
tmp_node.lchild = self.root.lchild
self.root = tmp_node
return
while tmp_node.lchild.lchild:
tmp_node = tmp_node.lchild
cur_node.data = tmp_node.lchild.data ## switch with right child 's leftmost
tmp_node.lchild = None
def main_bstree():
root_tree = bsTree()
root_tree.insert(bNode(8))
root_tree.insert(bNode(10))
root_tree.insert(bNode(12))
root_tree.insert(bNode(0))
root_tree.insert(bNode(100))
root_tree.insert(bNode(101))
print("first_travel:")
root_tree.first_travel(root_tree.root, printf)
print("middle_travel:")
root_tree.middle_travel(root_tree.root, printf)
print("last_travel:")
root_tree.last_travel(root_tree.root, printf)
print("broad_travel:")
root_tree.broad_travel(root_tree.root, printf)
root_tree.delete(10)
print("2 middle_travel:")
root_tree.middle_travel(root_tree.root, printf)
if __name__ == "__main__":
main_bstree()
平衡二叉搜索树AVL
二叉搜索树的时间复杂度最坏的情况下可能退化成链表,时间复杂度为O(n); 二叉树的搜索次数为树的层级。调整搜索树的层级,使其达到平衡均匀,看看平衡的二叉树的策略。
平衡搜索二叉树,要点左右子树的高度差不超过1;也就是小于2。那么AVL的搜索时间复杂度就是O(logn)。
AVL在插入和删除的时候可能会打破其平衡结构,所以在插入/删除时需要做旋转操作,维持其平衡。由插入节点的层逐层向上判断是否失去平衡,若失去,则作旋转,回复平衡,这个代码中可以通过递归来实现。
旋转操作
LL/RR旋转
特点:搜索二叉树特性,左子树<root<右子树。
LL/RR 结构则是出现在排序后的两端,,即大端或小端,即左子树的左节点,或右子树的右节点。由于类似,我们只讨论LL旋转。
图示:
旋转三步走:
1.断,2连,3替换。
LR/RL旋转
特点:插入的节点在排序后的中间,即左子树的右节点或右子树的左节点。则需要经过两次旋转,才能达到平衡。
以LR为例。先右旋调整为LL结构,在左旋达到平衡。
图示:
先以B为基准进行RR旋转,转化成LL结构,在以A为基准进行LL旋转达到平衡。
注:写代码时基准都是A,因为A的左右子树高度差>1了,所以做递归的判断,函数实现传入的参数为A。
代码实现:
要点,删除/插入后,判断高度差,大于2则要调整。每一层都要做。
改善AVL,如果插入时高度差没变化,则不在向上做判断和调整。
代码:
#include<bits/stdc++.h>
using namespace std;
struct Node{
Node *left;
Node *right;
int val;
int height;
void UpdateHeight(){
int Left = left ? left->height : 0;
int Right = right ? right->height : 0;
height = max(Left, Right) + 1;
}
int cal_height(){
int Left = left ? left->height : 0;
int Right = right ? right->height : 0;
return Left - Right;
}
};
Node* LL(Node* node){
Node* Right = node->right;
Node* Left = Right->left;
Right->left = node;
node->right = Left;
node->UpdateHeight();
Right->UpdateHeight();
return Right;
}
Node* RR(Node* node){
Node* Left = node->left;
Node* Right = Left->right;
Left->right = node;
node->left = Right;
node->UpdateHeight();
Left->UpdateHeight();
return Left;
}
Node *RL(Node* node){
node->right = RR(node->right);
return LL(node);
}
Node *LR(Node* node){
node->left = LL(node->left);
return RR(node);
}
Node* Insert(Node* node, int val){
if(node == NULL){
node = new Node();
node->val = val;
// return node;
}
else if(val > node->val){
node->right = Insert(node->right, val);
int Height = node->cal_height();
if(Height == -2){
if(val > node->right->val){
node = LL(node);
}
else node = RL(node);
}
}
else {
node->left = Insert(node->left, val);
int Height = node->cal_height();
if(Height == 2){
if(val < node->left->val){
node = RR(node);
}
else node = LR(node);
}
}
node->UpdateHeight();
return node;
}
Node *Delete(Node* node, int val){
if(node == NULL){
return NULL;
}
else if(node->val > val){
node->left = Delete(node->left, val);
}
else if(node->val < val){
node->right = Delete(node->right, val);
}
else {
if(node->left){
Node* tmp;
for(tmp = node->left; tmp->right != NULL; tmp = tmp->right);
node->val = tmp->val;
node->left = Delete(node->left, node->val);
}
else if(node->right){
Node *tmp;
for(tmp = node->right; tmp->left != NULL; tmp = tmp->left);
node->val = tmp->val;
node->right =Delete(node->right, node->val);
}
else {
delete(node);
return NULL;
}
}
if(node->cal_height()==2)
{
if(node->left->cal_height()==1)
node = RR(node);
else node = LR(node);
}
else if(node->cal_height()==-2)
{
if(node->right->cal_height()==-1)
node = LL(node);
else node = RL(node);
}
node->UpdateHeight();
return node;
}
void pre_travel(Node* node)
{
if(node==NULL) return;
printf("%d ",node->val);
pre_travel(node->left);
pre_travel(node->right);
}
int main(){
Node *root = NULL;
root = Insert(root, 5);
// cout << 5 << endl;
root = Insert(root, 6);
// cout << 6 << endl;
root = Insert(root, 3);
// cout << 3 << endl;
// root = Insert(root, 3);
root = Insert(root, 2);
// cout << 2 << endl;
root = Insert(root, 4);
// cout << 4 << endl;
root = Insert(root, 1);
// cout << 1 << endl;
root = Insert(root, 9);
cout << 9 << endl;
root = Insert(root, 8);
cout << 8 << endl;
root = Insert(root, 7);
cout << 7 << endl;
root = Delete(root,6);
root = Delete(root,1);
printf("\n先序遍历:\n");
pre_travel(root);
printf("\n");
printf("\n");
return 0;
}
https://www.jianshu.com/p/e136ec79235c
https://www.cnblogs.com/letlifestop/p/11587354.html