小张学linux内核之数据结构:2. rbtree

rbtree作为平衡二叉树在linux内核中的使用很广泛,如前文讲到的cfs调度类组织调度实体se,以及内存子系统中用来管理内存。

定义:
红黑树是接近平衡的二叉树,因为它的最大高度最大是最短高度的二倍。所谓的黑高相同的性质。每个节点不是黑色就是红色,且父节点和字节点不能都是红色。

特性:

  1. 父节点和子节点(任一个)不能同为红色。
  2. 根节点是黑色;
  3. 所有叶子节点和NIL节点是黑色。
  4. ”黑高“相同,任一节点通过右子树和左子树到达叶子节点所有路径的黑色节点数是相同的。(确保没有一条路径会比其他路径长出2倍)
  5. 最大高度:2log(N+1)
  6. 查找/删除/插入 :时间复杂度为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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值