面试准备-二叉树

二叉树

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

    原文作者:vivian_ll
    原文地址: https://blog.csdn.net/vivian_ll/article/details/88171513
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