python 数据结构与算法——树

二叉树的 python 实现

class BinaryTree:
    def __init__(self, data):
        self._data = data  # root node
        self._left = None  # left child
        self._right = None

    @property
    def data(self):
        return self._data

    @data.setter
    def data(self, data):
        self._data = data

    @property
    def right(self):
        return self._right

    @right.setter
    def right(self, right):
        self._right = right

    @property
    def left(self):
        return self._left

    @left.setter
    def left(self, left):
        self._left = left

    def insertLeft(self, newNode):
        if self.left == None:
            self.left = BinaryTree(newNode)
        else:
            temp = BinaryTree(newNode)
            temp.left = self.left
            self.left = temp

    def insertRight(self, newNode):
        if self.right == None:
            self.right = BinaryTree(newNode)
        else:
            temp = BinaryTree(newNode)
            temp.right = self.right
            self.right = temp

二叉树的应用

  • 编译器中的表达式树
  • 霍夫曼编码树
  • 二叉搜索树
  • 优先队列

二叉树的常见问题

1. 树的遍历

1.1 先根遍历

def preorderRecursive(root, result):
    if not root:
        return
    result.append(root.data)
    preorderRecursive(root.left, result)
    preorderRecursive(root.right, result)

def preorderIterative(root, result):
    if not root:
        return
    stack = []
    stack.append(root)
    while stack:
        node = stack.pop()
        result.append(node.data)
        if node.right:
            stack.append(node.right)
        if node.left:
            stack.append(node.left)

1.2 中根遍历

def inorderRecursive(root, result):
	if not root:
		return

	inorderRecursive(root.left, result)
	result.append(root.data)
	inorderRecursive(root.right, result)

def inorderIterative(root, result):
	if not root:
		return
	stack = []
	node = root
	while stack or node:
		if node:
			stack.append(node)
			node = node.left
		else:
			node = stack.pop()
			result.append(node.data)
			node = node.right

1.3 后根遍历

def postorderRecursive(root, result):
    if not root:
        return    
    postorderRecursive(root.left, result)
    postorderRecursive(root.right, result)
    result.append(root.data)

def postorderIterative(root, result):
    if not root:
        return
    visited = set()
    stack = []
    node = root
    while stack or node:
        if node:
            stack.append(node)
            node = node.left
        else:
            node = stack.pop()
            if node.right and not node.right in visited:
                stack.append(node)
                node = node.right
            else:
                visited.add(node)
                result.append(node.data)
                node = None

1.4 横向遍历

def levelOrder(root, result):
	if root is None:
		return
	q = Queue()
	q.enqueue(root)
	while not q.isEmpty():
		node = q.dequeue()  # FIFO
		result.append(node.data)
		if node.left is not None:
			q.enqueue(node.left)
		if node.right is not None:
			q.enqueue(node.right)

2. 找出树中的最大值

这类题目是醉翁之意不在酒,本质上是想考考你怎么遍历,在遍历的时候保留最大值即可。

def findMaxRecursive(root):
	global maxData
	if not root:
	    return

	if root.data > maxData:
	    maxData = root.data

	findMaxRecursive(root.left)
	findMaxRecursive(root.right)

3. 插入节点

在横向遍历时插入节点

def insertInBinaryTreeUsingLevelOrder(root, data):
	newNode = BinaryTree(data)
	if root is None:
		root = newNode
		return root
	q = Queue()
	q.enQueue(root)
	while not q.isEmpty():
		node = q.deQueue() 
		if node.left is not None:
			q.enQueue(node.left)
		else:
			node.left = newNode
			return root	
		if node.right is not None:
			q.enQueue(node.right)
		else:
			node.right = newNode
			return root

4. 统计树的节点数

def findSizeRecursive(root):
	if root is None:
		return 0
	return 1 + findSizeRecursive(root.left) + findSizeRecursive(root.right)

5. 统计树的深度

def maxDepth(root):
	if root is None:
		return 0
	return 1 + maxDepth( maxDepth(root.left) , maxDepth(root.right) )

6. 找出最深的节点

横向搜索到的最后一个节点就是最深的

def deepestNode(root):
	if root is None:
		return None
	q = Queue()
	q.enQueue(root)
	node = None
	while not q.isEmpty():
		node = q.deQueue() 
		if node.left is not None:
			q.enQueue(node.left)

		if node.right is not None:
			q.enQueue(node.right)
	return node

7. 删除节点

现将要删除的节点和最深的节点交换,然后删除最深的节点

8. 统计树的叶子数

横向搜索到最后一层,计数即可

def numberOfLeaves(root):
	if root is None:
		return 0
	q = Queue()
	q.enQueue(root)
	count = 0
	while not q.isEmpty():
		node = q.deQueue() 
		if node.left is None and node.right is None:
			count += 1
		else:
			if node.left is not None:
				q.enQueue(node.left)

			if node.right is not None:
				q.enQueue(node.right)
	return count

9. 最近公共祖先

Least Common Ancestors

def lca(root, alpha, beta):
	if not root:
		return None
	if root.data == alpha or root.data == beta:
		return root

	left = lca(root.left, alpha, beta)
	right = lca(root.right, alpha, beta)

	if left and right:  # alpha & beta are on both sides
		return root
	else:
		return left if left else right

10. 利用 先根和中根序列 构造树

需要特别提醒,可以确定唯一树的情况有:

  • 先序+中序
  • 后序+中序
  • 层序+中序

以下情况不能唯一确定:

  • 先序+后序
  • 先序+层序
  • 后序+层序
def buildTree(preorder, inorder):
    root = BinaryTree(preorder[0])
    rootPos = inorder.index(preorder[0])
    root.left = buildTree(preorder[1:1+rootPos], inorder[:rootPos])
    root.right = buildTree(preorder[rootPos+1:], inorder[rootPos+1:])
    return root

# 测试
root = buildTree('ABDECF','DBEAFC')
postOrder = []
postorderRecursive(root, postOrder)
assert(''.join(postOrder) == 'DEBFCA')

11. 判断两棵树是否同构

def isIsomorphic(root1, root2):
	if(not root1 and not root2): 
		return True
	if((not root1 and root2) or (root1 and not root2)):
	        return False
	return (isIsomorphic(root1.left, root2.left) and isIsomorphic(root1.right, root2.right))

二叉搜索树

Binary Search Trees

基本操作:

  • 插入
  • 删除
  • 查找

特点:

  • BST 的 中根序列 是从小到大有序排列的
  • BST 的查找效率取决于其深度,最好是 O(logn),最坏是 O(n)

平衡二叉搜索树

为了避免二叉搜索树的左右分支深度差异太大,在插入删除节点时,往往需要局部调整树的结构,保证树的深度始终是 O(logn)

常见的 平衡二叉搜索树 罗列如下:

  • AVL 树
  • 红黑树
  • 伸展树(分裂树)Splay Tree
  • B 树
  • 替罪羊树 Scapegoat Trees

AVL树

Adelson-Velskii and Landis

  • AVL 树是二叉搜索树
  • AVL 树的左右子树的高度差至多为 1
  • AVL 树的高度为 O(logn)

其它

一般树

  • 一般树的子节点可以不止 2 个
  • 一般树可以用二叉树来表示,此时二叉树的左右子节点分别存储一般树的 第一个孩子(firstChild)下一个同辈(nextSibling)

线索二叉树

Threaded Binary Tree

充分利用冗余变量

表达式树

编译

异或(XOR)树

提升空间利用率

发布了274 篇原创文章 · 获赞 446 · 访问量 42万+

猜你喜欢

转载自blog.csdn.net/itnerd/article/details/103887031