代码随想录算法训练营day15 | 110.平衡二叉树、257. 二叉树的所有路径、404.左叶子之和、222.完全二叉树的节点个数

碎碎念:
参考:代码随想录

110.平衡二叉树

题目链接

110.平衡二叉树

思想

高度是节点距离叶子节点的距离。——后序
深度是节点距离根节点的距离。——前序
本题使用后序遍历。
递归三部曲:

  1. 参数和返回值:要判断左右子树的高度,所以返回值为int类型。要是判断当前的树已经不是平衡二叉树了就向上返回-1.
  2. 空节点返回0.
  3. 单层递归的逻辑:
    左:递归 再看得到的返回值是不是-1
    右:递归 再看得到的返回值是不是-1
    中:两个子树都是平衡二叉树了,接下来要看左右子树的高度差,高度差>1,return -1;符高度差<=1返回1+max(右子树的高度,左子树的高度)。

题解

class Solution {
public:
    int getHeight(TreeNode* node) {
        if (node == NULL) return 0;
        int leftHeight = getHeight(node->left);
        if (leftHeight == -1) return -1;
        int rightHeight = getHeight(node->right);
        if (rightHeight == -1) return -1;
        return abs(leftHeight - rightHeight) > 1 ? -1 : 1 + max(leftHeight, rightHeight);
    }
    bool isBalanced(TreeNode* root) {
        return getHeight(root) == -1 ? false : true;
    }
};
class Solution:
    def getHeight(self, node):
        if node == None:
            return 0;
        leftHeight = self.getHeight(node.left)
        if leftHeight == -1:
            return -1
        rightHeight = self.getHeight(node.right)
        if rightHeight == -1:
            return -1
        return -1 if abs(leftHeight-rightHeight) > 1 else 1+max(leftHeight, rightHeight)
        

    def isBalanced(self, root: Optional[TreeNode]) -> bool:
        return False if self.getHeight(root) == -1 else True
        

反思

257. 二叉树的所有路径

题目链接

257. 二叉树的所有路径

思想

应该用前序遍历,因为使用前序遍历才会输出路径。
**回溯:**比如上图,把5弹出去,把2弹出去,这个过程就是回溯。
递归三部曲:

  1. 返回值参数path单条路径result string类型最后的结果
  2. 终止条件:左右孩子都为空(遍历到了叶子节点),把path转成字符串再加入result。 return
  3. 单层逻辑:为了防止漏掉叶子节点,所以路径收集的操作要放到终止条件的上面。左:如果左节点不为空,向左遍历,回溯;右:如果右节点不为空,向右遍历,回溯。
    注意常见的题解代码里隐含了回溯。

题解

// C++比较细节的代码写法
class Solution {
public:
    void traversal(TreeNode* cur, vector<int>& path, vector<string>& result) {
        path.push_back(cur->val);  // 放在这里防止漏掉叶子节点
        if (cur->left == NULL && cur->right == NULL) {
            string sPath;
            for (int i = 0; i < path.size() - 1; i++) {
                sPath += to_string(path[i]);
                sPath += "->";
            }
            sPath += to_string(path[path.size() - 1]);
            result.push_back(sPath);
            return;
        }
        if (cur->left) {
            traversal(cur->left, path, result);
            path.pop_back();  // 回溯
        }
        if (cur->right) {
            traversal(cur->right, path, result);
            path.pop_back();  // 回溯
        }
    }
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> result;
        vector<int> path;
        if (root == NULL) return result;
        traversal(root, path, result);
        return result;
    }
};
// C++常见的解法 隐含了回溯
class Solution {
private:

    void traversal(TreeNode* cur, string path, vector<string>& result) {
        path += to_string(cur->val); // 中
        if (cur->left == NULL && cur->right == NULL) {
            result.push_back(path);
            return;
        }
        if (cur->left) traversal(cur->left, path + "->", result); // 左 回溯隐含在path+"->" 执行完递归函数以后,path依然是之前的数,相当于回溯
        if (cur->right) traversal(cur->right, path + "->", result); // 右
    }

public:
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> result;
        string path;
        if (root == NULL) return result;
        traversal(root, path, result);
        return result;

    }
};
# python
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)) # map 函数,将 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

反思

注意看是怎么回溯的,也注意积累一下字符串的处理。

