文章目录
- 前言
- 65.树的概念
- 66.树的实例:模拟文件系统
- 67.二叉树的概念
- 68.二叉树的遍历
- 69.二叉搜索树的概念。
- 70.二叉搜索树:插入
- 71.二叉搜索树:查询
- 72.二叉搜索树:删除
- 73.二叉搜索树:删除实现
- 总结
前言
学习python数据结构与算法,学习常用的算法,
b站学习链接
65.树的概念
之前讲推排序时学过这个树的概念
【零基础】学python数据结构与算法笔记4
这个树,我单拎出来一个节点就是一个树,B是一个子树,EIJPQ也是一个子树。
66.树的实例:模拟文件系统
模拟linux系统里的文件系统
Linux 文件与目录管理
命令mkdir
ls
cd
先建立树的一个节点
然后用树实现文件系统,一个文件夹下有几个文件夹。
进入usr文件夹下,下有python一个子文件夹
…/ 返回上一级根目录,[var/, bin/, usr/]
67.二叉树的概念
二叉树链式存储:将二叉树的节点定义为一个对象,节点之间通过类似链表的链接方式来连接。
之前堆排序里讲的是完全二叉树,只有右边少数,这里讲的不是完全二叉树。
比方说这样一颗树
只有左孩子和右孩子,如下定义,找根节点的左孩子的右孩子的数据,为C
68.二叉树的遍历
二叉树遍历方式有4种
前序遍历:EACBDGF
中序遍历:ABCDEGF
后序遍历:BDCAFGE
层次遍历:EAGCFBD
前序遍历
是写打印根节点,再打印左孩子,再打印右孩子吗,用递归写出
先是根节点E,再是左节点A,A没有左节点然后右节点C,然后左节点B,右节点D,左树遍历完了然后右树,先是G,没有左节点,最后右节点F。
中序遍历
先是左孩子,再是根节点,再是右孩子,根节点在中间,和前序遍历只是顺序不同
先是左孩子A然后看CBD这个树的左孩子是B,然后根节点C,然后右节点D,再是整个树的根节点E,最后右节点G,再是F
后序遍历
先是左孩子,然后右孩子,最后根节点。
先看左孩子这一侧ACBD子树,再看BCD子树,先是左孩子B,再是右孩子D,最后根节点C,然后ACBD的根节点A,看GF树,左孩子没有,右孩子F,根节点G,最后大树的根节点E。
这三种面试的时候经常会考,还会考一种,知道前序排序和中序排序画树和后序排序,还有知道中序排序和后序排序画树和前序排序。
层次排序
按照一层一层打印,不是二叉树也可以按照此方法
思路如下,使用队列,先进队根节点,然后出根节点,如果有左孩子或者右孩子就进队,然后再出此时的根节点,进左右孩子,直到队出完,就遍历完了。就和之前学的广度优先搜索差不多。【零基础】学python数据结构与算法笔记9中的队列实现迷宫问题
69.二叉搜索树的概念。
二叉搜索树是一颗二叉树且满足性质:设x是二叉树的一个节点。如果y是x左子树的一个节点,那么y.key<=x.key;如果y是x右子树的一个节点,那么y.key>=x.key。
二叉搜索树的操作:查询、插入、删除
比方说查11,先看17,11比17小,看17的左子树5,11比5大看5的右子树,是11找到。
比方说插入32,先看17,32比17大,看右子树36,比36小看29,比29大所以在29的右子树上。
查询和插入都是折半的操作,所以大概是logn,删除操作比较复杂,待会说。
70.二叉搜索树:插入
根据刚刚所说写出二叉搜索树的插入
#二叉搜索树
class BiTreeNode:
def __init__(self,data):
self.data = data
self.lchild = None #左孩子
self.rchild = None #右孩子
self.parent = None
class BST:#binary search tree
def __init__(self,li= None):
self.root = None
if li:
for val in li:
self.insert_no_rec(val)
#self.root = self.insert(self.root,val)
def insert(self,node,val): #递归写法
if not 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:#如果是空树
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
def pre_order(self,root):
if root:
print(root.data,end=",")
self.pre_order(root.lchild)
self.pre_order(root.rchild)
def in_order(self,root):
if root:
self.in_order(root.lchild)
print(root.data,end=",")
self.in_order(root.rchild)
def post_order(self,root):
if root:
self.post_order(root.lchild)
self.post_order(root.rchild)
print(root.data,end=',')
发现中序遍历是升序的排序,因为正好符合二叉搜索树的定义,左边最小,中间根大,右边最大。
71.二叉搜索树:查询
递归算法比不递归的慢,也比较难理解。
递归形式
非递归形式
查3就查不到
72.二叉搜索树:删除
分三种情况讨论
1.如果要删除的节点是叶子节点:直接删除
2.如果删除的节点只有一个孩子:将此节点的父亲与孩子连接,然后删除该节点
有一个特殊情况,假设现在只有17,35,29,38四个节点,要删除的是这个17这个根,那就需要重新更新根节点。
3.如果要删除的节点有两个孩子:将其右子树的最小节点(该节点最多有一个右孩子)删除,并替换当前节点。(或者说是左子树的最大节点删除并替换)
73.二叉搜索树:删除实现
先分情况写__remove_node_1
__remove_node_21
__remove_node_21
第三种写在delete里,
第二种实现有点像双链表的删除实现,画个图会好理解点。
总的代码如下
#二叉搜索树
class BiTreeNode:
def __init__(self,data):
self.data = data
self.lchild = None #左孩子
self.rchild = None #右孩子
self.parent = None
class BST:#binary search tree
def __init__(self,li= None):
self.root = None
if li:
for val in li:
self.insert_no_rec(val)
#self.root = self.insert(self.root,val)
def insert(self,node,val): #递归写法
if not 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:#如果是空树
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
def query(self,node,val): #递归写法
if not node:
return None
if node.data <val:#val大,找右节点
return self.query(node.rchild,val)
elif node.data >val:
return self.query(node.lchild,val)
else:#如果找到了
return node
def query_no_rec(self,val):
p = self.root
while p:
if p.data <val: #val大
p = p.rchild
elif p.data >val:
p = p.lchild
else: #找到了
return p
return None#找不到 return None
def __remove_node_1(self,node): #加两下划线表示是类的内置函数
#情况1:node是叶子节点
if not node.parent: #如果删除的是根节点
self.root = None
if node ==node.parent.lchild: #node是它父亲的左孩子
node.parent.lchild = None #删除左节点
else: #右孩子
node.parent.rchild = None
def __remove_node_21(self,node):
#情况2.1:node只有一个左孩子
if not node.parent: #如果删除的node是根节点
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
def __remove_node_22(self,node):
#情况2.2:node只有一个右孩子
if not node.parent: #如果删除的node是根节点
self.root = node.rchild #先连到根节点
node.lchild.parent = None #再把自己删了
elif node == node.parent.lchild: #如果删除的node是左孩子
node.parent.lchild = node.rchild #把自己的右孩子连到自己根节点的左孩子上
node.rchild.parent = node.parent #把自己的根节点连到自己右孩子的父节点上
else: #如果删除的node是右孩子
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: #1.叶子节点
self.__remove_node_1(node)
elif not node.rchild: #2.1只有一个左孩子
self.__remove_node_21(node)
elif not node.lchild: #2.2
self.__remove_node_22(node)
else: #3.两个孩子都有 #将其右子树的最小节点(最多有一个右孩子)删除,并替换当前节点
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)
def pre_order(self,root):
if root:
print(root.data,end=",")
self.pre_order(root.lchild)
self.pre_order(root.rchild)
def in_order(self,root):
if root:
self.in_order(root.lchild)
print(root.data,end=",")
self.in_order(root.rchild)
def post_order(self,root):
if root:
self.post_order(root.lchild)
self.post_order(root.rchild)
print(root.data,end=',')
最后成功删除
总结
学习了二叉树和二叉搜索树的基本实现。
文章目录
- 前言
- 65.树的概念
- 66.树的实例:模拟文件系统
- 67.二叉树的概念
- 68.二叉树的遍历
- 69.二叉搜索树的概念。
- 70.二叉搜索树:插入
- 71.二叉搜索树:查询
- 72.二叉搜索树:删除
- 73.二叉搜索树:删除实现
- 总结