二叉搜索树基本操作实现(python)
创建 、遍历(递归、非递归)、查找、删除等(有问题请指出,谢谢哈!)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2021-04-17
# @Author : dwchen
# @Link :
# @Version :
"""二叉搜索树BST(二叉排序树、二叉查找树)binary search tree介绍
二叉搜索树或者是一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于或等于它的根节点的值;
(2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
(3)左、右子树也分别为二叉搜索树;
"""
"""二叉搜索树基本操作
二叉搜索树创建
先序遍历(递归,迭代)
中序遍历(递归,迭代)
后序遍历(递归,迭代)
层次遍历(迭代)
查找
删除
获取树的最大深度
二叉搜索树所有路径
"""
class Stack():
"""列表实现顺序栈
"""
def __init__(self):
self.stack = []
self.num = 0
def push(self, value): # 进栈
self.stack.append(value)
self.num += 1
def pop(self): # 出栈
if self.stack:
data = self.stack.pop()
self.num -= 1
return data
def top(self):
if self.stack:
return self.stack[-1]
def is_empty(self): # 判断栈是否空
return not bool(self.stack)
class Node():
def __init__(self, data):
self.data = data
self.lchild = None
self.rchild = None
# self.traverse_count = 0 # 记录结点被遍历的次数,便于后序非递归遍历post_order1, 其他方法不需要这个变量
class BST():
def __init__(self):
self.root = None
def create_BST(self, data_list):
for data in data_list:
self.root = self.insert(self.root, data)
def insert(self, root, data):
if root == None:
node = Node(data)
root = node
elif data == root.data:
return None
elif data > root.data:
root.rchild = self.insert(root.rchild, data)
else:
root.lchild = self.insert(root.lchild, data)
return root
# def insert(self, data): # 非递归 添加节点
# node = Node(data)
# if self.root == None:
# self.root = node
# else:
# parent = None
# cur = self.root
# while cur != None:
# parent = cur
# if data > cur.data:
# cur = cur.rchild
# elif data < cur.data:
# cur = cur.lchild
# else:
# pass
# if data > parent.data:
# parent.rchild = node
# elif data < parent.data:
# parent.lchild = node
def pre_order_traverse(self, node):
if node is not None:
print(node.data, end=' ')
self.in_order_traverse(node.lchild)
self.in_order_traverse(node.rchild)
def in_order_traverse(self, node):
if node is not None:
self.in_order_traverse(node.lchild)
print(node.data, end=' ')
self.in_order_traverse(node.rchild)
def post_order_traverse(self, node):
if node is not None:
self.in_order_traverse(node.lchild)
self.in_order_traverse(node.rchild)
print(node.data, end=' ')
#-----------------------------------非递归遍历------------------------------#
# 前序、中序和后序遍历:三者的共同点是“左子节点”一定在“右子节点”之前输出,“中间根节点”则根据遍历的类型选择放在最前、中间还是最后。
# a.遇到一个节点,访问它,然后把它压栈,并去遍历它的左子树;
# b.当左子树遍历结束后,从栈顶弹出该节点并将其指向右儿子,继续a步骤;
# c.当所有节点访问完即最后访问的树节点为空且栈空时,停止。
def pre_order(self):
T = self.root
S = Stack()
while T or (not S.is_empty()):
while T:
print(str(T.data), end=' ')
S.push(T)
T = T.lchild
if not S.is_empty():
T = S.pop()
T = T.rchild
print()
# a.中序先将结点进栈,遍历到左下角尽头,
# b.然后再出栈访问,并将其指向其右儿子,继续a步骤
# c.当所有结点访问完即最后访问的树结点为空且栈为空时,停止
def in_order(self):
p = self.root
stack = Stack()
while p or not stack.is_empty():
while p:
stack.push(p)
p = p.lchild
if not stack.is_empty():
p = stack.pop()
print(p.data, end=' ')
p = p.rchild
print()
# 思路一:对于任一结点P,将其入栈,然后沿其左子树一直往下搜索
# 直到搜索到没有左孩子结点,此时该结点出现在栈顶,但是此时不能将其出栈并访问,
# 因为其右孩子还未被访问。 所以,接下来按照相同的规则对其右子树进行
# 相同的处理,当遍历完其右孩子时,该结点第二次出现在栈顶,此时可以将该结点
# 出栈并访问。这样就保证了正确的访问顺序。可以看出,每个结点都第二次出现在
# 栈顶时,才能访问它。因此需要多设置一个变量标识该结点是第几次出现在栈顶
def post_order1(self):
p = self.root
temp = None
stack = Stack()
#stack.push(p)
while p or not stack.is_empty():
while p:
p.traverse_count = 1
stack.push(p)
p = p.lchild
if not stack.is_empty():
temp = stack.pop()
if temp.traverse_count == 1:
temp.traverse_count = 2
stack.push(temp)
p = temp.rchild
elif temp.traverse_count == 2:
print(temp.data, end=' ')
print()
# 思路2:要保证根结点在左右孩子访问之后才能访问,因此,对于任一结点P,先将其入栈。
# 如果P不存在左孩子和右孩子,则可以直接访问它;或者P存在左孩子或右孩子,但是其左孩子
# 和右孩子都已经被访问过,则同样可以直接访问该结点。若非上述两种情况,则将P的右孩子和左孩子
# 依次入栈,这样就保证每次取栈顶元素的时候,左孩子在右孩子前面被访问,左右孩子都在根结点前被访问。
def post_order2(self):
pre = None
cur = self.root
stack = Stack()
stack.push(cur)
while not stack.is_empty():
cur = stack.top()
if(cur.lchild == None and cur.rchild == None) or (pre != None and (pre == cur.lchild or pre == cur.rchild)):
print(cur.data, end=' ')
stack.pop()
pre = cur
else:
if cur.rchild != None:
stack.push(cur.rchild)
if cur.lchild != None:
stack.push(cur.lchild)
print()
# 思路3:前序遍历的访问顺序是根,左,右。而后续遍历的节结点访问顺序是左,右,根。
# 若把前序遍历的顺序改为根,右,左。最后,将结果倒置一下,即为后序遍历的结果。
# https://zhuanlan.zhihu.com/p/82074260
def post_order3(self):
p = self.root
temp_stack = Stack()
result_stack = Stack()
while p or not temp_stack.is_empty():
while p:
result_stack.push(p)
temp_stack.push(p)
p = p.rchild
if not temp_stack.is_empty():
p = temp_stack.pop()
p = p.lchild
while not result_stack.is_empty():
temp = result_stack.pop()
print(temp.data, end=' ')
print()
# 一种不常见的后序遍历方法
# https://blog.youkuaiyun.com/alexmiaomiao/article/details/81813021
def post_order4(self):
stack = Stack()
stack.push(self.root)
while not stack.is_empty():
p = stack.pop()
if type(p) is Node:
stack.push(p.data)
if p.rchild:
stack.push(p.rchild)
if p.lchild:
stack.push(p.lchild)
else:
print(p, end=' ')
print()
def level_oreder(self): #层次遍历
queue = [] # list 模拟队列
queue.append(self.root)
while queue:
node = queue.pop(0)
print(node.data, end=' ')
if node.lchild:
queue.append(node.lchild)
if node.rchild:
queue.append(node.rchild)
#----------------------------------非递归遍历结束-----------------------------------------------#
#递归写法, 用层次遍历记录层数也可以
def max_depth(self, root):
if root is None:
return 0
left_depth = self.max_depth(root.lchild)
right_depth = self.max_depth(root.rchild)
return left_depth+1 if left_depth > right_depth else right_depth+1
def get_all_tree_path(self):
pass
def search(self, value):
cur = self.root
while cur is not None:
if cur.data == value:
break
if value > cur.data:
cur = cur.rchild
else:
cur = cur.lchild
return cur
def search_parent(self, value):
parent = None
cur = self.root
while cur is not None:
if cur.data == value:
break
if value > cur.data:
parent = cur
cur = cur.rchild
else:
parent = cur
cur = cur.lchild
return parent
def search_target_and_parent(self, value):
parent = None
cur = self.root
while cur is not None:
if cur.data == value:
break
if value > cur.data:
parent = cur
cur = cur.rchild
else:
parent = cur
cur = cur.lchild
return cur, parent
def find_min(self, root):
cur = root
while cur.lchild:
cur = cur.lchild
return cur
# 删除指定结点,需要分如下几种情况:
# 删除非根节点
# 1.待删除结点没有孩子(即:叶节点)
# 2.待删除结点只有一个孩子(左孩子或者右孩子)
# 3.待删除结点有两个孩子
# 删除根节点
# 1.待删除结点没有孩子(即:叶节点)
# 2.待删除结点只有一个孩子(左孩子或者右孩子)
# 3.待删除结点有两个孩子
# https://blog.youkuaiyun.com/qq_45783660/article/details/115320477
def remove(self, value):
target, parent = self.search_target_and_parent(value)
if parent: # 删除非 根节点, 根节点的parent为 None
if target.lchild is None and target.rchild is None:
if target == parent.lchild:
parent.lchild = None
if target == parent.rchild:
parent.rchild = None
elif target.lchild is not None and target.rchild is not None:
right_min_node = self.find_min(target.rchild)
temp = right_min_node.data
self.remove(temp)
target.data = temp
else:
if target.lchild is None and target.rchild is not None:
if target == parent.lchild:
parent.lchild = target.rchild
if target == parent.rchild:
parent.rchild = target.rchild
if target.lchild is not None and target.rchild is None:
if target == parent.lchild:
parent.lchild = target.lchild
if target == parent.rchild:
parent.rchild = target.lchild
else: # 删除 根节点
if target.lchild is None and target.rchild is None:
self.root = None
elif target.lchild is not None and target.rchild is not None:
self.root.lchild.rchild = self.root.rchild
self.root = self.root.lchild
else:
if target.lchild is None and target.rchild is not None:
self.root = self.root.rchild
if target.lchild is not None and target.rchild is None:
self.root = self.root.lchild
if __name__ == "__main__":
a = [1, 2, 9, 6, 7]
bst = BST()
bst.create_BST(a)
#bst.in_order_traverse(bst.root)
# bst.pre_order()
# bst.in_order()
# bst.post_order1()
# bst.post_order2()
# bst.post_order3()
# bst.post_order4()
# bst.level_oreder()
# max_depth = bst.max_depth(bst.root)
# print(max_depth)
# bst.search(1)
# bst.search(3)
bst.remove(1)
bst.in_order()