1、树的属性
- 树是分层的
- 树的一个节点的所有子节点独立于另一个节点的所有子节点。
- 每个叶节点是唯一的。
- 可以将树的整个部分(子树)移动到树中的不同位置,而不影响层次结构的较低级别。
2、基本概念
- 节点:树的基本组成部分,可以有一个名称,称为“键”。节点也可以有附加信息,称为“有效载荷”
- 边:树的另一个基本组成部分。边连接两个节点以显示它们之间存在关系。
- 根:树中唯一没有传入边的节点。
- 路径:由边连接节点的有序列表。
- 子节点:具有来自相同传入边的节点的集合称为该节点的子节点。
- 父节点:具有和它相同传入边的所连接的节点。
- 兄弟:同一个父节点的所有子节点称为兄弟节点。
- 子树:子树是由父节点和该父节点的所有后代组成的一组节点和边。
- 叶节点:是没有子节点的节点。
- 层数: 节点n的层数为从根节点到该节点所经过的分支数目。根节点的层数为0.
- 高度: 树中任意节点嘴的的层数。
2、使用列表构造树。
myTree = ['a', ['b', ['d',[],[]], ['e',[],[]] ], ['c', ['f',[],[]], []] ]
print(myTree)
print('left subtree = ', myTree[1])
print('root = ', myTree[0])
print('right subtree = ', myTree[2])
['a', ['b', ['d', [], []], ['e', [], []]], ['c', ['f', [], []], []]]
left subtree = ['b', ['d', [], []], ['e', [], []]]
root = a
right subtree = ['c', ['f', [], []], []]
myTree[1][1]
['d', [], []]
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(0)
print(tree)
insertLeft(tree,1)
print(tree)
insertRight(tree, 2)
print(tree)
print(insertLeft(tree, 3))
[0, [], []]
[0, [1, [], []], []]
[0, [1, [], []], [2, [], []]]
[0, [3, [1, [], []], []], [2, [], []]]
- 可见上面的代码在插入子树的时候均是插在根节点下,如果根节点下原来有子树,则将原来的子树移动到新插入的子树的下面。
3、节点表示
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(self):
'''前序遍历作为类的方法'''
print(self.key)
if self.leftChild:
self.leftChild.preorder()
if self.rightChild:
self.rightChild.preorder()
r = BinaryTree('a')
print(r.getRootVal(), r.getLeftChild(), r.getRightChild())
r.insertLeft('b')
print(r.getRootVal(),r.getLeftChild().getRootVal(), r.getRightChild() )
r.insertRight('c')
print(r.getRootVal(),r.getLeftChild().getRootVal(), r.getRightChild().getRootVal())
r.getRightChild().setRootVal('hello')
print(r.getRightChild().getRootVal())
a None None
a b None
a b c
hello
6、使用树实现数学运算
- 如果当前符号是 ‘(’ ,添加一个新节点作为当前节点的左子节点,并下降到左子节点。
- 如果当前符号在列表 [’+’,’ - ‘,’/’,’*’] 中,请将当前节点的根值设置为由当前符号表示的运算符。 添加一个新节点作为当前节点的右子节点,并下降到右子节点。
- 如果当前符号是数字,请将当前节点的根值设置为该数字并返回到父节点。
- 如果当前令牌是 ‘)’ ,则转到当前节点的父节点
def buildParseTree(fpexp):
fplist = fpexp.split()
pStack = []
eTree = BinaryTree('')
pStack.append(eTree)
currentTree = eTree
for i in fplist:
if i == '(':
currentTree.insertLeft('')
pStack.append(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.append(currentTree)
currentTree = currentTree.getRightChild()
elif i == ')':
currentTree = pStack.pop()
else:
raise ValueError
return eTree
pt = buildParseTree("( ( 10 + 5 ) * 3 )")
pt.getRootVal()
'*'
pt.getLeftChild().getLeftChild().getRootVal()
10
import operator
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()
evaluate(pt)
45
7、树的遍历:
def preorder(tree):
'''作为外部方法'''
if tree:
print(tree.getRootVal())
preorder(tree.getLeftChild())
preorder(tree.getRightChild())
def postorder(tree):
'''作为外部方法'''
if tree:
preorder(tree.getLeftChild())
preorder(tree.getRightChild())
print(tree.getRootVal())
def inorder(tree):
'''作为外部方法'''
if tree:
preorder(tree.getLeftChild())
print(tree.getRootVal())
preorder(tree.getRightChild())
preorder(pt)
*
+
10
5
3
postorder(pt)
+
10
5
3
*
inorder(pt)
+
10
5
*
3
def postordereval(tree):
'''使用后序遍历计算表达式的结果'''
opers = {'+': operator.add, '-':operator.sub, '*': operator.mul, '/':operator.truediv}
res1 = None
res2 = None
if tree:
res1 = postordereval(tree.getLeftChild())
res2 = postordereval(tree.getRightChild())
if res1 and res2:
return opers[tree.getRootVal()](res1, res2)
else:
return tree.getRootVal()
postordereval(pt)
45
def printexp(tree):
'''利用中序遍历将树结构的表达式恢复为完全括号表达式'''
sVal = ""
if tree:
sVal = '(' + printexp(tree.getLeftChild())
sVal = sVal + str(tree.getRootVal())
sVal = sVal + printexp(tree.getRightChild()) + ")"
return sVal
printexp(pt)
'(((10)+(5))*(3))'
8、二叉堆实现优先队列
- 优先级队列是队列的一种变种,队列中的项的逻辑顺序由他们的优先级确定,优先级高的项排在队列的前面,因此将一个新的项插入到优先级队列时,新项有可能会移动到前面。二叉堆允许在O(logn)中排队和取出队列。
- 二叉堆有两个有趣变体:最小堆(键值越小优先级越高,越排在前面); 最大堆(键值越大优先级越高)
- 主要的操作
- BinaryHeap()创建新的空二叉堆
- insert(k) 向堆中插入一个新的项
- findMin()返回具有最小键值的项
- delMin()返回拥有最小键值的项,并将其从堆中删除。
- isEmpty()判断二叉堆是否为空
- size()返回堆中项的个数
- buildHeap()从键列表创建一个新的堆。
8.1、实现最小二叉堆
- 完整二叉树:是一个树中,其每层都有其所有的节点,除了树的最底层,从左到右填充。
- 父节点的索引为p,则左右子节点的索引为2p和2p+1.
- 堆的排序属性:在堆中,对具有父p的每个节点x, p中的键小于或等于x中的键。
- 堆是一个完全二叉树,因此超过中间点的任何节点都是树的叶节点。
class BinHeap:
def __init__(self):
self.heapList = [0]
self.currentSize = 0
def insert(self, k ):
self.heapList.append(k)
self.currentSize += 1
self.percUp(self.currentSize)
def percUp(self, i):
'''当前节点的父节点可以通过当前节点的索引除以2来计算'''
while i // 2 > 0:
if self.heapList[i] < self.heapList[i//2]:
temp = self.heapList[i//2]
self.heapList[i // 2] = self.heapList[i]
slef.heapList[i] = temp
i = i // 2
def percDown(self, i):
while (i * 2) <= self.currentSize:
mc = self.minChild(i)
if self.heapList[i] > self.heapList[mc]:
temp = self.heapList[i]
self.heapList[i] = self.heapList[mc]
self.heapList[mc] = temp
i = mc
def minChild(self, i):
'''找到左右子节点中的最小值'''
if i * 2 +1 > self.currentSize:
return i * 2
else:
if self.heapList[i * 2] < self.heapList[i * 2 +1]:
return i * 2
else:
return i * 2 +1
def delMin(self):
'''因为实现的是最小堆,所以在删除时删除的是根节点。找到根节点很容易,但是将其删除后需要维持堆结和堆的顺序属性。
这里分为两步实现。首先将列表的最后一项移动到根位置。然后将新的根节点下沉到合适的位置。 '''
retval = self.heapList[1]
self.heapList[1] = self.heapList[self.currentSize]
self.currentSize -= 1
self.heapList.pop()
self.percDown(1)
return retval
def buildHeap(self, alist):
i = len(alist) // 2
self.currentSize = len(alist)
self.heapList = [0] + alist[:]
while i > 0:
self.percDown(i)
i = i -1
def isEmpty(self):
return self.currentSize == 0
def size(self):
return self.currentSize
def findMin(self):
return self.heapList[1]
bh = BinHeap()
bh.buildHeap([9,5,6,2,3])
print(bh.findMin())
print(bh.delMin())
print(bh.findMin())
print(bh.delMin())
print(bh.delMin())
print(bh.isEmpty())
print(bh.size())
print(bh.delMin())
print(bh.delMin())
print(bh.isEmpty())
print(bh.size())
2
2
3
3
5
False
2
6
9
True
0