如何使用递归、堆栈和队列来理解和处理倒置二叉树

221 阅读5分钟

倒置二叉树是许多公司面试时最常问的问题之一。在这篇文章中,我们将详细了解如何使用递归、堆栈和队列来理解和处理倒置二叉树的任务。

目录。

  • 二叉树
  • 二叉树的倒置
  • 反转的方法
    • 递归
    • 使用堆栈的迭代解决方案
    • 使用队列的迭代解决方案
  • 不同解决方案的时间和空间复杂度

二叉树

当一棵树中的每个非叶子节点都有至少2个孩子时,它被称为二叉树,下面是一个二叉树的例子。

Invert a Binary tree

二叉树的反转

倒置一棵二叉树是产生另一棵二叉树作为输出,其中原二叉树的所有非叶子节点的左和右节点被互换。倒置的二进制树也被称为镜像树。比如说。

Invert a Binary tree

反转的方法

二叉树可以用递归或迭代的方法进行倒置。对于这两种情况,二叉树节点的基本代码如下:

class Node:
   def __init__(self,val=0):
       self.data=val
       self.left=None
       self.right=None

递归法

在这种方法中,我们通过交换左右节点来反转给定的二叉树,并对每个非叶子节点递归进行同样的过程,直到到达叶子节点。

反转二叉树的步骤:

  1. 如果根/当前节点为NULL,则进行反转
  2. 对于当前节点N,交换左、右子节点
  3. 用步骤1和2递归处理新的左和右子节点

代码片段。

以下是递归二叉树的代码:

class Inversion:
    def invert(self,root:Node):
        if root == None:
            return None
            
        //Swapping the children    
        temp = root.left
        root.left = root.right
        root.right = temp
        
        //Recursion
        self.invert(root.left)
        self.invert(root.right)
        return root

解释

在上面的代码片断中,invert函数首先检查树是否为空。如果不是,它首先交换根的两个子树,然后递归地交换两个子树,直到根有一些值。一旦根为NULL,递归调用就会停止。

使用堆栈的迭代解决方案

这种方法类似于前序遍历法。给定的二叉树使用创建的堆栈进行反转,在堆栈中存储根,使用堆栈操作进行反转。

使用堆栈迭代地反转二叉树的步骤:

  1. 如果根/当前节点为NULL,则进行反转

  2. 定义一个堆栈S

  3. 在堆栈S中加入根节点

  4. 当堆栈S不是空的时候:
    4.1.从堆栈S中弹出节点N
    4.2.交换节点N的左和右子节点
    4.3.4.3. 将节点N的新的左和右子推到堆栈S。

  5. 二叉树是倒置的

代码片段

class Inversion:
    def swap(root):
        if root == None:
            return
            
        //Swapping the children 
        temp = root.left
        root.left = root.right
        root.right = temp
        
    def invert(root:Node):
        if root == None:
            return
        stack = []
        stack.append(root)
        while (stack.empty()!=True):
            current = stack.pop()
            swap(current)
            if(current.right):
                stack.append(current.right)
            if(current.left):
                stack.append(current.left)   
                

解释:在上面的代码片段中,反转类包含两个函数:一个swap函数用于交换根的左和右的子节点,一个invert函数用于执行反转。

在反转函数中,首先创建一个堆栈,将根推入其中。然后,堆栈的顶部元素被弹出,并被送入交换函数,最后的左右节点以同样的顺序被推入堆栈。如此反复,直到堆栈为空。

使用队列的迭代式解决方案

在这种方法中,代码类似于水平顺序遍历。创建了一个队列,而不是一个存储根的堆栈,并且以一种有序的方式进行反转。为了执行队列操作,队列模块被导入如下:

from queue import Queue

使用队列迭代地反转二叉树的步骤。

  1. 如果根/当前节点为NULL,则进行反转。

  2. 定义一个队列Q。

  3. 将根节点添加到队列Q中。

  4. 当队列Q不是空的时候:
    4.1.从队列Q的左侧弹出节点N。
    交换节点N的左右子节点
    4.3.将节点N的新的左右子节点推到队列Q。

  5. 二叉树是倒置的。

代码片段:

class Inversion:
    def swap(root):
        if root == None:
            return
            
        //Swapping the children     
        temp = root.left
        root.left = root.right
        root.right = temp
        
    def invert(root:Node):
        if root == None:
            return
        q = Queue()
        q.put(root)
        while(q.empty()!=True):
            current = q.popleft()
            swap(current)
            if current.left:
                q.put(current.left)
            if current.right:
                q.put(current.right)

解释:在上面的锥体片段中,反转类也包含了一个交换函数和一个反转函数,其目的与在下面给出的相同

'使用堆栈的迭代解决'*中给出的一样,尽管这两个反转函数在逻辑上有所不同。

在反转函数中,首先创建一个队列并将根添加到其中。然后,最左边的元素,即队列的前节点被取消,并通过交换函数来交换其左右子节点。首先,跳出的节点的左子被排队,然后是右子。这样重复进行,直到队列为空。

不同方案的时间和空间复杂度

下面给出了不同解决方案的时间复杂度和空间复杂度:

方法时间复杂度空间复杂度
递归O(n)O(h)
使用堆栈进行迭代解决O(n)O(n)
使用队列的迭代解决方案O(n)O(n)

在上表中,n是树的总节点数,h是树的高度。

当考虑到二叉树的迭代方法时,在堆栈和队列的情况下,对于树中存在的每个节点,增加和删除的操作只发生一次。因此,完成的操作总数=2n。由于每个节点只被访问一次,所以在这两种情况下的时间复杂度都是O(n)。我们注意到,在这两种情况下,内存空间只被创建的堆栈或队列占用,其最大元素数为n(最坏情况)。所以,这两种情况下的空间复杂度都是O(n)。

当考虑倒置二叉树的递归方法时,时间复杂度为O(n),因为树的每个节点只被遍历一次。由于存在递归,函数调用被存储在一个辅助的递归栈中。需要注意的是,递归函数是为每一级调用的,最坏的情况是所有级别的函数调用都放在辅助堆栈中。因此,如果h是二叉树的高度,这种情况下的空间复杂性将是O(h)。

在完成OpenGenus的这篇文章后,你一定对如何反转二叉树有了清晰的认识。