一、树
非线性结构
根在顶层 root
叶在下面
除了上面的一个根节点都叫子节点(统称为节点)
根节点是唯一一个没有入边的节点
除了根节点外每个节点都有且只有一条与其他节点相连的入边(指向该节点的边),每个节点可能有许多跳出边(从该节点指向其他节点的边)
路径:有序排列,路径是唯一的
子节点集:父节点的所有“孩子”
父节点:
兄弟节点:同一个节点的所有子节点互为兄弟节点
子树:一个父节点的某个子节点的所有边和后代节点所构成的集合叫做这个父节点的子树。
叶节点:没有子节点的节点
层数:一个节点的层数是指从根节点到此节点的路径中的边的数目
高度:层数的最大值
树的特征:
要么为空树,要么必须有根节点
父节点的唯一性(除了根节点)
可以沿着唯一的路径从根节点到每一个节点
如果这个树的每个节点都之多有两个子节点,称为二叉树
二、二叉树
定义:
是由n(n>=0)个结点组成的有限集合、每个节点最多有两个子树的有序树。它或者是空集,或者是由一个根和称为左、右子树的两个不相交的二叉树组成。
度:节点的出边数(0,1,2)
树中所有节点的度的最大值称为树的度
二叉树的度是2
二叉树的特点:
二叉树是有序树,即使只有一个子树,也必须区分左右子树
二叉树的每个节点的度只能是0,1,2
二叉树中所有节点的5中形态:空节点,无左右子树的节点,只有左子树的节点,只有右子树的节点,具有左右子树的节点
二叉树的性质:
二叉树的第i层至多有2i-1个结点
对任何一颗二叉树T,如果叶子结点数为N0,度为2的节点数为N2,则N0=N2+1
叶子数 = 度为2的边数 + 1
边数 = 节点数 - 1
满二叉树:每一层节点数都满
完全二叉树:除了最后一层外其余层都是满的,并且最后一层或者是满的,或者是右边缺少连续若干个结点
满二叉树是完全二叉树的特例
深度为h的满二叉树的总节点数为2h-1
二叉树的遍历
定义:按照一定的次序访问树中所有的节点,并且每个节点的值仅被访问一次的过程。
可能的三种遍历次序:
先序遍历:vLR 先遍历根节点,再左子树,再右子树 (每个子树都是如此)
中序遍历:LvR
后序遍历:LRv
v表示根节点
先序遍历:a bde cf
中序遍历:dbe a fc
后序遍历:deb fc a
三、二叉树的实现
二叉树的实现
用嵌套列表表示二叉树(三个元素)
第一个元素表示根节点,第二个元素表示左子树,第三个元素表示右子树(递归思想,这其中每个字列表都必须有三个元素,直到该字列表出现空元素[ ]为止)
使用嵌套列表实现二叉树
def BinaryTree(r): #创建树及根节点
return [r, [], []]
def insertLeft(root, newBranch): #插入左子树
t = root.pop(1) #把树的本身的左子树取出
if len(t) >= 1: #左子节点的列表不为空
root.insert(1, [newBranch, t, []])
else:
root.insert(1, [newBranch, [], []])
return root
def insertRight(root, newBranch):
t = root.pop(2)
if len(t) >= 1:
root.insert(2, [newBranch, [], t])
else:
root.insert(2, [newBranch, [], []])
return root
def getRootVal(root): #查询根节点的值
return root[0]
def setRootVal(root, newVal): #设置根节点的值
root[0] = newVal
def getLeftChild(root): #查询左子树的值
return root[1]
def getRightChild(root): #查询右子树的值
return root[2]
tree = BinaryTree('a')
insertLeft(tree, 'b')
insertRight(tree, 'c')
insertLeft(getLeftChild(tree), 'd')
insertRight(getLeftChild(tree), 'e')
insertLeft(getRightChild(tree), 'd')
print(tree)
二叉树遍历的实现
深度遍历二叉树:
递归思想
def preorder(root): #先序遍历
if root!=[]:
print(root[0], end='')
preorder(root[1])
preorder(root[2])
def inorder(root): #中序遍历
if root!=[]:
inorder(root[1])
print(root[0], end='')
inorder(root[2])
def postorder(root): #后序遍历
if root!=[]:
postorder(root[1])
postorder(root[2])
print(root[0], end='')
tree = ['a',
['b',
['d', [], []],
['e', [], []]],
['c',
['f', [], []],
[]]
]
广度遍历二叉树:
先第一层(根节点),再第二层,直到最后一层(叶节点)
def levelOrder(root):
res = [] #排序列表
#如果根节点为空,则返回空列表
if root is None:
return res
#存储当前位置的父节点
q = []
#首先根节点入队
q.append(root)
#当节点列表为空时,循环结束
while len(q) != 0: #同层下有0,1,2个节点
for i in range(len(q)):
r = q.pop(0) #当前节点出队
if r.left is not None:
q.append(r.left) #左子树入队
if r.right is not None:
q.append(r.right) #右子树入队
res.append(r.value) #第一个结点排序,q列表中剩余下面的左右子树,继续循环
return res
二叉树节点的个数
算法思路:递归思想;当树为空时,节点个数为0;否则,节点总数 = 根节点个数 + 左子树节点个数 + 右子树节点个数
def count(root):
if root == []:
return 0
else:
n1 = count(root[1])
n2 = count(root[2])
n = 1+ n1 + n2
return n
二叉排序树
定义:
- 要么是一课空树
- 如果不为空,那么其左子树节点的值都小于根节点的值;右子树节点的值都大于根节点的值
- 左右子树也是二叉排序树
- 树中每个节点的值都大于它的左子树中所有节点的值,而小于它的右子树中所有节点的值。
- 中序遍历得到的节点序列为:12, 15, 23, 30, 52, 74(有序的)
高效的数据操作:查找,插入,删除
1. 查找算法
思想:
查找给定值item的元素时,若二叉排序树为空,则查找失败,返回False
若item 等于 当前树根节点的值,查找成功,返回True
若item 小于 根节点的值,则继续在根的左子树中查找
若item 大于 根节点的值,则继续在根的右子树中查找
def searchBintree(tree, num): #查找
if tree == []:
return False
elif num == tree[0]:
return True
elif num < tree[0]:
return searchBintree(tree[1], num)
else:
return searchBintree(tree[2], num)
mytree = [30,
[15,
[12, [], []],
[23, [], []],
],
[52,
[],
[74, [], []],
]
]
num = int(input('请输入要查找的数:'))
searchBintree(mytree, num)
2. 插入算法
思想:
若二叉排序树为 空 ,则由item生成的新节点作为 根节点 插入;
若item 小于 根节点的值,则将新节点插入到根的 左子树 上;
若item 大于等于 当前树根节点的值,则将新节点插入到根的 右子树 上。
def insert1(tree, num):
if tree == []:
tree.extend([num, [], []])
elif num < tree[0]:
insert1(tree[1], num)
else:
insert1(tree[2], num)
return tree
mytree = [30,
[15,
[12, [], []],
[23, [], []],
],
[52,
[],
[74, [], []],
]
]
num = int(input('请输入要插入的数:'))
insert1(mytree, num)
3. 删除算法
因为删除之后要保证新树还是一棵二叉排序树,要保持原本的结构,分情况讨论。
删除的三种情况:
- 叶子节点:只需把该节点从列表移除
- 单分支节点:将非空子节点上移
- 双分支节点:用左子树的最大值节点替换该节点
def getmax(tree): #计算树的最大值,并将其删除
if tree[2] == []: #若右子树为空
x = tree[0] #最大值为根节点值,二叉排序树的性质
if tree[1] != []: #若左子树不为空,左子树上移
tree[:] = tree[1]
else:
tree.clear() #左右子树都为空,清空列表
return x
else:
return getmax(tree[2]) #继续在右子树中查找最大值,二叉排序树的性质:右侧大
def delete(tree, num): #num要删除的数
if tree == []:
return False
if num < tree[0]: #小于根节点
return delete(tree[1], num) #左子树中找
elif num > tree[0]: #右子树中找
return delete(tree[2], num)
else: #找到待删除节点
if tree[1] == [] and tree[2] == []: #左右子树都为空:叶子节点
tree.clear() #直接清空该子树
elif tree[1] == []: #左子树为空:单分支节点
tree[:] = tree[2] #右子树上移
elif tree[2] == []: #右子树为空:单分支节点
tree[:] = tree[1] #左子树上移
else: #左右子树都不为空:双分支节点
max = getmax(tree[1]) #找左子树的最大值并删除
tree[0] = max #用左子树的最大值作为新的根节点
return True
mytree = [30,
[15,
[12, [5, [], []], [13, [], []]],
[23, [], []],
],
[52,
[],
[74, [], []],
]
]
delete(mytree, 15)
print(mytree)