概念介绍
本博客在学习北京大学陈斌老师《数据结构与算法》MOOC课程中总结反思形成。
二叉树的鼎鼎大名人尽皆知,它整体的思维逻辑也符合人类整理归纳的习惯,因此在理解概念上的难度并不大。
树作为一种典型的“非线性结构”,具备两个显著的特征:
- 分类体系是层次化的,比如文件系统、物种分类、HTML文档、域名体系等
- 一个节点的子节点与另一个节点的子节点相互之间是隔离、独立的;
- 每一个叶节点都具有唯一性,就比如“我爱学习算法”在不同语言中的表达,尽管都是“我爱学习算法”,但是因为属于不同的
语言体系,它仍然具备唯一性。
树作为一种简洁的表达方式,具备很多术语归纳如下:
二叉树的实现
嵌套列表法
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 BinarryTree:
def __init__(self,rootObj):
self.key = rootObj
self.leftChild = None
self.rightChild = None
def insertLeft(self,newNode):
if self.leftChild == None:
self.leftChild = BinarryTree(newNode)
else:
t = BinarryTree(newNode)
t.leftChild = self.leftChild
self.leftChild = t
def insertRight(self,newNode):
if self.rightChild == None:
self.rightChild = BinarryTree(newNode)
else:
t = BinarryTree(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
应用举例
表达式解析树
项目需求:
从全括号表达式构建表达式解析树,利用表达式解析树对表达式求值,从表达式解析树恢复原表达式的字符串形式
梳理逻辑:
如果当前单词是“(”:为当前节点添加一个新节点作为其左子节点,当前节点下降为这个新节点
如果当前单词是操作符“+,-,*,/”:将当前节点的值设为此符号,为当前节点添加一个新节点作为其右子节点,当前节点下降为这个新节点
如果当前单词是操作数:将当前节点的值设为此数,当前节点上升到父节点
如果当前单词是“)”:则当前节点上升到父节点
要点:当前节点的跟踪
基本操作:
创建左右子树=> 调用insertLeft/Right
当前节点设置值=>调用setRootval
下降到左右子树=>调用getLeft/RightChild
用一个栈=>记录跟踪父节点
当前节点下降时,将下降前的节点push入栈;当前节点需要上升到父节点时,上升到pop出栈的节点
老师实现
from pythonds.basic.stack import Stack
import operator
class BinarryTree:
def __init__(self, rootObj):
self.key = rootObj
self.leftChild = None
self.rightChild = None
def look(self):
print(self.key, self.leftChild, self.rightChild)
def insertLeft(self, newNode):
if self.leftChild == None:
self.leftChild = BinarryTree(newNode)
else:
t = BinarryTree(newNode)
t.leftChild = self.leftChild
self.leftChild = t
def insertRight(self, newNode):
if self.rightChild == None:
self.rightChild = BinarryTree(newNode)
else:
t = BinarryTree(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 buildParseTree(fplist):
pStack = Stack()
eTree = BinarryTree('')
pStack.push(eTree)
currentTree = eTree
for i in fplist:
if i == '(':
currentTree.insertLeft('')
pStack.push(currentTree)
currentTree = currentTree.getLeftChild()
elif i not in ['+', '-', '*', '/', ')']:
currentTree.setRootVal(int(i))
parent = pStack.pop()
currentTree = parent
elif i in ['+', '-', '*', '/']:
currentTree.setRootVal(i)
currentTree.insertRight('')
pStack.push(currentTree)
currentTree = currentTree.getRightChild()
elif i == ')':
currentTree = pStack.pop()
else:
raise ValueError
return eTree
def evaluate(parseTree):
opers = {'+':operator.add,'-':operator.sub,
'*':operator.mul,'/':operator.truediv}
# 缩小规模
leftC = parseTree.getLeftChild()
rightC = parseTree.getRightChild()
# 递归调用
if leftC and rightC:
fn = opers[parseTree.getRootVal()]
return fn(evaluate(leftC),evaluate(rightC))
else:
# 基本结束条件
return parseTree.getRootVal()
自己实现
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]
def buildParseTree(fplist):
pStack = Stack()
eTree = BinaryTree('')
print(eTree)
pStack.push(eTree)
currentTree = eTree
for i in fplist:
if i == '(':
insertLeft(currentTree , '')
pStack.push(currentTree )
currentTree = getLeftChild(currentTree)
print('current:',currentTree)
elif i not in ['+', '-', '*', '/', ')']:
setRootval(currentTree, int(i))
parent = pStack.pop()
currentTree = parent
print('current:', currentTree)
elif i in ['+', '-', '*', '/']:
setRootval(currentTree, i)
insertRight(currentTree, '')
pStack.push(currentTree)
currentTree = getRightChild(currentTree)
print('current:', currentTree)
elif i == ')':
currentTree = pStack.pop()
else:
raise ValueError
print(i,':',eTree)
return eTree
调试过程
自己的实现方式主要采用了列表实现方法,方便打印出中间值去理解二叉树的变化过程,测试方案是(3+(4*5))。
打印结果如下:
['', [], []]
current: ['', [], []]
( : ['', ['', [], []], []]
current: ['', [3, [], []], []]
3 : ['', [3, [], []], []]
current: ['', [], []]
+ : ['+', [3, [], []], ['', [], []]]
current: ['', [], []]
( : ['+', [3, [], []], ['', ['', [], []], []]]
current: ['', [4, [], []], []]
4 : ['+', [3, [], []], ['', [4, [], []], []]]
current: ['', [], []]
* : ['+', [3, [], []], ['*', [4, [], []], ['', [], []]]]
current: ['*', [4, [], []], [5, [], []]]
5 : ['+', [3, [], []], ['*', [4, [], []], [5, [], []]]]
) : ['+', [3, [], []], ['*', [4, [], []], [5, [], []]]]
) : ['+', [3, [], []], ['*', [4, [], []], [5, [], []]]]
心得体会
-
整体实现过程中,二叉树的变化过程和理解有难度。
-
个人认为应该需要重点关注“key”值,关注当前节点的变化去帮助理解。
-
在代码书写过程中,可以手动 画出栈对于节点的管理帮助加深理解。