代码随想录算法 - 二叉树2

题目1 110. 平衡二叉树

给定一个二叉树,判断它是否是

平衡二叉树

示例 1:

img

输入:root = [3,9,20,null,null,15,7]
输出:true

示例 2:

img

输入:root = [1,2,2,3,3,null,null,4,4]
输出:false

示例 3:

输入:root = []
输出:true

思路

这道题只要循环的去比较子树的高度就可以了,可以用递归法和迭代法做。

递归法

class Solution {
public:
    int depth(TreeNode* root)
    {
        if(!root)
        {
            return 0;
        }
        return max(depth(root->left), depth(root->right)) + 1;
    }
    bool isAvlTree(TreeNode* root)
    {
        if(!root)
        {
            return true;
        }
        int val = abs(depth(root->left) - depth(root->right));
        return  0 <= val && val < 2 && isAvlTree(root->left) && isAvlTree(root->right);
    }
    bool isBalanced(TreeNode* root) {
        if(!root)
        {
            return true;
        }
        return isAvlTree(root);
    }
};

迭代法

这道题用迭代法做要使用辅助栈和队列来计算深度和保存子节点。(也可以使用栈+后序遍历来计算深度。)

代码

class Solution {
public:
    int depth(TreeNode* root)
    {
        if(!root)
        {
            return 0;
        }
        queue<TreeNode*> nodeStack;
        nodeStack.push(root);
        int depth = 0;
        while(!nodeStack.empty())
        {
            int num = nodeStack.size();
            for(int i = 0; i < num; i++)
            {
                TreeNode* cur = nodeStack.front();
                nodeStack.pop();
                if(cur->left)
                    nodeStack.push(cur->left);
                if(cur->right)
                    nodeStack.push(cur->right);
            }
            depth++;
        }
        cout << endl;
        return depth;
    }
    bool isBalanced(TreeNode* root) {
        if(!root)
        {
            return true;
        }
        stack<TreeNode*> nodeStack;
        nodeStack.push(root);
        while(!nodeStack.empty())
        {
            int num = nodeStack.size();
            for(int i = 0; i < num; i++)
            {
                TreeNode* cur = nodeStack.top();
                nodeStack.pop();
                if(abs(depth(cur->left) - depth(cur->right)) > 1)
                {
                    return false;
                }
                if(cur->left)
                    nodeStack.push(cur->left);
                if(cur->right)
                    nodeStack.push(cur->right);
            }
        }
        return true;
    }
};

题目2 257. 二叉树的所有路径

给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。

叶子节点 是指没有子节点的节点。

示例 1:

img

输入:root = [1,2,3,null,5]
输出:["1->2->5","1->3"]

示例 2:

输入:root = [1]
输出:["1"]

提示:

  • 树中节点的数目在范围 [1, 100]
  • -100 <= Node.val <= 100

思路

递归法

使用递归法,每次将一个非空节点加入string对象中,当且仅当该节点无孩子节点时将string保存到vector对象中;当有孩子时递归调用孩子的处理函数。

代码

class Solution {
public:
    void getPath(TreeNode* root, vector<string> &vec, string str)
    {
        str += "->" + to_string(root->val);
        if(!root->left && !root->right)
        {
            vec.push_back(move(tmpstr));
            return;
        }
        if(root->left)
        {
           getPath(root->left, vec, str);
        }
        if(root->right)
        {
            getPath(root->right, vec, str);
        }
    }
    vector<string> binaryTreePaths(TreeNode* root) {
        if(!root->left && !root->right)
        {
            return vector<string>(1, to_string(root->val));
        }
        vector<string> result;
        if(root->left)
            getPath(root->left, result, to_string(root->val));
        if(root->right)
            getPath(root->right, result, to_string(root->val));
        return result;
    }
};

迭代法

mmp,写着写着头晕了,随想录里用两个辅助栈做起来确实简单。我这道题用的后序遍历以及一个辅助栈stack和一个辅助string来做的,除了根节点要单独处理外,要注意的是其他节点可能有两种可能,分别是叶子节点和非叶子节点。碰到叶子节点直接出栈并保存string到vector对象中;碰到非叶子节点有三种可能,分别是才入栈的节点,将左孩子加入栈的节点,将左右孩子都加入栈的节点。才入栈的节点由一个TreeNode*对象表示,左孩子入栈过的节点用一个TreeNode*和一个nullptr表示,左右孩子都处理过了用一个TreeNode*和两个nullptr表示。

代码

