通用算法 - [树结构] -二叉树的一些基本操作(一)

1、前言

数组、链表、二叉树、动态规划、栈与队列是面试中常考的知识点,而在这几个知识点中,二叉树属于难度比较大的部分,因此在这里总结一下二叉树常考的操作,包括:
(1)二叉树的递归遍历;
(2)二叉树的非递归遍历;
(3)求二叉树叶子节点的个数;
(4)求二叉树的深度;
(5)在二叉树中查找某颗子树;
(6)求二叉树中和为某个值的路径;
(7)求二叉树中的最大路径和;
(8)二叉树的镜像;
(9)判断某个序列是否是二叉搜索树的后序遍历序列;
(10)二叉搜索树和双向链表的相互转换;
(11)树中两个结点的最近祖先;
(12)二叉树搜索树的插入与删除;
(13)堆的插入与删除;
(14)判断一棵树是不是平衡二叉树;
(15)平衡二叉树的调整;

2、二叉树的基本操作

(1)二叉树的递归遍历
递归的形式非常简洁,几行代码即可搞定。前序、后序、中序遍历的形式基本一样,只是输出结点值的时机不一样。
基本思想:如果该结点为叶结点,则直接返回,否则,看该结点的左孩子是否为空,如果不为空,则递归的遍历其左孩子;接着看其右孩子是否为空,如果不为空,则递归的遍历其右孩子;
代码实现

    
def PreOrder_recursion(rootnode):

    if rootnode == None:

        return

    print(rootnode.val)

    if rootnode.left != None:

        PreOrder_recursion(rootnode.left)

    if rootnode.right != None:

        PreOrder_recursion(rootnode.right)

    return

def MidOrder_recursion(rootnode):

    if rootnode == None:

        return

    if rootnode.left != None:

        MidOrder_recursion(rootnode.left)

    print(rootnode.val)

    if rootnode.right != None:

        MidOrder_recursion(rootnode.right)

    return 

def BackOrder_recursion(rootnode):

    if rootnode == None:

        return 
    
    if rootnode.left != None:

        BackOrder_recursion(rootnode.left)

    if rootnode.right != None:

        BackOrder_recursion(rootnode.right)

    print(rootnode.val)

    return

(2)二叉树的非递归遍历

基本思想:如果采用非递归遍历,我们需要使用一个栈来模拟函数的递归调用。对于三种遍历方式,我们都采用push当前结点->push左子树->pop左子树->push右子树->pop右子树的方式,只是不同的遍历方式,输出结点值的时机不同。

对于前序遍历来说,每次访问到一个节点,即每次要将节点进栈时,输出该节点的值;【即在左右节点入栈时输出左右子节点的值】

对于中序遍历来说,每次将当前节点的右节点进栈时,输出当前节点的值;【即在右节点进栈、以及pop叶节点的时候,输出当前节点的值】

对于后序遍历来说,每次将节点从栈中弹出的时候,输出当前节点的值。【即在pop的时候输出当前节点的值】

另外,我们还需要一个last_pop指针来记录上次pop的节点;

如果当前节点的左节点非空,并且左节点和子节点都不是上次last_pop,则将左节点进栈;

如果当前节点的右节点非空,也不是last_pop,且当前节点的左节点为空或者左节点是last_pop,则将右节点进栈;

否则的话,让当前节点出栈,让当前节点标记为last_pop。

  • 注意,这里有两个比较重要的数据结构:
  • 当前节点:栈顶元素;
  • last_pop:上一次pop出去的结点;

当前结点被pop有两种情况,一是当前结点为叶节点(左节点 和右节点都为空),二是当前结点的左右结点都已经被访问过了(右节点为last_pop)

二叉树的前序、中序、后序遍历采用的都是深度优先策略。

代码实现

def PreOrder_Norecursion(rootnode):
    stack = []

    stack.append(rootnode)

    print(rootnode.val,end="")

    last_pop = rootnode

    while len(stack) != 0:

        curnode = stack[-1]

        if curnode.left != None and curnode.left != last_pop and curnode.right != last_pop:

            stack.append(curnode.left)

            print(curnode.left.val,end="")

        elif curnode.right != None and curnode.right != last_pop and (curnode.left == None or curnode.left == last_pop):

            stack.append(curnode.right)

            print(curnode.right.val,end="")

        else:

            stack.pop()

            last_pop = curnode

    return


