一、树
树是一种可以递归定义的数据结构。树是由n个节点组成的结合:
如果n=0,是一颗空树;如果n>0,那么一定存在一个节点作为树的根节点,其他节点可以分为m个集合,每个集合本身又是一棵树。
树的实例:模拟文件系统
class Node:
def __init__(self, name, type="dir"):
self.name = name
self.type = type #"dir" or "file"
self.children = []
self.parent = None
def __repr__(self):
return self.name
class FileSystemTree:
def __init__(self):
self.root = Node("/") #根目录
self.now = self.root #当前文件夹
def mkdir(self, name):
#name 以 / 结尾
if name[-1] != "/":
name += "/"
node = Node(name)
self.now.children.append(node)
node.parent = self.now
def ls(self): #展示当前目录下的所有目录
return self.now.children
def cd(self, name): #转换目录
if name[-1] != "/":
name += "/"
if name == "../":
self.now =self.now.parent
return
for child in self.now.children:
if child.name == name:
self.now = child
return
else:
raise ValueError("invaild dir")
tree = FileSystemTree()
tree.mkdir("var/")
tree.mkdir("bin/")
tree.mkdir("user/")
tree.cd("user/")
tree.mkdir("Python")
tree.cd("../")
print(tree.ls())
二、二叉树
二叉树的链式存储:将二叉树的节点定义为一个对象,节点之间通过类似链表方式来连接
#二叉树结点
class BiTreeNode:
def __init__(self,data):
self.data = data
self.lchild = None #左孩子
self.rchild = None #右孩子
1、二叉树的遍历
DLR前序遍历:EACBDGF
LDR中序遍历:ABCDEGF
LRD后序遍历:BDCAFGE
层次遍历:EAGCFBD(用队列写)
给定一个序列的两种遍历方式,就可以确定这棵树
前序遍历
#前序遍历
def pre_order(root):
if root:
print(root.data, end=',')
pre_order(root.lchild)
pre_order(root.rchild)
中序遍历
#中序遍历
def in_order(root):
if root:
in_order(root.lchild)
print(root.data,end=",")
in_order(root.rchild)
后序遍历
#后序遍历
def post_order(root):
if root:
post_order(root.lchild)
post_order(root.rchild)
print(root.data,end=",")
层次遍历
#层次遍历
from collections import deque
def level_order(root):
queue = deque()
queue.append(root)
while len(queue) > 0: #只要队不空
node = queue.popleft()
print(node.data,end=",")
if node.lchild:
queue.append(node.lchild)
if node.rchild:
queue.append(node.rchild)
三、二叉搜索树
二叉搜索树是一棵二叉树且满足性质:设x是二叉树的一个节点,如果y是x左子树的一个节点,那么y.key <= x.key;如果y是x右子树的一个节点,那么y.key >= x.key
二叉搜索树的操作:查询、插入、删除
1、二叉树的插入
class BiTreeNode:
def __init__(self,data):
self.data = data
self.lchild = None
self.rchild = None
self.parent = None
class BST:
def __init__(self,li=None):
self.root = None
if li:
for val in li:
self.insert_no_rec(val)
#递归写法
def insert(self,node,val):
if not node: #如果node是空
node = BiTreeNode(val)
elif val < node.data:
node.lchild = self.insert(node.lchild,val)
node.lchild.parent = node
elif val > node.data:
node.rchild = self.insert(node.rchild,val)
node.rchild.parent = node
return node
#非递归写法
def insert_no_rec(self, val):
p = self.root
if not p: #如果p是空 整个树为空树
self.root = BiTreeNode(val)
return
while True:
if val < p.data:
if p.lchild: #如果左子树存在
p = p.lchild
else: #如果左子树不存在 直接插入
p.lchild = BiTreeNode(val)
p.lchild.parent = p
return
elif val > p.data:
if p.rchild:
p = p.rchild
else:
p.rchild = BiTreeNode(val)
p.rchild.parent = p
return
else:
return #如果等于直接返回
二叉搜索树的中序遍历一定是按升序排好序的。
2、二叉树的查询
# 递归写法
def query(self,node,val):
if not node: #为空 找不到
return None
if node.data < val:
return self.query(node.rchild,val)
elif node.data > val:
return self.query(node.lchild,val)
else: #等于val 找到
return node
# 非递归写法
def query_no_rec(self,val):
p = self.root
while p:
if p.data < val:
p = p.rchild
elif p.data > val:
p = p.lchild
else:
return p
return None
3、二叉树的删除
(1)如果要删除的节点是叶子节点:直接删除
(2)如果要删除的节点只有一个孩子:将此节点的父亲与孩子连接,然后删除该节点
(3)如果要删除的节点有两个孩子:将其右子树的最小节点(往左走到头)(该节点最多有一个右孩子)删除,并替换当前节点。/ 或寻找其左子树的最大节点(往右走到头)。
# 情况1 node是叶子节点 直接删掉
def __remove_node_1(self,node):
if not node.parent: #如果父节点为空 只有根节点的父节点为空,即如果是根节点
self.root = None
if node == node.parent.lchild: #如果node是父亲的左孩子
node.parent.lchild = None
node.parent = None
else: #node是父亲的右孩子
node.parent.rchild = None
node.parent = None
# 情况2.1 node只有一个左孩子
def __remove_node_21(self,node):
if not node.parent: #如果是根节点
self.root = node.lchild
node.lchild.parent = None
elif node == node.parent.lchild: #如果node是父亲的左孩子
node.parent.lchild = node.lchild
node.lchild.parent = node.parent
else: #如果node是父亲的右孩子
node.parent.rchild = node.lchild
node.lchild.parent = node.parent
# 情况2.2 node只有一个右孩子
def __remove_node_22(self, node):
if not node.parent:
self.root = node.rchild
node.rchild.parent = None
elif node == node.parent.lchild:
node.parent.lchild = node.rchild
node.rchild.parent = node.parent
else:
node.parent.rchild = node.rchild
node.rchild.parent = node.parent
#删除节点
def delete(self,val):
if self.root: #不是空树 再删除
node = self.query_no_rec(val)
if not node: #如果这个节点不存在
return False
if not node.lchild and not node.rchild: #如果没有孩子
self.__remove_node_1(node)
elif not node.rchild: #如果没有右孩子 有左孩子
self.__remove_node_21(node)
elif not node.lchild: #如果没有左孩子 有右孩子
self.__remove_node_22(node)
else: #左右孩子都有
#寻找右子树最小节点 右子树一直往左走
min_node = node.rchild
while min_node.lchild: #找到右子树最左的节点
min_node = min_node.lchild
node.data = min_node.data #把找到的节点数据替换给要删除的node节点
#再删除被替换的min_node节点
if min_node.rchild: #如果只有有右孩子
self.__remove_node_22(min_node)
else: #如果是叶子节点
self.__remove_node_1(min_node)
四、二叉搜索树的效率
平均情况下,二叉搜索树进行搜索的时间复杂度为O(lgn)
最坏情况下,二叉搜索树可能非常偏斜,如图,变成了一个列表
解决方案:随机化插入 / AVL树