class Solution {
public:
    int length(int & value)
    {
        int length = 0;
        if(value < 0)
            length++;
        while(value)
        {
            length++;
            value /= 10;
        }
        return move(length);
    }
    vector<string> binaryTreePaths(TreeNode* root) {
        //根节点的双child节点为空则直接返回
        if(!root->left && !root->right)
        {
            return vector<string>(1, to_string(root->val));
        }
        vector<string> result;
        //根节点单独处理一下
        string str(to_string(root->val));
        stack<TreeNode*> nodeStack;
        if(root->right)
            nodeStack.push(root->right);
        if(root->left)
            nodeStack.push(root->left);
        int size;
        while(!nodeStack.empty())
        {
            TreeNode* curNode = nodeStack.top();
            //curNode为空则说明curNode的左child节点已经加过nodeStack并处理过了
            if(curNode == nullptr)
            {
                nodeStack.pop();
                curNode = nodeStack.top();
                //curNode还为空则说明curNode的右节点已经加过并且处理过了
                if(curNode == nullptr)
                {
                    //因为非叶子节点要压入三次栈,因此要出三次
                    nodeStack.pop();
                    size = length(nodeStack.top()->val);
                    nodeStack.pop();
                    str.erase(str.size() - (2 + size), 2 + size);
                }
                //未处理过的curNode
                else
                {
                    //压入两次nullptr表示该非叶子节点已经成功压入左孩子和右孩子
                    nodeStack.push(nullptr);
                    nodeStack.push(nullptr);
                    if(curNode->right)
                    {
                        nodeStack.push(curNode->right);
                    }                    
                }

            }
            else
            {
                str += "->" + to_string(curNode->val);
                //若节点没有双child则为叶子节点,添加到result中
                if(!curNode->left && !curNode->right)
                {
                    result.push_back(str);
                    size = length(curNode->val);
                    str.erase(str.size() - (2 + size), 2 + size);
                    nodeStack.pop();
                }
                //非叶子节点则继续压入nullptr用于判断该节点是否处理过
                else
                {
                    nodeStack.push(nullptr);
                }
                if(curNode->left)
                {
                    nodeStack.push(curNode->left);
                }
            }
        }
        return move(result);
    }
};

题目4 404. 左叶子之和

给定二叉树的根节点 root ,返回所有左叶子之和。

示例 1:

img

输入: root = [3,9,20,null,null,15,7] 
输出: 24 
解释: 在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24

示例 2:

输入: root = [1]
输出: 0

思路

递归

主要是每个节点是否为左节点这个状态要记录,记录好了就正常递归做就行了,终止边界是空节点或者叶子节点。

代码

class Solution {
public:
    int lftValueSum(TreeNode* root, bool isLeft)
    {
        if(root == nullptr)
        {
            return 0;
        }
        if(!root->left && !root->right )
        {
            return isLeft ? root->val : 0;
        }
        return lftValueSum(root->left, true) + lftValueSum(root->right, false);
    }
    int sumOfLeftLeaves(TreeNode* root) {
        return lftValueSum(root, false);
    }
};

迭代法

代码随想录里的迭代法是用parent节点来判断左孩子是否为左叶子节点,我使用的是左叶子节点直接判断的方法来解决这道题。在入栈时对左孩子入栈两次,则左孩子节点就被标记了,左叶子节点就多了个双child节点为nullptr的条件。知道了这些就好做了。

代码

class Solution {
public:
    int sumOfLeftLeaves(TreeNode* root) {
        int sum = 0;
        stack<TreeNode*> nodeStack;
        nodeStack.push(root);
        while(!nodeStack.empty())
        {
            TreeNode* curNode = nodeStack.top();
            nodeStack.pop();
            if(curNode == nullptr)
            {
   
                curNode = nodeStack.top();
                nodeStack.pop();
                if(!curNode->left && !curNode->right)
                {
                    sum += curNode->val;
                    continue;
                }
            }
            if(curNode->left)
            {
                nodeStack.push(curNode->left);
                nodeStack.push(nullptr);
            }
            if(curNode->right)
            {
                nodeStack.push(curNode->right);
            }
        }
        return sum;
    }
};

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

给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。

完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。

示例 1:

img

输入:root = [1,2,3,4,5,6]
输出:6

示例 2:

输入:root = []
输出:0

示例 3:

输入:root = [1]
输出:1

提示:

  • 树中节点的数目范围是[0, 5 * 104]
  • 0 <= Node.val <= 5 * 104
  • 题目数据保证输入的树是 完全二叉树

进阶:遍历树来统计节点是一种时间复杂度为 O(n) 的简单解决方案。你可以设计一个更快的算法吗?

思路

递归

前中后续递归都可以遍历所有的节点

代码

class Solution {
public:
    int countNodes(TreeNode* root) {
        if(root == nullptr)
        {
            return 0;
        }
        return 1 + countNodes(root->left) + countNodes(root->right);
    }
};

迭代法

用前中后层序都可以做

代码

class Solution {
public:
    int countNodes(TreeNode* root) {
        queue<TreeNode*> que;
        if (root != NULL) que.push(root);
        int result = 0;
        while (!que.empty()) {
            int size = que.size();
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                result++;   // 记录节点数量
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
        }
        return result;
    }
};

公式法(可递归可迭代)*

我硬套的公式,写的很糟糕。代码随想录里的公式法很有用,运用了分而治之的思想,每个完全二叉树可拆分为满二叉树+完全二叉树。

posted @ 2024-09-11 22:55  酱油黑龙  阅读(292)  评论(0)    收藏  举报