一般树与二叉树的区别
二叉树是一种重要的树形结构,其结构定义为:二叉树是n(n≥0)个结点的有限集,它或为空树(n=0),或由一个根结点和两棵分别称为根的左子树和右子树的、互不相交的二叉树组成。
既不是满二叉树也不是完全二叉树,则为一般二叉树
二叉树的每个结点至多有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。
二叉树存储方式一般分为两种,顺序存储与链表式存储
顺序存储:
链式存储:
顺序存储可能造成存储空间的浪费。
class Node:
def __init__(self,item,l_child=None, r_child=None):
self.item = item
self.l_child = l_child
self.r_child = r_child
#优点:寻找一个节点的孩子节点比较方便。
#缺点:寻找一个节点得双亲节点很不方便。
class Node:
def __init__(self,item,parent=None):
self.item = item
self.parent = parent
#优点:寻找一个节点得双亲节点操作实现很方便
#缺点:寻找一个节点的孩子节点很不方便
class Node:
def __init__(self,item,parent=None,l_child=None, r_child=None):
self.item = item
self.parent = parent
self.l_child = l_child
self.r_child = r_child
#优点:找某个节点的双亲节点和孩子节点非常方便
class Node:
def __init__(self,item,NextBrother=None,l_child=None, r_child=None):
self.item = item
self.NextBrother = NextBrother #指向其下一个兄弟节点
self.l_child = l_child
self.r_child = r_child
#优点:找某个节点的兄弟结点节点和孩子节点非常方便
一棵深度为k,且有2k - 1个节点的树是满二叉树。
通俗解释:除叶结点外,每一个结点都有左右子树,且叶结点都处在最底层的二叉树。
如上图a
所示,就是一个满二叉树,从图形上看,满二叉树是一个三角形。
完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h 层所有的结点都连续集中在最左边,这就是完全二叉树。
满二叉树一定是完全二叉树,完全二叉树不一定是满二叉树。
二叉排序树又叫二叉查找树或者二叉搜索树,它首先是一个二叉树,而且必须满足下面的条件:
A-B-D-H-I-E-J-C-F-K-G
H-D-I-B-E-J-A-F-K-C-G
H-I-D-J-E-B-K-F-G-C-A
A-B-C-D-E-F-G-H-I-J-K
class Node(object):
def __init__(self, item):
self.item = item
self.lchild = None
self.rchild = None
class Tree(object):
NodeList = []
def __init__(self):
self.root = None
def add(self, item):
node = Node(item)
if self.root == None:
self.root = node
Tree.NodeList.append(self.root)
else:
while True:
node = Tree.NodeList[0]
if node.lchild == None:
node.lchild = node
Tree.NodeList.append(node.lchild)
return
elif node.rchild == None:
node.rchild=node
Tree.NodeList.append(node.rchild)
Tree.NodeList.pop(0)
return
def traverse(self): # 层次遍历
if not self.root:
return None
NodeList = [self.root]
res = [self.root.item]
while NodeList != []:
pop_node = NodeList.pop(0)
if pop_node.lchild is not None:
NodeList.append(pop_node.lchild)
res.append(pop_node.lchild.item)
if pop_node.rchild is not None:
NodeList.append(pop_node.rchild)
res.append(pop_node.rchild.item)
return res
def preorder(self,root): # 先序遍历
if root is None:
return []
result = [root.item]
left_item = self.preorder(root.lchild)
right_item = self.preorder(root.rchild)
return result + left_item + right_item
def inorder(self,root): # 中序序遍历
if root is None:
return []
result = [root.item]
left_item = self.inorder(root.lchild)
right_item = self.inorder(root.rchild)
return left_item + result + right_item
def postorder(self,root): # 后序遍历
if root is None:
return []
result = [root.item]
left_item = self.postorder(root.lchild)
right_item = self.postorder(root.rchild)
return left_item + right_item + result
t = Tree()
for i in range(10):
t.add(i)
print('层序遍历:',t.traverse())
print('先序遍历:',t.preorder(t.root))
print('中序遍历:',t.inorder(t.root))
print('后序遍历:',t.postorder(t.root))
已知先序与中序遍历结果,可以推导出树形结构
已知中序与后序遍历结果,可以推导出树形结构
已知先序与后序遍历结果,无法推导出树形结构,因为无法判断根结点的之前或之后的结点是属于左子树还是右子树
红黑树(Red Black Tree) 是一种自平衡二叉查找树。红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。红黑树的性质有以下5点
平衡二叉树:又称AVL树,它或者是颗空树,或者是具有下列性质的二叉树:它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1。若将二叉树节点的平衡因子BF定义为该节点的左子树的深度减去它的右子树的深度,则平衡二叉树上所有节点的平衡因子只可能为-1,0,1。只要二叉树上有一个节点的平衡因子的绝对值大于1,那么这颗平衡二叉树就失去了平衡。
所谓以X为中心左旋,就是调整X与其右子树的关系,原 X 的右子节点 Y 变为父节点,将 X 父节点变为左子节点;那原先的子节点在旋转之后如何分布呢?其实不必要计较哪些规则语句,因为可以推导出来
推导步骤如下:
所谓以X为中心右旋,就是调整X与其左子树的关系,原 X 的左子节点 Y 变为父节点,将 X 父节点变为右子节点;那原先的子节点在旋转之后如何分布呢?
推导步骤如下:
红黑树首先是一棵二叉排序树,所以它的插入操作要遵循二叉排序树的插入原则;先把要插入的节点的key与根进行比较,小则和根的左孩子做比较,大则跟右孩子作比较,直到找到叶子节点。
现在按照二叉查找树的插入方式插入了节点21,那么这个21该是什么颜色呢?
1、如果是黑色,那么不管原来的红黑树是什么样的,这样一定会破坏平衡,因为原来的树是平衡的,现在在这一条路径上多了一个黑色,必然违反了性质5
2、如果是红色,那么也有可能会破坏平衡,主要可能是违反了性质4,新插入的点21的父节点22为红色。但是却有一种特殊情况,比如上图中,如果我插入一个key=0的节点。把0这个节点置为红色,并不会影响原来树的平衡,因为0的父节点是黑色。如下图:
所以选择插入的21节点为红色
21的颜色置为红色,他的父亲也是红色,违反性质4,需要调整,怎么调整呢?现在就要看新插入的点的叔叔节点了。
因为21的叔叔27是红色的。所以要保持平衡,可以把25设置成红色,22,27分别设置成黑色,
新的问题:17是红色的,孩子25也是红色的,违反了性质4,需要继续调整,将17、18调整为黑色,此时根结点根据变换规则会变成红色,则不符合性质2,因此继续调整根结点的颜色为黑色,至此所有规则都符合,插入完成。
这只是插入操作一个示例,总的来说红黑树的插入操作需要变色、左旋、右旋来完成,更详细的说明可以参考我下面列出的博客。
class RBNode:
def __init__(self, val, color="black"):
self.key = x
self.left = None
self.right = None
self.parent = None
self.color = 'black'
self.size=None
def is_black_node(self):
return self.color == "black"
def is_red_node(self):
return self.color == "red"
def set_black_node(self):
self.color = "black"
def set_red_node(self):
self.color = "red"
#定义红黑树
class RBTree(object):
def __init__(self):
self.nil = RBNode(0)
self.root = self.nil
#左旋转
def LeftRotate( T, x):
'''
* 左旋示意图:对节点x进行左旋,左旋选择x的右结点
* parent parent
* / /
* x y
* / \ / \
* lx y ----------> x ry
* / \ / \
* ly ry lx ly
'''
y = x.right
x.right = y.left
if y.left != T.nil:
y.left.parent = x
y.parent = x.parent
if x.parent == T.nil:
T.root = y
elif x == x.parent.left:
x.parent.left = y
else:
x.parent.right = y
y.left = x
x.parent = y
#右旋转
def RightRotate( T, x):
'''
* 右旋示意图:对节点x进行右旋,选择x的左结点
* parent parent
* / /
* x y
* / \ / \
* y rx -----> ly x
* / \ / \
* ly ry ry rx
'''
y = x.left
x.left = y.right
if y.right != T.nil:
y.right.parent = x
y.parent = x.parent
if x.parent == T.nil:
T.root = y
elif x == x.parent.right:
x.parent.right = y
else:
x.parent.left = y
y.right = x
x.parent = y
#红黑树的插入
def RBInsert( T, z):
y = T.nil
x = T.root
while x != T.nil:
y = x
if z.key < x.key:
x = x.left
else:
x = x.right
z.parent = y
if y == T.nil:
T.root = z
elif z.key < y.key:
y.left = z
else:
y.right = z
z.left = T.nil
z.right = T.nil
z.color = 'red'
RBInsertFixup(T, z)
return z.key, '颜色为', z.color
#红黑树的上色
def RBInsertFixup( T, z):
while z.parent.color == 'red':
if z.parent == z.parent.parent.left:
y = z.parent.parent.right
if y.color == 'red':
z.parent.color = 'black'
y.color = 'black'
z.parent.parent.color = 'red'
z = z.parent.parent
else:
if z == z.parent.right:
z = z.parent
LeftRotate(T, z)
z.parent.color = 'black'
z.parent.parent.color = 'red'
RightRotate(T,z.parent.parent)
else:
y = z.parent.parent.left
if y.color == 'red':
z.parent.color = 'black'
y.color = 'black'
z.parent.parent.color = 'red'
z = z.parent.parent
else:
if z == z.parent.left:
z = z.parent
RightRotate(T, z)
z.parent.color = 'black'
z.parent.parent.color = 'red'
LeftRotate(T, z.parent.parent)
T.root.color = 'black'
def RBTransplant( T, u, v):
if u.parent == T.nil:
T.root = v
elif u == u.parent.left:
u.parent.left = v
else:
u.parent.right = v
v.parent = u.parent
def RBDelete(T, z):
y = z
y_original_color = y.color
if z.left == T.nil:
x = z.right
RBTransplant(T, z, z.right)
elif z.right == T.nil:
x = z.left
RBTransplant(T, z, z.left)
else:
y = TreeMinimum(z.right)
y_original_color = y.color
x = y.right
if y.parent == z:
x.parent = y
else:
RBTransplant(T, y, y.right)
y.right = z.right
y.right.parent = y
RBTransplant(T, z, y)
y.left = z.left
y.left.parent = y
y.color = z.color
if y_original_color == 'black':
RBDeleteFixup(T, x)
#红黑树的删除
def RBDeleteFixup( T, x):
while x != T.root and x.color == 'black':
if x == x.parent.left:
w = x.parent.right
if w.color == 'red':
w.color = 'black'
x.parent.color = 'red'
LeftRotate(T, x.parent)
w = x.parent.right
if w.left.color == 'black' and w.right.color == 'black':
w.color = 'red'
x = x.parent
else:
if w.right.color == 'black':
w.left.color = 'black'
w.color = 'red'
RightRotate(T, w)
w = x.parent.right
w.color = x.parent.color
x.parent.color = 'black'
w.right.color = 'black'
LeftRotate(T, x.parent)
x = T.root
else:
w = x.parent.left
if w.color == 'red':
w.color = 'black'
x.parent.color = 'red'
RightRotate(T, x.parent)
w = x.parent.left
if w.right.color == 'black' and w.left.color == 'black':
w.color = 'red'
x = x.parent
else:
if w.left.color == 'black':
w.right.color = 'black'
w.color = 'red'
LeftRotate(T, w)
w = x.parent.left
w.color = x.parent.color
x.parent.color = 'black'
w.left.color = 'black'
RightRotate(T, x.parent)
x = T.root
x.color = 'black'
def TreeMinimum( x):
while x.left != T.nil:
x = x.left
return x
#中序遍历
def Midsort(x):
if x!= None:
Midsort(x.left)
if x.key!=0:
print('key:', x.key,'x.parent',x.parent.key)
Midsort(x.right)
代码参考博客:https://blog.csdn.net/z649431508/article/details/78034751
红黑树推荐博客文章:https://www.cnblogs.com/yangecnu/p/Introduce-Red-Black-Tree.html
AVL树LL、LR、RR、RL类型判断和调整:http://www.cnblogs.com/qingergege/p/7294892.html
这里的不同的博客有很多不同的说法,有的说B树是二叉搜索树,有的说是多路搜索树,有的说是平衡二叉搜索树,这里我更倾向于是平衡二叉搜索树。下面是按照多路搜索树的定义
B树:是一种多路搜索树(并不是二叉的)
B树与红黑树最大的不同在于,B树的结点可以有许多子女,从几个到几千个。那为什么又说B树与红黑树很相似呢?因为与红黑树一样,一棵含n个结点的B树的高度也为O(lgn),但可能比一棵红黑树的高度小许多,应为它的分支因子比较大。所以,B树可以在O(logn)时间内,实现各种如插入(insert),删除(delete)等动态集合操作。
B-树的特性:
B-树的搜索,从根结点开始,对结点内的关键字(有序)序列进行二分查找,如果命中则结束,否则进入查询关键字所属范围的儿子结点;重复,直到所对应的儿子指针为空,或已经是叶子结点;
B+树是B-树的变体,也是一种多路搜索树, 其定义基本与B-树同,其特点是:
B+的特性:
B*树 是B+树的变体,在B+树的非根和非叶子结点再增加指向兄弟的指针;
参考博客:https://www.cnblogs.com/qlqwjy/p/7965491.html
后面的关于红黑树、B树的代码实现我暂写不出来,只能写一写基本的定义以及说明,敬请谅解,经过这些总结,虽然写不出代码来,但至少理解了一些思想,还是有点收获的。