代码随想录day16:二叉树03(平衡二叉树,所有路径,左叶子之和,完全二叉树的节点个数)

1. 平衡二叉树(leetcode 110)

递归:

大家也明白了,比较高度,我们要使用后序遍历。

递归三部曲开始分析:

1. 函数返回值和参数:传入当前节点,返回当前节点高度。如果当前节点左右高度差大于一,我们直接返回-1终止函数。

2. 终止条件,若空节点,高度为0

3. 单层递归逻辑:分别求出左右子树高度,若差值大于一,则return -1。若左右子树有-1的高度的话,也直接return -1。这一步也是要记得处理的,不然之后可能会出错

class Solution:
    def getheight(self, root):
        if not root:
            return 0
        left = self.getheight(root.left)
        if left == -1: return -1   
        #这边不设置的话,之后会可能会继续判断两树高度。要先给他处理了。

        right = self.getheight(root.right)
        if right == -1: return -1

        if abs(left-right) > 1:
            return -1
        return max(left, right) + 1

    def isBalanced(self, root: Optional[TreeNode]) -> bool:
        if not root:
            return 1
        if self.getheight(root) == -1:
            return 0
        else:
            return 1 

迭代:

这题不同于上一题的用层序遍历求深度,我们可以从上往下层序遍历。我们这次要求的是每个节点的左右节点高度,我们只能从下往上。因此我们无法用层序遍历来解决这道题。所以我们只能用栈来模拟后序遍历,设计一个迭代的方程来算出每个节点的高度。然后验证左右的高度差是否为一。

大方向上还是和递归的思路差不多的。但这题使用栈的话我们会进行很多的重复运算,时间效率比较低,所以不做推荐。

2. 二叉树的所有路径(leetcode 257)

这道题我们要找的是根节点到叶子节点的路径,需要使用前序遍历,从上往下走。

在此题中我们将第一次涉及到回溯,因为我们要把每个路径都记录下来,需要通过回溯来退回原来的路径,并进入新的路径。

我们先使用递归做前序遍历。递归回溯是一家的,本题也需要用到回溯。

递归:

三部曲

1. 确认参数以及返回值。我们想要传入根节点,记录每条路径的path,和存放结果集的result。递归不需要返回值

2. 确认递归终止条件。本题的终止条件为找到叶节点,也就是左右孩子为空的时候。

if cur.left == None and cur.right == None:
    终止逻辑

为什么我们不判断cur是否为空呢?因为下面的逻辑可以控制空节点不进入循环。

再来看一下终止处理的逻辑。

我们这里使用list结构来记录每条path,并最终放进我们的result list里面。

我们为什么使用list来记录路径是因为接下来处理单层递归逻辑的时候要做回溯,list方便。

if not cur.left and not cur.right:
    sPath = '->'.join(map(str, path))
    result.append(sPath)
    return

3. 确定单层递归逻辑:因为哦我们使用的是前序遍历,要优先处理中间节点。所以我们要先将我们的cur节点放入path中。

然后是递归和回溯。我们在中止条件那边没有判断cur是否为空,因此在递归的时候如果为空就不进行下一层递归了。因此递归前要加上判断语句,判断我们的下一层cur是否为空。

递归完我们就要进行回溯。我们要知道回溯和递归是一一对应的,有一个递归就有一个回溯。因此我们也应该将回溯写进我们的递归逻辑中,不能拆开。

因此整体的完整版代码如下:

class Solution:
    def traversal(self, cur, path, result):
        path.append(cur.val) #访问并处理中节点
        if not cur.left and not cur.right: #到达叶子
            sPath = '->'.join(map(str, path))
            result.append(sPath)
            return      #完成路径,加入结果集
        if cur.left:   #左节点
            self.traversal(cur.left, path, result)  #左递归
            path.pop()    #回溯
        if cur.right:
            self.traversal(cur.right, path, result)  #右递归
            path.pop()    #回溯

    def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:  
        result = []
        path = []
        if not root:
            return result
        self.traversal(root, path, result)
        return result
        

迭代法:

