文章目录
一、树
作为数据结构的树和现实世界中的树有很多共同之处,二者皆有根、茎、叶。不同之处在于前者的根在顶部而叶在底部。
树的第一个属性是层次性,第二个属性是一个节点的所有子节点都与另一个节点的所有子节点无关,第三个属性是叶子节点都是独一无二的。
节点是树的基础,我们称作键,节点可以带有附加信息,称为有效载荷。边是树的另一个基础部分,两个节点通过一条边相连,表示它们之间存在的关系,除了根节点以外,每个节点都仅有一条入边,可以有多条出边。根节点是树中唯一没有入边的节点。路径是由边连接的有序节点列表,层数是从根节点到n的唯一路径长度,根节点的层数为0.树的高度是其中节点层数的最大值。
定义1:树由节点及连接节点的边构成,具有以下属性:有一个根节点,除了根节点外,其他的每个节点都与唯一的父节点相连,从根节点到其他每个节点都有且仅有一条路径,如果每个节点有两个子节点,就称这样的树为二叉树。
定义2:一颗树要么为空,要么由一个根结点和零棵或多棵子树欧城,子树本身也是一棵树。每棵子树的根节点通过一条边连到父树的根节点。
实现
BinaryTree()创建一个二叉树实例
getLeftChild()返回当前节点的左子节点所对应的二叉树
getRightChild()返回当前节点的右子节点所对应的二叉树
setRootVal(val)在当前节点中存储参数val中的对象
getRootVal()返回当前节点存储的对象
insertLeft(val)新建一颗二叉树,并将其作为当前节点的左子节点
insertRight(val)新建一颗二叉树,并将其作为当前节点的右子节点
实现树的关键在于选择一个好的内部存储技巧。
列表之列表
在列表之列表的树中,我们将根节点的值作为列表的第一个元素,第二个元素代表的左子树的列表,第三个元素是代表右子树的列表。可以通过标准的切片来访问子树。
'''
不是定义二叉树类
是创建可用于标准列表的函数
'''
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,[],[]])
# 访问函数
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]
r = BinaryTree(3)
insertLeft(r,4)
insertLeft(r,5)
insertRight(r,6)
insertRight(r,7)
print("1",r)
运行结果:
1 [3, [5, [4, [], []], []], [7, [], [6, [], []]]]
节点与引用
利用节点与引用,定义一个类,其中有根节点和左右子树的属性。
'''
构造方法接受一个对象,并将其存储到根节点中
正如可以在列表中存储任何对象,根节点对象也可以成为任何对象的引用
'''
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.left = self.leftChild
self.leftChild = t
def insertRight(self,newNode):
if self.rightChild == None:
self.rightChild = BinaryTree(newNode)
else:
t = BinaryTree(newNode)
t.right = self.rightChild
self.rightChild = t
# 二叉树访问函数
def getRight(self):
return self.rightChild
def getLeft(self):
return self.leftChild
def setRootVal(self,obj):
self.key = obj
def getRootVal(self):
return self.key
# 根节点的左右子节点本身都是BinaryTree类的实例
r = BinaryTree('a')
print("2",r.getRootVal())
print("3",r.getLeft())
r.insertLeft('b')
print("4",r.getLeft())
print("5",r.getLeft().getRootVal())
运行结果:
2 a
3 None
4 <__main__.BinaryTree object at 0x000002A0081CB550>
5 b
二叉树的应用
解析树
解析树可以用来表示现实世界中像句子或数学表达式这样的构造。
问题:
1.如何根据完全括号表达式构建解析树
2.如何计算解析树中的表达式
3.如何将解析树还原成最初的数学表达式
创建4条规则:
1.如果当前标记是(,就可以为当前节点添加一个左子节点,并下沉至该子节点;2.如果当前标记在列表[‘+’,‘-’,‘/’,'']中就将当前节点的值设为当前标记对应的运算符;为当前接待你添加一个右子节点,并下沉至该子节点;3.如果当前标记是数字,就将当前节点的值设为这个数并返回至父节点;4.如果当前标记是),就跳到当前节点的父节点。
表达式(3+(45))构建过程
# 输出树
def postorder(self):
if self.leftChild:
self.leftChild.postorder()
if self.rightChild:
self.rightChild.postorder()
print(self.key)
def buildParseTree(fpexp):
#分割标记
fplist = fpexp.split()
pStack = Stack()
# 创建树
eTree = BinaryTree('')
pStack.push(eTree)
currentTree = eTree
for i in fplist:
if i == '(':
# 创建左子树
currentTree.insertLeft('')
pStack.push(currentTree)
currentTree = currentTree.getLeft()
elif i not in ['+', '-', '*', '/', ')']:
# 数值
currentTree.setRootVal(int(i))
parent = pStack.pop()
currentTree = parent
elif i in ['+', '-', '*