目录
树的简单介绍
树不是线性的,是层级结构。
- 树有零个节点(空树),或者一个节点(根节点),或者多个节点;
- 树中根节点没有父节点,其他节点都有一个父节点;
- 树的叶子节点没有子节点,其他节点都有一个或多个子节点;
- 树的左右子树的后代互不相交。
二叉树
二叉树与普通树的区别:
二叉树的每个节点最多有2个子节点,分别称为左子节点、右子节点。
二叉树:
1、没有节点(空树)
2、包含根节点、左子树、右子树。每个子树都是一个二叉树。
二叉搜索树:BST(Binary Search Tree)
- 有序。
- 每个节点,若有左子树,左子树都比它小,若有右子树,右子树都比它大。
- 每个子树都是一个二叉搜索树。
- 没有值相同的节点。
- 可以是空树。
平衡二叉树:AVL树
- 本质上是二叉搜索树。
- 包含根节点、左子树、右子树。每个子树都是一个平衡二叉树。
- 平衡条件:每个节点的左右子树的高度之差的绝对值(平衡因子)最多为1。
完美的平衡二叉树:
叶子节点在最后两层。除了叶子节点,其他节点都有2个子节点。
(完美的平衡二叉树,除了最后一层,其余层级是满二叉树)
满二叉树
- 平衡二叉树的特殊情况。
- 叶子节点只在最后一层。
- 除了叶子节点,其他节点都有2个子节点。
- 若根节点所在层级设为第1层,则第i层共有
个节点。
- 若根节点所在层级设为第1层,则高度为k的树共有
个节点,共有
个叶子节点。
- 若根节点所在层级设为第1层,则共有n个节点的树的高度是
。
完全二叉树
- 平衡二叉树的特殊情况。
- 叶子节点在最后两层。最后一层的节点从左到右连续。
- 每个节点的左右子树的高度之差的绝对值最多为1。
(完全二叉树,最后一层的节点从左到右连续,其余层级是满二叉树)
完全二叉树和完美的平衡二叉树的区别:
完全二叉树最后一层必须从左到右连续。
二叉树的遍历
前序遍历
根节点 -->左子树 -->右子树 。
中序遍历
左子树 -->根节点 -->右子树 。
后序遍历
左子树 -->右子树 -->根节点。
广度遍历/层序遍历
从根节点开始,从上到下,从左到右访问节点。
二叉搜索树的实现(链表)
创建二叉搜索树的节点的类
class TreeNode:
""" 二叉搜索树的节点 """
def __init__(self,value):
self.value = value
self.left_child = None
self.right_child = None
创建二叉搜索树的类及方法清单
class BSTree:
""" 二叉搜索树 """
def __init__(self):
self.root = None
self._size = 0
def is_empty(self):
""" 判断是否是空树,返回布尔类型 """
return self.root == None
def size(self):
""" 统计树有多少节点 """
return self._size
def clear(self):
""" 清空树 """
self.root = None
def add_node(self,item):
""" 添加节点 """
pass
def find_node(self,item):
""" 查找节点,返回布尔类型 """
return
def remove_node(self,item):
""" 删除节点 """
pass
def trim(self,low,high):
""" 修剪节点,low<=元素值<=high """
pass
def pre_order(self):
""" 前序遍历,返回迭代器 """
pass
def mid_order(self):
""" 中序遍历,返回迭代器 """
pass
def post_order(self):
""" 后序遍历,返回迭代器 """
pass
def breadth_order(self):
""" 广度遍历/层级遍历,返回迭代器 """
pass
if __name__ == "__main__":
# 实例化和方法测试
tree = BSTree()
pass
添加节点
1、若为空树,则新元素为根节点。
2、不是空树,从根节点开始,若新元素比当前节点小,查看左子树的节点。若左子节点为空,则新元素为左子节点。
3、不是空树,从根节点开始,若新元素比当前节点大,查看右子树的节点。若右子节点为空,则新元素为右子节点。
注:新插入的节点总是叶子节点。
def add_node(self,item):
""" 添加节点 """
def recurse_node(node):
if item < node.value:
if not node.left_child: node.left_child = TreeNode(item)
else: recurse_node(node.left_child)
elif not node.right_child: node.right_child = TreeNode(item)
else: recurse_node(node.right_child)
if self.is_empty(): self.root = TreeNode(item)
else: recurse_node(self.root)
self._size += 1
查找节点
思路:从根节点开始,比对节点的值。若相等(即找到),返回True,若小于节点,比对左子树;若大于节点,比对右子树,没有找到返回False。
def find_node(self,item):
""" 查找节点,返回布尔类型 """
def recurse(node):
if not node: return False
elif item == node.value: return True
elif item > node.value: return recurse(node.right_child)
else: return recurse(node.left_child)
return recurse(self.root)
删除节点
1、若空树或者没有找到删除节点,返回None;
2、树只有一个节点,且就是删除的节点,则根节点直接为None;
3、删除的节点是叶子节点,则直接删除该节点;
4、删除的节点只有左/右子节点,则左/右子节点直接替代删除的节点;
5、删除的节点既有左子节点又有右子节点,两种方法:
第一种,若删除节点del是父节点parent的左子节点,parent的左子节点为del的左子节点left,del的直接前驱max_left的右子节点为del的右子节点right。若删除节点del是父节点parent的右子节点,parent的右子节点为del的左子节点left,del直接前驱max_left的右子节点为del的右子节点right。
涉及节点:删除的节点del,删除节点的父节点parent,删除节点的左子节点left,删除节点的右子节点right,删除节点的直接前驱(左子树中最大值节点)max_left。
第二种,找到删除节点del的直接前驱max_left,del由max_left直接替代,再将原max_left删除(原max_left的父节点P的右子节点为原max_left的左子节点L)。或者 找到直接后继min_right,del由min_right直接替代,再将原min_right删除(原min_right的父节点P的左子节点为原min_right的右子节点R)。
涉及节点:删除的节点del,删除节点的直接前驱(左子树中最大值节点)max_left,直接前驱的父节点P,直接前驱的左子节点L。或者 删除的节点del,删除节点的直接后继(右子树中最小值节点)min_right,直接后继的父节点P,直接后继的右子节点R。
某节点的直接前驱 | 某节点的直接后继 | |
---|---|---|
节点 | 某节点的左子树中,最大值的节点 | 某节点的右子树中,最小值的节点 |
节点位置 | 某节点的左子节点的右子节点的右子节点...最右子节点 | 某节点的右子节点的左子节点的左子节点...最左子节点 |
删除时,其子节点的处理 | 删除原直接前驱时,直接前驱的左子节点则为直接前驱的父节点的右子节点 | 删除原直接后继时,直接后继的右子节点则为直接后继的父节点的左子节点 |
def remove_node(self,item):
""" 删除节点 """
def deleteNode(node):
""" 递归寻找删除节点。返回子树 """
# 没有删除的节点,返回None
if not node: return
# 找到删除的节点,执行删除函数delete()
if node.value == item:
node = delete(node)
# 当前节点小于删除的节点,递归当前节点的右子节点
elif node.value < item: node.right_child = deleteNode(node.right_child)
# 当前节点大于删除的节点,递归当前节点的左子节点
else: node.left_child = deleteNode(node.left_child)
return node
def delete(node):
""" 删除节点。返回删除后的子树。
没有子节点,直接删除;有一个子节点时,子节点直接替代删除节点;
有两个节点时,直接前驱直接替代删除节点,并删除原直接前驱 """
# 没有左子节点也没有右子节点,删除的节点返回None
if not node.left_child and not node.right_child: return
# 有左子节点,删除的节点由其左子节点替代
if node.left_child and not node.right_child: return node.left_child
# 有右子节点,删除的节点由其右子节点替代
if node.right_child and not node.left_child: return node.right_child
# 既有左子节点又有右子节点,找到删除节点的直接前驱,原删除节点由直接前驱替代,原直接前驱的子节点直接为其父节点的子节点
parent = node
curr = node.left_child
while curr.right_child:
parent = curr
curr = curr.right_child
node.value = curr.value
if parent != node: parent.right_child = curr.left_child
else: node.left_child = curr.left_child
return node
# 根节点接收删除后的树
self.root = deleteNode(self.root)
修剪节点
给定范围(最小值low和最大值high),去除范围外的节点,只保留 low<=节点<=high。
1、空树,返回None;
2、小于范围中最小值,左子树剪枝,只递归返回右子树的修剪结果;
3、大于范围中最大值,右子树剪枝,只递归返回左子树的修剪结果;
4、在范围内,递归返回左子树和右子树的修剪结果。
def trim(self,low,high):
""" 修剪元素,low<=元素值<=high """
def recurse(node):
if not node: return
if node.value < low: return recurse(node.right_child)
if node.value > high: return recurse(node.left_child)
node.left_child = recurse(node.left_child)
node.right_child = recurse(node.right_child)
return node
return recurse(self.root)
前序遍历(根左右)
思路:一个栈astore(起始元素根节点)。
从栈顶依次取出一个元素,其值加入迭代器,并将其右子节点、左子节点依次(先右后左)加入栈。直至栈为空。
def pre_order(self):
""" 前序遍历,返回迭代器 """
if self.is_empty(): return
astore = [self.root]
while astore:
curr = astore.pop()
yield curr.value
if curr.right_child: astore.append(curr.right_child)
if curr.left_child: astore.append(curr.left_child)
使用递归
def pre_order(self):
""" 前序遍历,返回迭代器 """
alist = []
def recurse(node):
if not self.is_empty():
alist.append(node.value)
if node.left_child: recurse(node.left_child)
if node.right_child: recurse(node.right_child)
recurse(self.root)
return iter(alist)
中序遍历(左根右)
思路:一个栈astore,一个指针curr(起始指向根节点)。
将树左侧的所有左子节点全部加入栈。从栈顶依次取出一个元素,将其值加入迭代器,若有右子节点(以及右子节点的左子节点)则加入栈。直至栈为空。
def mid_order(self):
""" 中序遍历,返回迭代器 """
if self.is_empty():
return
astore = []
curr = self.root
while curr or astore:
if curr:
astore.append(curr)
curr = curr.left_child
else:
curr = astore.pop()
yield curr.value
curr = curr.right_child
使用递归
def mid_order(self):
""" 中序遍历,返回迭代器 """
alist = []
def recurse(node):
if not self.is_empty():
if node.left_child: recurse(node.left_child)
alist.append(node.value)
if node.right_child: recurse(node.right_child)
recurse(self.root)
return iter(alist)
后序遍历(左右根)
思路:两个栈astore(起始元素根节点)、bstore。
从astore依次取出一个元素,将元素加入bstore,将其左右子节点依次加入astore。直至astore为空。
从bstore依次取出一个元素,将其值加入迭代器。
def post_order(self):
""" 后序遍历,返回迭代器 """
if self.is_empty(): return
astore = [self.root]
bstore = []
while astore:
curr = astore.pop()
bstore.append(curr)
if curr.left_child: astore.append(curr.left_child)
if curr.right_child: astore.append(curr.right_child)
while bstore:
curr = bstore.pop()
yield curr.value
使用递归
def post_order(self):
""" 后序遍历,返回迭代器 """
alist = []
def recurse(node):
if not self.is_empty():
if node.left_child: recurse(node.left_child)
if node.right_child: recurse(node.right_child)
alist.append(node.value)
recurse(self.root)
return iter(alist)
广度遍历/层级遍历(从根节点开始,自上而下,从左到右)
思路:一个队列astore(起始元素是根节点)。
从队头取出一个元素,其值加入迭代器,并将其左右子节点依次从队尾加入队列。依次循环直至队列为空。
def breadth_order(self):
""" 广度遍历/层级遍历,返回迭代器 """
if self.is_empty(): return
astore = [self.root]
while astore:
curr = astore.pop(0)
yield curr.value
if curr.left_child: astore.append(curr.left_child)
if curr.right_child: astore.append(curr.right_child)
二叉搜索树完整代码:(链表实现)
class TreeNode:
""" 二叉搜索树的节点 """
def __init__(self,value):
self.value = value
self.left_child = None
self.right_child = None
class BSTree:
""" 二叉搜索树 """
def __init__(self):
self.root = None
self._size = 0
def is_empty(self):
""" 判断是否是空树,返回布尔类型 """
return self.root == None
def size(self):
""" 统计树有多少节点 """
return self._size
def clear(self):
""" 清空树 """
self.root = None
def add_node(self,item):
""" 添加节点 """
def recurse_node(node):
if item < node.value:
if not node.left_child: node.left_child = TreeNode(item)
else: recurse_node(node.left_child)
elif not node.right_child: node.right_child = TreeNode(item)
else: recurse_node(node.right_child)
if self.is_empty(): self.root = TreeNode(item)
else: recurse_node(self.root)
self._size += 1
def find_node(self,item):
""" 查找节点,返回布尔类型 """
def recurse(node):
if not node: return False
elif item == node.value: return True
elif item > node.value: return recurse(node.right_child)
else: return recurse(node.left_child)
return recurse(self.root)
def remove_node(self,item):
""" 删除节点 """
def deleteNode(node):
""" 递归寻找删除节点。返回子树 """
# 没有删除的节点,返回None
if not node: return
# 找到删除的节点,执行删除函数delete()
if node.value == item:
node = delete(node)
# 当前节点小于删除的节点,递归当前节点的右子节点
elif node.value < item: node.right_child = deleteNode(node.right_child)
# 当前节点大于删除的节点,递归当前节点的左子节点
else: node.left_child = deleteNode(node.left_child)
return node
def delete(node):
""" 删除节点。返回删除后的子树。
没有子节点,直接删除;有一个子节点时,子节点直接替代删除节点;
有两个节点时,直接前驱直接替代删除节点,并删除原直接前驱 """
# 没有左子节点也没有右子节点,删除的节点返回None
if not node.left_child and not node.right_child: return
# 有左子节点,删除的节点由其左子节点替代
if node.left_child and not node.right_child: return node.left_child
# 有右子节点,删除的节点由其右子节点替代
if node.right_child and not node.left_child: return node.right_child
# 既有左子节点又有右子节点,找到删除节点的直接前驱,原删除节点由直接前驱替代,原直接前驱的子节点直接为其父节点的子节点
parent = node
curr = node.left_child
while curr.right_child:
parent = curr
curr = curr.right_child
node.value = curr.value
if parent != node: parent.right_child = curr.left_child
else: node.left_child = curr.left_child
return node
# 根节点接收删除后的树
self.root = deleteNode(self.root)
def trim(self,low,high):
""" 修剪元素,low<=元素值<=high """
def recurse(node):
if not node: return
if node.value < low: return recurse(node.right_child)
if node.value > high: return recurse(node.left_child)
node.left_child = recurse(node.left_child)
node.right_child = recurse(node.right_child)
return node
return recurse(self.root)
def pre_order(self):
""" 前序遍历,返回迭代器 """
alist = []
def recurse(node):
if not self.is_empty():
alist.append(node.value)
if node.left_child: recurse(node.left_child)
if node.right_child: recurse(node.right_child)
recurse(self.root)
return iter(alist)
def mid_order(self):
""" 中序遍历,返回迭代器 """
alist = []
def recurse(node):
if not self.is_empty():
if node.left_child: recurse(node.left_child)
alist.append(node.value)
if node.right_child: recurse(node.right_child)
recurse(self.root)
return iter(alist)
def post_order(self):
""" 后序遍历,返回迭代器 """
alist = []
def recurse(node):
if not self.is_empty():
if node.left_child: recurse(node.left_child)
if node.right_child: recurse(node.right_child)
alist.append(node.value)
recurse(self.root)
return iter(alist)
def breadth_order(self):
""" 广度遍历/层级遍历,返回迭代器 """
if self.is_empty(): return
astore = [self.root]
while astore:
curr = astore.pop(0)
yield curr.value
if curr.left_child: astore.append(curr.left_child)
if curr.right_child: astore.append(curr.right_child)
if __name__ == "__main__":
tree = BSTree()
print("空树:",tree.is_empty())
print("节点数:",tree.size())
tree.add_node("H")
tree.add_node("D")
tree.add_node("F")
tree.add_node("A")
tree.add_node("E")
tree.add_node("Q")
tree.add_node("M")
tree.add_node("O")
print("空树:",tree.is_empty())
print("节点数:",tree.size())
for x in tree.pre_order():
print(x,end=" ")
print("前序遍历")
for y in tree.mid_order():
print(y,end=" ")
print("中序遍历")
for z in tree.post_order():
print(z,end=" ")
print("后序遍历")
for k in tree.breadth_order():
print(k,end=" ")
print("广度遍历/层级遍历")
# tree.trim("F","O")
print("查找节点",tree.find_node("H"))
tree.remove_node("H")
for x in tree.pre_order():
print(x,end=" ")
print("前序遍历(修剪或删除节点后)")
for y in tree.mid_order():
print(y,end=" ")
print("中序遍历(修剪或删除节点后)")
for z in tree.post_order():
print(z,end=" ")
print("后序遍历(修剪或删除节点后)")
for k in tree.breadth_order():
print(k,end=" ")
print("广度遍历/层级遍历(修剪或删除节点后)")
tree.clear()
print("空树(清空树后):",tree.is_empty())