404.左叶子之和

题目链接

404.左叶子之和

思想

在这里插入图片描述
遍历到9的时候可以知道它是不是叶子节点,但是无法判断是不是为父节点的左孩子。所以要通过它的父节点来判断。
本题应该使用后序遍历。
递归三部曲:

  1. 参数和返回值
  2. 终止条件 如果node==null 返回0; 如果是叶子节点,返回0
  3. 单层操作: 左:递归左边,如果当前节点的左孩子不为空,左孩子还是叶子节点,得到leftnum;右:递归右子树;中:返回左子树符合条件的和+右子树符合条件的和。

题解

// cpp
class Solution {
public:
    int sumOfLeftLeaves(TreeNode* root) {
        if (root == NULL) return 0;
        int leftValue = sumOfLeftLeaves(root->left);
        if (root->left && !root->left->left && !root->left->right)
            leftValue = root->left->val;
        int rightValue = sumOfLeftLeaves(root->right);

        return leftValue + rightValue;
    }
};
class Solution:
    def sumOfLeftLeaves(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0
        
        leftValue = self.sumOfLeftLeaves(root.left)
        if root.left and not root.left.left and not root.left.right:
            leftValue = root.left.val
        rightValue = self.sumOfLeftLeaves(root.right)

        return leftValue + rightValue
        

222.完全二叉树的节点个数

题目链接

222.完全二叉树的节点个数

思想

普通二叉树直接遍历就行,这道题强调了完全二叉树,要求我们尽量使用完全二叉树的特性。
**普通二叉树解法:**后序遍历
利用完全二叉树特性的解法:
满二叉树是一种特殊的二叉树,其中每个节点要么没有子节点,要么恰好有两个子节点。换句话说,除了叶子节点外,每个节点都有两个子节点。满二叉树的一个特性是,如果其深度为 (d),则总共有 (2^{d} - 1) 个节点。
完全二叉树是指除了最后一层外,其他每一层的节点数都达到最大可能数,并且最后一层的节点都集中在左边。对于完全二叉树,分别递归左孩子,和右孩子,递归到某一深度一定会有左孩子或者右孩子为满二叉树,然后依然可以按照满二叉树的节点数计算公式来计算。
**如何判断一个子树是否为满二叉树:**分别计算左侧深度和右侧深度,如果两数相等,说明它是满二叉树,就可以用公式计算。
这种方式利用了满二叉树的特性,也避免遍历没有必要遍历的节点。
递归三部曲:

  1. 返回值和参数
  2. 终止条件: NULL 以及找到满二叉树(分别计算左侧深度和右侧深度,然后比较深度,相等就直接用公式计算节点数量)
  3. 单层逻辑:向左遍历,向右遍历,返回左子树节点数量+右子树节点数量+1

这种方式并没有把所有的节点都遍历一遍。

题解

// 普通二叉树解法 后序遍历 cpp
class Solution {
public:
    int countNodes(TreeNode* root) {
        if (root == NULL) return 0;
        return countNodes(root->left) + countNodes(root->right) + 1;
    }
};
# 普通二叉树解法 后序遍历 python
class Solution:
    def countNodes(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0
        return 1 + self.countNodes(root.left) + self.countNodes(root.right)        
// 利用完全二叉树性质解法 cpp
class Solution {
public:
    int countNodes(TreeNode* root) {
        if (root == NULL) return 0;
        int leftDepth = 1, rightDepth = 1;
        TreeNode* left = root->left;
        TreeNode* right = root->right;
        while (left) {
            left = left->left;
            leftDepth++;
        }
        while (right) {
            right = right->right;
            rightDepth++;
        }
        if (leftDepth == rightDepth) {
            return (1 << leftDepth) - 1;  // 左移1位是x2 左移leftDepth位是x2^(leftDepth) 

        }
        
        return countNodes(root->left) + countNodes(root->right) + 1;
    }
};
# 利用完全二叉树性质的解法
class Solution:
    def countNodes(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0
        leftDepth = 1
        rightDepth = 1
        left = root.left
        right = root.right
        while left:
            left = left.left
            leftDepth += 1
        while right:
            right = right.right
            rightDepth += 1
        if leftDepth == rightDepth:
            return (1 << leftDepth) - 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、付费专栏及课程。

余额充值