树的一些定义
节点
节点是树的基本部分。它可以有一个名称,我们称之为“键”。节点也可以有附加信息。我们将
这个附加信息称为“有效载荷”。虽然有效载荷信息不是许多树算法的核心,但在利用树的应用
中通常是关键的。
边
边是树的另一个基本部分。边连接两个节点以显示它们之间存在关系。每个节点(除根之
外)都恰好从另一个节点的传入连接。每个节点可以具有多个输出边。
根
树的根是树中唯一没有传入边的节点。在 Figure 2 中,/ 是树的根。
路径
路径是由边连接节点的有序列表。例如,
Mammal→→Carnivora→→Felidae→→Felis→→Domestica是一条路径。
子节点
具有来自相同传入边的节点 c 的集合称为该节点的子节点。在 Figure 2中,节点 log/,spool/
和 yp/ 是节点 var/ 的子节点。
父节点
具有和它相同传入边的所连接的节点称为父节点。在 Figure 2 中,节点 var/ 是节点 log/,
spool/ 和 yp/ 的父节点。
兄弟
树中作为同一父节点的子节点的节点被称为兄弟节点。节点 etc/ 和 usr/ 是文件系统树中的兄
弟节点。
子树
子树是由父节点和该父节点的所有后代组成的一组节点和边。
叶节点
叶节点是没有子节点的节点。
层数
节点 n 的层数为从根结点到该结点所经过的分支数目。
高度
树的高度等于树中任何节点的最大层数。
树具有以下属性:
- 树的一个节点被指定为根节点。
- 除了根节点之外,每个节点 n 通过一个其他节点 p 的边连接,其中 p 是 n 的父节点。
- 从根路径遍历到每个节点路径唯一。
- 如果树中的每个节点最多有两个子节点,我们说该树是一个二叉树。
列表型树
myTree = ['a', #root
['b', #left subtree
['d', [], []],
['e', [], []] ],
['c', #right subtree
['f', [], []],
[] ]
]
myTree = ['a', ['b', ['d',[],[]], ['e',[],[]] ], ['c', ['f',[],[]], []] ]
print(myTree)
print('left subtree = ', myTree[1])
print('root = ', myTree[0])
print('right subtree = ', myTree[2])
操作列表树
要将左子树添加到树
的根,我们需要在根列表的第二个位置插入一个新的列表。我们必须小心。如果列表已经在
第二个位置有东西,我们需要跟踪它,并沿着树向下把它作为我们添加的列表的左子节点。
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]
节点表示
class BinaryTree:
def __init__(self,rootObj):
self.key = rootObj
self.leftChild = None
self.rightChild = None
我们必须考虑两种插入情况。 第一种情况的特征没有现有左孩子的节点。当没有左孩子时,只需向树中添加一个节点。 第二种情况的特征在于具有现有左孩子的节点。在第二种情况下,我们插入一个节点并将现有的子节点放到树中的下一个层。
def insertLeft(self,newNode):
if self.leftChild == None:
self.leftChild = BinaryTree(newNode)
else:
t = BinaryTree(newNode)
t.leftChild = self.leftChild
self.leftChild = t
def insertRight(self,newNode):
if self.rightChild == None:
self.rightChild = BinaryTree(newNode)
else:
t = BinaryTree(newNode)
t.rightChild = self.rightChild
self.rightChild = t
def getRightChild(self):
return self.rightChild
def getLeftChild(self):
return self.leftChild
def setRootVal(self,obj):
self.key = obj
def getRootVal(self):
return self.key
树的遍历
有三种常用的模式来访问树中的所有节点。这些模式之间的差异是每个节点被访问的顺序。我们称这种访问节点方式为“遍历”。我们将看到三种遍历方式称为 前序,中序 和 后序 。
- 前序 在前序遍历中,我们首先访问根节点,然后递归地做左侧子树的前序遍历,随后是右侧子树的递归前序遍历。
- 中序 在一个中序遍历中,我们递归地对左子树进行一次遍历,访问根节点,最后递归遍历右子树。
- 后序 在后序遍历中,我们递归地对左子树和右子树进行后序遍历,然后访问根节点。
def preorder(tree): #前序
if tree:
print(tree.getRootVal())
preorder(tree.getLeftChild())
preorder(tree.getRightChild())
def postorder(tree): #后序
if tree != None:
postorder(tree.getLeftChild())
postorder(tree.getRightChild())
print(tree.getRootVal())
def inorder(tree): #中序
if tree != None:
inorder(tree.getLeftChild())
print(tree.getRootVal())
inorder(tree.getRightChild())