二叉树
1.前序:根-左-右
# 先序打印二叉树(递归)
def preOrderTraverse(node):
if node is None:
return None
print(node.val)
preOrderTraverse(node.left)
preOrderTraverse(node.right)
# 先序打印二叉树(非递归)
def preOrderTravese(node):
stack = [node]
while len(stack) > 0:
print(node.val)
if node.right is not None:
stack.append(node.right)
if node.left is not None:
stack.append(node.left)
node = stack.pop()
# 思路:借助辅助栈。当根结点存在,保存结果,根结点入栈;将根结点指向左子树;根结点不存在,栈顶元素出栈,并将根结点指向栈顶元素的右子树;直到栈空。
2.中序:左-根-右
# 中序打印二叉树(递归)
def inOrderTraverse(node):
if node is None:
return None
inOrderTraverse(node.left)
print(node.val)
inOrderTraverse(node.right)
# 中序打印二叉树(非递归)
def inOrderTraverse(node):
stack = []
pos = node
while pos is not None or len(stack) > 0:
if pos is not None:
stack.append(pos)
pos = pos.left
else:
pos = stack.pop()
print(pos.val)
pos = pos.right
3.后序:左-右-根
# 后序打印二叉树(递归)
def postOrderTraverse(node):
if node is None:
return None
postOrderTraverse(node.left)
postOrderTraverse(node.right)
print(node.val)
# 后序打印二叉树(非递归)
# 使用两个栈结构
# 第一个栈进栈顺序:左节点->右节点->跟节点
# 第一个栈弹出顺序: 跟节点->右节点->左节点(先序遍历栈弹出顺序:跟->左->右)
# 第二个栈存储为第一个栈的每个弹出依次进栈
# 最后第二个栈依次出栈
def postOrderTraverse(node):
stack = [node]
stack2 = []
while len(stack) > 0:
node = stack.pop()
stack2.append(node)
if node.left is not None:
stack.append(node.left)
if node.right is not None:
stack.append(node.right)
while len(stack2) > 0:
print(stack2.pop().val)
4.按层遍历:从上到下、从左到右按层遍历
# 先进先出选用队列结构
import queue
def layerTraverse(head):
if not head:
return None
que = queue.Queue() # 创建先进先出队列
que.put(head)
while not que.empty():
head = que.get() # 弹出第一个元素并打印
print(head.val)
if head.left: # 若该节点存在左子节点,则加入队列(先push左节点)
que.put(head.left)
if head.right: # 若该节点存在右子节点,则加入队列(再push右节点)
que.put(head.right)
def levelOrder(self, root: TreeNode):
# 两个列表保存当前层和下一层 不用数据结构
output = []
if root is None:
return output
stack = [] # stack保存每一层
stack.append(root)
while (stack):
curr_level = []
next_level = [] # 两个列表保存当前层和下一层
for node in stack:
curr_level.append(node.val)
if node.left:
next_level.append(node.left)
if node.right:
next_level.append(node.right)
stack = next_level
output.append(curr_level)
return output
5.二叉树节点个数
# 求二叉树节点个数
def treeNodenums(node):
if node is None:
return 0
nums = treeNodenums(node.left)
nums += treeNodenums(node.right)
return nums + 1
6.二叉树的最大深度
# 二叉树的最大深度
def bTreeDepth(node):
if node is None:
return 0
ldepth = bTreeDepth(node.left)
rdepth = bTreeDepth(node.right)
return (max(ldepth, rdepth) + 1)
7.之字形遍历
思路:使用两个栈,一个栈存奇数行(先保存右子树再保存左子树),一个栈存偶数行(先保存左子树再保存右子树)。
思路2:在按层打印的基础上将奇数层列表反向
8.输入某二叉树的前序遍历和中序遍历的结果,重建该二叉树
在拿到这道题目的时候,我们首先明确一点,就是如何根据前序遍历和中序遍历来求出根结点。根结点对于二叉树来说,至关重要,只有先确定根结点,我们才能确定其他结点。
我们还是从一个测试用例开始。
假设某个二叉树的前序遍历结果为{1, 2, 4, 7, 3, 5, 6, 8}, 中序遍历的结果为{4, 7, 2, 1, 5, 3, 8, 6}。因为二叉树的前序遍历是根据:根,左,右的顺序来,所以前序遍历开头的元素就是根结点,也就是说,1就是二叉树的根结点。然后再看看中序遍历:左, 根,右,前面已经确定了1就是根结点,那么1的左边序列就是二叉树左边的元素,也就是说,{4, 7, 2}就是二叉树左边的元素,而{5, 3, 8, 6}就是二叉树右边的元素。
二叉树的特点就是任何非叶结点的结点都可以成为根结点,所以我们可以从上面得到的两个序列中,按照之前的分析推敲出整个树的结构,也就是采用递归的方法。
9.输入两棵二叉树A和B,判断B是不是A的子结构
递归
10.输入一个二叉树,输出它的镜像
前序遍历二叉树,然后在找到左右子结点后将它们进行交换的过程。
11.输入一个整数数组,判断该数组是否是某二叉搜索树的后序遍历的结果
对于一个序列S,最后一个元素是x (也就是根),如果去掉最后一个元素的序列为T,那么T满足:T可以分成两段,前一段(左子树)小于x,后一段(右子树)大于x,且这两段(子树)都是合法的后序序列。
12.输入一棵二叉树和某个整数值,打印出二叉树中结点值的和为输入整数的所有路径,所谓的路径,是指从根结点开始到叶结点,形成一条路径
在二叉树的所有遍历算法中,只有前序遍历是从根结点开始的,所以我们可以考虑一下前序遍历。既然是要计算路径上所有值的总和,那么保存路径上的每个值也是必要的。更重要的步骤是当我们发现这条路径不对时,要能够返回到叶结点的根结点,并且删除掉这些值,也就是说,路径上的值是”先进后出”,也就是栈的结构。
13.判断二叉树是否为二叉搜索树、完全二叉树、平衡二叉树
1.二叉搜索树:若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。(即中序遍历情况下,值依次增大)
# 二叉搜索树
# 中序遍历情况下,值递增则为二叉树
def isBSTree(head):
minimum = -100000 # 设定一个最小值
if head is None:
return False
prenum = minimum
stack = []
while head or len(stack) > 0:
if head:
stack.append(head)
head = head.left
else:
head = stack.pop()
if head.val < prenum: # 保证中序遍历情况下值递增
return False
else:
prenum = head.val
head = head.right
return True
2.完全二叉树:若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。(除了最后一层之外的其他每一层都被完全填充,并且所有结点都保持向左对齐。)
# 判断一棵树是否为完全二叉树
# 左无、右有 ==> 返回 False
# 左无、右无 ==> 激活判断:之后所有节点都是叶节点
# 左有、右无 ==> 激活判断:之后所有节点都是叶节点 ==》 只要右无之后都必须是叶节点
# 左有、右有 ==> 不用处理
import queue
def isCBTree(head):
if not head:
return False
que = queue.Queue()
que.put(head)
flag = False # 是否激活判断过程
while not que.empty():
head = que.get()
if head.left:
que.put(head.left)
if head.right:
que.put(head.right)
if (not head.left) and head.right: #左空、又不空必不为CBT
return False
if flag: # 若过程激活则判断节点是否为叶节点
if head.left or head.right:
return False
if not (head.left and head.right): # 左不空、右空 | 左空、右空
flag = True # 激活判断在此之后的节点必须为叶节点
return True
3.平衡二叉树:平衡二叉树是一棵二叉树,其可以为空,或满足如下2个性质:①左右子树深度之差的绝对值不大于1。②左右子树都是平衡二叉树。
第一种写法:递归返回判断结果和子节点深度
# 判断二叉树是否为平衡二叉树
# 先判断该节点是否平衡
# 再递归去判断左节点和右节点是否平衡
def process(head):
if head is None:
return True, 0
leftData = process(head.left)
if not leftData[0]:
return False, 0
rightData = process(head.right)
if not rightData[0]:
return False, 0
if abs(leftData[1]-rightData[1]) > 1:
return False, 0
return True, max(leftData[1],rightData[1]) + 1
第二种常见写法:
# 判断二叉树是否为平衡二叉树
# 先判断该节点是否平衡
# 再递归去判断左节点和右节点是否平衡
# 递归求当前节点的深度
def getdepth(node):
if not node:
return 0
ld = getdepth(node.left)
rd = getdepth(node.right)
return max(ld, rd) + 1
def isB(head):
if not head:
return True
ld = getdepth(head.left)
rd = getdepth(head.right)
if abs(ld - rd) > 1:
return False
return isB(head.left) and isB(head.right)
14.输入一棵二叉搜索树,将该树转化成一个排序的双向链表,要求不能创建任何新的结点,只能调整树中结点指针的指向
由于需要排序,在二叉树所有遍历算法中,只有中序遍历是按照顺序来的。
15.如果我们将二叉树看成一个图,父子结点间的连线看成是双向的,将两个结点间的边的个数定义了这两个结点的距离。写一个程序求一棵二叉树中相距最远的两个结点间的距离
遇到这种问题,可以自己画几个二叉树来寻找规律:相距最远的两个结点,一定是两个叶结点或者是一个叶结点到它的根结点。
无论是什么情况,我们都要遍历二叉树中所有结点一次,所以提高效率的关键就是遍历的算法。我们可以采用深度优先遍历算法。
手写程序细节
注意类内函数调用时的self、函数返回时不要忘记return
参考网址:
https://www.cnblogs.com/icekx/p/9127569.html
https://www.cnblogs.com/icekx/p/9131304.html
https://www.cnblogs.com/wenjiang/p/3321815.html