def MidOrder_Norecursion(rootnode):
    stack = []

    stack.append(rootnode)

    last_pop = rootnode

    while len(stack) != 0:

        curnode = stack[-1]

        if curnode.left != None and curnode.left != last_pop and curnode.right != last_pop:

            stack.append(curnode.left)

        elif curnode.right != None and curnode.right != last_pop and (curnode.left == None or curnode.left == last_pop):

            stack.append(curnode.right)

            print(curnode.val,end="")

        else:

            stack.pop()

            last_pop = curnode

            if curnode.right == None:
                print(curnode.val,end="")

    return


def BackOrder_Norecursion(rootnode):
    stack = []

    stack.append(rootnode)

    last_pop = rootnode

    while len(stack) != 0:

        curnode = stack[-1]

        if curnode.left != None and curnode.left != last_pop and curnode.right != last_pop:

            stack.append(curnode.left)

        elif curnode.right != None and curnode.right != last_pop and (curnode.left == None or curnode.left == last_pop):

            stack.append(curnode.right)

        else:

            stack.pop()

            last_pop = curnode

            print(curnode.val,end="")
    return

(3)二叉树种叶子节点的个数
基本思路:使用变量leaf_count对叶节点进行计数,在遍历二叉树时,如果遇到叶节点,则leaf_count 的值加1。

代码实现

def LeafNums_Norecursion(rootnode):
    leaf_count = 0

    stack = []

    stack.append(rootnode)

    last_pop = rootnode

    while len(stack) != 0:

        curnode = stack[-1]

        if curnode.left != None and curnode.left != last_pop and curnode.right != last_pop:

            stack.append(curnode.left)

        elif curnode.right != None and curnode.right != last_pop and (curnode.left == None or curnode.left == last_pop):

            stack.append(curnode.right)

        else:

            stack.pop()

            last_pop = curnode

            if curnode.left == None and curnode.right == None:
                leaf_count += 1

    return leaf_count

leaf_count2 = 0
def LeafNums_Recursion(rootnode):
    if rootnode == None:
        return

    if rootnode.left == None and rootnode.right == None:

        global leaf_count2

        leaf_count2 += 1

    if rootnode.left != None:
        LeafNums_Recursion(rootnode.left)

    if rootnode.right != None:
        LeafNums_Recursion(rootnode.right)

    return

(4)二叉树的深度
基本思想
(1)递归:如果节点为空,则深度为0,否则,该树的深度等于左子树的深度与右子树深度的最大值+1
(2)非递归:stack模拟函数的递归调用,那么栈中元素的最大数量即为树的深度。

代码实现

def Maxdepth_Recursion1(rootnode):
    # 遇到空节点才返回

    if rootnode == None:
        return 0

    left_depth = Maxdepth_Recursion1(rootnode.left)

    right_depth = Maxdepth_Recursion1(rootnode.right)

    root_depth = max(left_depth, right_depth) + 1

    return root_depth


def Maxdepth_Recursion2(rootnode):
    # 遇到叶节点或者空节点,返回,避免多递归一层。

    if rootnode == None:
        return 0

    if rootnode.left == None and rootnode.right == None:
        return 1

    left_depth = 0

    if rootnode.left != None:
        left_depth = Maxdepth_Recursion2(rootnode.left)

    right_node = 0

    if rootnode.right != None:
        right_depth = Maxdepth_Recursion2(rootnode.right)

    root_depth = 0

    root_depth = max(left_depth, right_depth) + 1

    return root_depth


def Maxdepth_NoRecursion(rootnode):

    stack = []

    stack.append(rootnode)

    last_pop = rootnode

    depth = 0

    while len(stack) != 0:

        curnode = stack[-1]

        depth = max(len(stack), depth)

        if curnode.left != None and curnode.left != last_pop and curnode.right != last_pop:

            stack.append(curnode.left)

        elif curnode.right != None and curnode.right != last_pop and (curnode.left == None or curnode.left == last_pop):

            stack.append(curnode.right)

        else:

            stack.pop()

            last_pop = curnode

    return depth

>>下一篇:通用算法 - [树结构] -二叉树的一些基本操作(二)

你可能感兴趣的:(数据结构与算法)