一、基本概念
1.1 树的特征:
我们学过像栈和队列这样的线性数据结构,对递归也有一定的了解,我们说有线性数据结构,那就有非线性数据结构,因此先让我们来看看最简单和常见的非线性数据结构——树(Tree)。树在计算机科学的各个领域中被广泛应用,包括操作系统,图形学,数据库系统和计算机网络。树结构和自然界的树有许多相似的地方,也有根、枝和叶,它们的不同之处在于计算机中的树结构根在顶部而叶子则在底部。
那么树有什么特征呢?
-
第一个特征是:树是分层的,这里分层的意思是树的顶层部分更加宽泛,而底层部分更加精细具体。
下面是一个例子,最上层是“界”,它下面的一层(上层的子层)是“门”,然后是“纲”等等。但是,无论我们细分到多少层,这里面包含的生命体仍是动物。
-
第二个特征是:一个节点(node)的所有子节点(children)和另一个节点的子节点是完全独立的。
比如“猫属”有两个子节点“家生”和“野生”,“蝇属”中也有一个“家生”, 但它和“猫属”中的“家生”是完全不同而且相互独立的。这意味着我们可以在不影响“猫属” 的子节点的情况下更改“蝇属”的子节点。 -
第三个特征是:它的每个叶节点(leaf)都是不同的。
对每一种动物,我们都可以从根节点(root)开始沿着一条特定的路径找到它对应的叶节点,并把它和其他动物区分开, 例如对于家猫,我们可以沿着 动物界 → 脊索动物门 → 哺乳动物纲 → 食肉动物目 → 猫科 → 猫属 → 家猫 找到它。
1.2 树结构的相关术语和定义:
树的定义一:
树的定义二:(递归定义)
二、树的实现
2.1 方法一:嵌套列表法
"""
预备知识:你会发现,我们在插入时,传入的列表并不需要返回,因为列表list和字典dict是可变的类型,
就和c++中的地址传递或者引用是一个道理,也就是说函数里面的改变,会把实参也
改变了
"""
def BinaryTree(r):
"""
作用:创建二叉树
:param r:
:return:
"""
return [r, [], []]
def insertLeft(root, newBranch):
"""
插入左子树
:param root:
:param newBranch:
:return:
"""
# 将左子树取出来
t = root.pop(1)
# 如果左子树不为空,则把新插入的newBranch作为这个树的根节点,原来左子树的根节点变为它的左子树
if len(t) > 1:
root.insert(1, [newBranch, t, []])
# 如果左子树为空,则把新插入的newBranch作为这个树的根节点即可
else:
root.insert(1, [newBranch, [], []])
# return root # 不用返回,因为列表和字典是可变数据类型
def insertRight(root, newBranch):
"""
插入右子树
:param root:
:param newBranch:
:return:
"""
# 将右子树取出来
t = root.pop(2)
# 如果右子树不为空,则新节点作为右子树的根,原来的右子树的根节点变为新节点的右子树
if len(t) > 1:
root.insert(2, [newBranch, [], t])
# 如果右子树为空,则插入右子树作为根节点
else:
root.insert(2, [newBranch, [], []])
# return root
def getRootVal(root):
"""
取根节点的值
:param root:
:return:
"""
return root[0]
def setRootVal(root, newVal):
"""
设置根节点的值
:param root:
:param newVal:
:return:
"""
root[0] = newVal
def getLeftChild(root):
"""
取左子树的值
:param root:
:return:
"""
return root[1]
def getRightChild(root):
"""
取右子树的值
:param root:
:return:
"""
return root[2]
# 下面是测试代码
if __name__ == "__main__":
r = BinaryTree(3) # 创建一个根节点的数据项为3的二叉树
insertLeft(r, 4) # 插入左子树
insertRight(r, 5) # 插入右子树
insertRight(r, 6) # 插入右子树
l = getLeftChild(r) # 取左子树
print(r)
print(l)
setRootVal