我们也可以用前序遍历的迭代方式来模拟遍历路径。我们用栈来进行模拟递归,和回溯,存放遍历路径。我们创建一个stack来遍历且处理我们每次访问到的节点,同时我们用一个path stack来记录我们到达每个节点后的当前路径。因此当我们在到达叶节点时,我们就可以把当前路径加入我们的result中,同时删除这个记录,来达到我们回溯的目的,这样我们就能以前序的顺序获得所有路径。

class Solution:
    def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:
        if not root:
            return []
        stack = [root]
        path_st = [str(root.val)]
        result = []
        
        while stack:
            cur = stack.pop()
            path = path_st.pop()
            #如果当前为叶节点,添加到result
            if not cur.left and not cur.right:
                result.append(path)
            if cur.right:
                stack.append(cur.right)
                path_st.append(path + '->' + str(cur.right.val))
            if cur.left:
                stack.append(cur.left)
                path_st.append(path + '->' + str(cur.left.val))
        return result
        

3. 左叶子之和(leetcode 404)

这题我们只需要左叶子,所以判定有两个条件,首先是该节点为左节点,且该节点没有孩子。

递归法:

1. 参数就是我们的根节点,返回值就是左叶子之和

2. 终止条件为空节点。

3. 单层递归逻辑:如果遇到左叶子,记录数值,然后通过递归取左子树的左叶子和,以及右子树的左叶子之和。

class Solution:
    def sumOfLeftLeaves(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0
        leftval = 0  #初始化左叶子值,有的root没有左叶子
        if root.left and (not root.left.left and not root.left.right):
            leftval = root.left.val
        
        #中左右结构递归。
        return leftval + self.sumOfLeftLeaves(root.left) + self.sumOfLeftLeaves(root.right)
        

这边也补充一下迭代法的写法,同样是前序遍历,处理好判断条件就行

class Solution:
    def sumOfLeftLeaves(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0
        stack = [root]
        leftsum = 0

        while stack:
            cur = stack.pop()
            if cur.left and (not cur.left.left and not cur.left.right):
                leftsum += cur.left.val
            if cur.left and (cur.left or cur.right):
                stack.append(cur.left)
            if cur.right and (cur.left or cur.right):
                stack.append(cur.right)
        return leftsum

4. 完全二叉树的节点个数(leetcode 222)

这题也是比较简单,我们右递归按前序的方式向下,沿途访问一个节点,count+1就行。

class Solution:
    def countNodes(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0
        return self.countNodes(root.left) + self.countNodes(root.right) +1

迭代:

class Solution:
    def countNodes(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0
        count = 0
        stack = [root]
        while stack:
            cur = stack.pop()
            count += 1
            if cur.left: stack.append(cur.left)
            if cur.right: stack.append(cur.right)
        return count

bfs:

class Solution:
    def countNodes(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0
        count = 0
        queue = collections.deque([root])
        while queue:
            for _ in range(len(queue)):
                cur = queue.popleft()
                count += 1
                if cur.left:
                    queue.append(cur.left)
                if cur.right:
                    queue.append(cur.right)
        return count

完全二叉树写法:

以上三种写法是我们遍历普通二叉树的方式,时间复杂度为O(n)但我们要注意到这题的数是完全二叉树。完全二叉树只有最底层会没满,前面的第n层每层包含了2*n个节点。若全满为满二叉树。

完全二叉树只有两种情况,情况一:就是满二叉树,情况二:最后一层叶子节点没有满。

对于情况一,可以直接用 2^树深度 - 1 来计算,注意这里根节点深度为1。

对于情况二,分别递归左孩子,和右孩子,递归到某一深度一定会有左孩子或者右孩子为满二叉树,然后依然可以按照情况1来计算。

在完全二叉树中,如果递归向左遍历的深度等于递归向右遍历的深度,那说明就是满二叉树。

class Solution:
    def countNodes(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0
        count = 1
        left = root.left
        right = root.right
        while left and right:
            count += 1
            left = left.left
            right = right.right
        if not left and not right:
            return 2 ** count -1
        return self.countNodes(root.left) + self.countNodes(root.right) + 1

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值