Leetcode算法训练日记 | day17

一、平衡二叉树

1.题目

Leetcode:第 110 题

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

示例 1:

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

示例 2:

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

示例 3:

输入:root = []
输出:true

2.解题思路

使用递归法和迭代法遍历二叉树,对于每个节点,计算其左右子树的深度差。如果深度差大于1,则树不平衡,返回false。如果遍历结束,没有发现深度差大于1的情况,则树是平衡的,返回true。

3.实现代码

#include <iostream>
#include <vector>
#include <queue>
#include <stack>
using namespace std;

// 定义一个结构体TreeNode,用于表示二叉树的节点。
struct TreeNode {
    int val; // 存储节点的值。
    TreeNode* left; // 指向该节点左子树的指针。
    TreeNode* right; // 指向该节点右子树的指针。
    // TreeNode的构造函数,用于创建一个TreeNode实例。
    // 参数x是节点的值,left和right默认为NULL,表示没有左右子节点。
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

// 一、判断平衡二叉树(递归法)
class Solution1 {
public:
    // 定义名为getHeight的辅助函数,用于计算二叉树节点的高度。
    // 同时,该函数会检查子树的高度差是否不超过1,以此判断树是否平衡。
    // 函数接受一个参数:指向二叉树节点的指针node。
    int getHeight(TreeNode* node) {
        if (node == NULL) return 0; // 如果传入的节点为空,说明是一棵空树,返回高度0。

        int leftHeiht = getHeight(node->left);  // 递归地计算左子树节点的高度。
        if (leftHeiht == -1) return -1; // 如果左子树高度计算结果为-1,说明左子树不平衡,直接返回-1。

        int rightHeiht = getHeight(node->right);// 递归地计算右子树节点的高度。
        if (rightHeiht == -1) return -1;// 如果右子树高度计算结果为-1,说明右子树不平衡,直接返回-1。

        
        int heightDiff = abs(leftHeiht - rightHeiht);// 计算左右子树的高度差。
        if (heightDiff > 1) return -1;// 如果高度差大于1,说明当前节点的子树不平衡,返回-1。
        // 如果子树平衡,返回当前节点的高度,为左右子树的最大高度加1(当前节点本身的高度)。
        return 1 + max(leftHeiht, rightHeiht);
    }
    // 定义名为isBalanced的成员函数,用于判断二叉树是否平衡。
    // 函数接受一个参数:指向二叉树根节点的指针root。
    bool isBalanced(TreeNode* root) {
        // 调用getHeight函数计算根节点的高度,如果返回值为-1,说明树不平衡,返回false。
        // 否则,返回true,表示树是平衡的。
        return getHeight(root) == -1 ? false : true;
    }
};


// 二、判断平衡二叉树(迭代法)
class Solution2 {
public:
    // getDepth函数用于计算二叉树的深度。
    // 函数接受一个参数:指向二叉树节点的指针cur。
    int getDepth(TreeNode* cur) {
        stack<TreeNode*> st; // 创建一个栈st,用于存储遍历过程中的节点。
        if (cur != NULL) st.push(cur); // 如果当前节点cur不为空,则将其压入栈中。
        int depth = 0; // 初始化深度计数器为0。
        int result = 0;  // 初始化最大深度计数器为0。

        while (!st.empty()) {// 使用while循环遍历栈不为空时的所有节点。
            TreeNode* node = st.top(); // 获取栈顶节点。
            if (node != NULL) {// 如果节点不为空,则执行以下操作:
                st.pop(); // 将当前节点从栈中弹出。
                st.push(node); // 将当前节点压入栈中,以便后续处理。
                st.push(NULL);// 将一个空指针压入栈中,作为左右子树遍历的分隔符。
                depth++;// 深度计数器加1。

                if (node->right) st.push(node->right); // 如果节点有右子节点,将其压入栈中。
                if (node->left) st.push(node->left);// 如果节点有左子节点,将其压入栈中。
            }

            else { // 如果节点为空,说明已经到达了叶子节点的下方,执行以下操作:
                st.pop();// 弹出空指针
                node = st.top();//获取节点
                st.pop();//弹出节点
                depth--;// 节点计数器减1。
            }
            // 更新最大深度计数器,取当前最大值。
            result = result > depth ? result : depth;
        }
        return result;// 返回最大深度。
    }

    // isBalanced函数用于判断二叉树是否平衡。
    // 函数接受一个参数:指向二叉树根节点的指针root。
    bool isBalanced(TreeNode* root) {
        stack<TreeNode*> st;   // 创建一个栈st,用于存储遍历过程中的节点。
        if (root == NULL) return true; // 如果根节点root为空,说明是一棵空树,返回true。
        st.push(root); // 将根节点压入栈中。

        while (!st.empty()) { // 使用while循环遍历栈不为空时的所有节点。
            TreeNode* node = st.top();  // 获取栈顶节点
            st.pop();// 弹出当前节点。
            // 计算当前节点的左右子树深度差。
            int depthDiff = abs(getDepth(node->left) - getDepth(node->right));
            if (depthDiff > 1) { // 如果左右子树深度差大于1,说明树不平衡,返回false。
                return false;
            }
            if (node->left) st.push(node->left); // 如果栈顶节点有左子节点,将其压入栈中。
            if (node->right) st.push(node->right);// 如果栈顶节点有右子节点,将其压入栈中。
        }
        return true; // 如果遍历结束,说明树是平衡的,返回true。
    }
};

//测试
// 辅助函数,用于创建一个新的TreeNode
TreeNode* createNode(int value) {
    return new TreeNode(value);
}

// 辅助函数,用于构建二叉树
TreeNode* buildTree(vector<int>& values) {
    if (values.empty()) return NULL;
    TreeNode* root = createNode(values[0]);
    queue<TreeNode*> queueNode;
    queueNode.push(root);
    int i = 1;
    while (!queueNode.empty()) {
        TreeNode* node = queueNode.front();
        queueNode.pop();
        if (i < values.size()) {
            node->left = createNode(values[i++]);
            queueNode.push(node->left);
        }
        if (i < values.size()) {
            node->right = createNode(values[i++]);
            queueNode.push(node->right);
        }
    }
    return root;
}

// 打印容器中的所有元素,用于验证测试结果
void printVector(const vector<int>& vec) {
    for (int value : vec) {
        cout << value << " ";
    }
    cout << endl;
}

// 主函数
int main() {

    vector<int> treeValues = { 1, 2, 3, 4, 5, 6, 7 };// 定义二叉树的层序遍历结果,用于构建二叉树
    TreeNode* root = buildTree(treeValues); // 构建二叉树
    Solution1 s1;// 创建Solution类的实例
    Solution2 s2;
    int result1 = s1.isBalanced(root);// 传入二叉树的根节点
    int result2 = s2.isBalanced(root);
    cout << "判断平衡二叉树(递归法)结果是: " << result1 << endl;
    cout << endl;
    cout << "判断平衡二叉树(迭代法)结果是:" << result2 << endl;
    cout << endl;
    return 0;
}

二、二叉树的所有路径

1.题目

Leetcode:第 257 题

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

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

示例 1:

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

示例 2:

输入:root = [1]
输出:["1"]
2.解题思路

使用递归法和迭代法遍历二叉树所有节点,记录遍历的路径。

3.实现代码
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <string>
using namespace std;

// 定义一个结构体TreeNode,用于表示二叉树的节点。
struct TreeNode {
    int val; // 存储节点的值。
    TreeNode* left; // 指向该节点左子树的指针。
    TreeNode* right; // 指向该节点右子树的指针。
    // TreeNode的构造函数,用于创建一个TreeNode实例。
    // 参数x是节点的值,left和right默认为NULL,表示没有左右子节点。
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

// 一、二叉树的所有路径(递归法)
class Solution1 {
public:
    // traversal函数用于递归遍历二叉树并收集所有从根到叶子节点的路径。
    // cur是当前遍历到的节点,path是用于记录当前路径的容器,result是存储所有路径的容器。
    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();// 同样,在回溯时从路径中移除当前节点。
        }
    }

    // binaryTreePaths函数用于返回二叉树的所有根到叶子节点的路径。
    // root是二叉树的根节点。
    vector<string> binaryTreePaths(TreeNode* root) {
        
        vector<string> result;// 初始化一个空的字符串容器,用于存储所有路径的字符串表示。
        vector<int> path;// 初始化一个空的整数容器,用于记录当前路径。
        if (root == NULL) return result;// 如果根节点为空,则直接返回空的路径列表。
        traversal(root, path, result);// 调用traversal函数开始递归遍历,传入根节点、路径容器和结果容器。
        return result;// 返回包含所有路径的字符串容器。
    }
};

// 一、二叉树的所有路径(迭代法)
class Solution2 {
public:
    // binaryTreePaths函数用于返回二叉树的所有路径。
    // root是二叉树的根节点。
    vector<string> binaryTreePaths(TreeNode* root) {
        stack<TreeNode*> treeSt; // 创建一个栈treeSt,用于保存树的遍历节点。
        stack<string> pathSt;// 创建一个栈pathSt,用于保存遍历路径的节点。
        vector<string> result;// 创建一个字符串向量result,用于保存最终路径集合。
        if (root == NULL) return result;// 如果根节点为空,直接返回空的路径列表。
        treeSt.push(root);// 将根节点入treeSt栈,同时将根节点的值入pathSt栈。
        pathSt.push(to_string(root->val));
        
        while (!treeSt.empty()) {// 使用while循环遍历栈不为空时的所有节点。
            // 取出treeSt栈顶的节点,
            TreeNode* node = treeSt.top();
            treeSt.pop(); 

            // 取出pathSt栈顶的路径字符串。
            string path = pathSt.top(); 
            pathSt.pop();

            // 如果当前节点是叶子节点,将路径字符串添加到结果列表中。
            if (node->left == NULL && node->right == NULL) {
                result.push_back(path);
            }

            // 如果当前节点有右子节点,将其入treeSt栈,并更新对应的路径。
            if (node->right) {
                treeSt.push(node->right);
                pathSt.push(path + "->" + to_string(node->right->val));
            }

            // 如果当前节点有左子节点,将其入treeSt栈,并更新对应的路径。
            if (node->left) {
                treeSt.push(node->left);
                pathSt.push(path + "->" + to_string(node->left->val));
            }
        }
        // 返回包含所有路径的字符串向量。
        return result;
    }
};

//测试
// 辅助函数,用于创建一个新的TreeNode
TreeNode* createNode(int value) {
    return new TreeNode(value);
}

// 辅助函数,用于构建二叉树
TreeNode* buildTree(vector<int>& values) {
    if (values.empty()) return NULL;
    TreeNode* root = createNode(values[0]);
    queue<TreeNode*> queueNode;
    queueNode.push(root);
    int i = 1;
    while (!queueNode.empty()) {
        TreeNode* node = queueNode.front();
        queueNode.pop();
        if (i < values.size()) {
            node->left = createNode(values[i++]);
            queueNode.push(node->left);
        }
        if (i < values.size()) {
            node->right = createNode(values[i++]);
            queueNode.push(node->right);
        }
    }
    return root;
}

// 打印容器中的所有元素,用于验证测试结果
void printVector(const vector<int>& vec) {
    for (int value : vec) {
        cout << value << " ";
    }
    cout << endl;
}

// 主函数
int main() {

    vector<int> treeValues = { 1, 2, 3, 4, 5, 6, 7 };// 定义二叉树的层序遍历结果,用于构建二叉树
    TreeNode* root = buildTree(treeValues); // 构建二叉树
    Solution1 s1;// 创建Solution类的实例
    Solution2 s2;
    vector<string>result1 = s1.binaryTreePaths(root);// 传入二叉树的根节点
    vector<string>result2 =s2.binaryTreePaths(root);
    cout << "二叉树所有左子叶之和(递归法)是: " <<endl;
    for (auto & s : result1) {
        cout << s << endl;
    }
    cout << endl;
    cout << "二叉树所有左子叶之和(迭代法)是: " <<endl;
    for (auto& s : result1) {
        cout << s << endl;
    }
    cout << endl;
    return 0;
}

三、二叉树所有左子叶之和

1.题目

Leetcode:第 404 题

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

示例 1:

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

示例 2:

输入: root = [1]
输出: 0
2.解题思路

使用递归法和迭代法遍历二叉树所有节点,找到所有左子叶并求和。

3.实现代码
#include <iostream>
#include <vector>
#include <stack>
#include <queue>
using namespace std;

// 定义一个结构体TreeNode,用于表示二叉树的节点。
struct TreeNode {
    int val; // 存储节点的值。
    TreeNode* left; // 指向该节点左子树的指针。
    TreeNode* right; // 指向该节点右子树的指针。
    // TreeNode的构造函数,用于创建一个TreeNode实例。
    // 参数x是节点的值,left和right默认为NULL,表示没有左右子节点。
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

// 一、计算二叉树所有左子叶之和(递归法)
class Solution1 {
public:
    // sumOfLeftLeaves函数用于计算二叉树中所有左叶子节点的值的和。
    // 函数接受一个参数:指向二叉树根节点的指针root。
    int sumOfLeftLeaves(TreeNode* root) {
       
        if (root == NULL) return 0; // 如果根节点为空,返回0,因为没有任何叶子节点。

        // 如果根节点的左右子节点都为空,说明根节点是叶子节点,但不是左叶子节点,返回0。
        if (root->left == NULL && root->right == NULL) return 0;

        // 递归地计算左子树中所有左叶子节点的值的和。
        int leftValue = sumOfLeftLeaves(root->left);

        // 检查当前节点的左子节点是否是叶子节点(没有左右子节点)。
        // 如果是,更新leftValue为当前节点左子节点的值。
        if (root->left && !root->left->left && !root->left->right) {
            leftValue = root->left->val;
        }

        int rightValue = sumOfLeftLeaves(root->right);// 递归地计算右子树中所有左叶子节点的值的和。
        int sum = leftValue + rightValue; // 计算左右子树中左叶子节点的值的和。
        return sum;// 返回计算得到的和。
    }
};

// 二、计算二叉树所有左子叶之和(迭代法)
class Solution2 {
public:
    // sumOfLeftLeaves函数用于计算二叉树中所有左叶子节点的值的和。
    // 函数接受一个参数:指向二叉树根节点的指针root。
    int sumOfLeftLeaves(TreeNode* root) {
        stack<TreeNode*> st;// 创建一个栈st,用于在遍历过程中存储节点。
        if (root == NULL) return 0;// 如果根节点为空,直接返回0,因为没有任何叶子节点。
        st.push(root);// 将根节点入栈。
        int result = 0; // 初始化结果变量为0。
        
        while (!st.empty()) {// 使用while循环遍历栈不为空时的所有节点。
            TreeNode* node = st.top(); // 取出栈顶的节点。
            st.pop();// 弹出栈顶的节点。

            // 如果当前节点的左子节点存在,并且左子节点没有左右子节点,即左子节点是叶子节点,
            // 则将左子节点的值加到结果中。
            if (node->left != NULL && node->left->left == NULL && node->left->right == NULL) {
                result += node->left->val;
            }

            if (node->left) st.push(node->left); // 如果当前节点有左子节点,将其入栈。
            if (node->right) st.push(node->right);// 如果当前节点有右子节点,将其入栈。
        }
        // 返回计算得到的左叶子节点的值的和。
        return result;
    }
};


//测试
// 辅助函数,用于创建一个新的TreeNode
TreeNode* createNode(int value) {
    return new TreeNode(value);
}

// 辅助函数,用于构建二叉树
TreeNode* buildTree(vector<int>& values) {
    if (values.empty()) return NULL;
    TreeNode* root = createNode(values[0]);
    queue<TreeNode*> queueNode;
    queueNode.push(root);
    int i = 1;
    while (!queueNode.empty()) {
        TreeNode* node = queueNode.front();
        queueNode.pop();
        if (i < values.size()) {
            node->left = createNode(values[i++]);
            queueNode.push(node->left);
        }
        if (i < values.size()) {
            node->right = createNode(values[i++]);
            queueNode.push(node->right);
        }
    }
    return root;
}

// 打印容器中的所有元素,用于验证测试结果
void printVector(const vector<int>& vec) {
    for (int value : vec) {
        cout << value << " ";
    }
    cout << endl;
}

// 主函数
int main() {

    vector<int> treeValues = { 1, 2, 3, 4, 5, 6, 7 };// 定义二叉树的层序遍历结果,用于构建二叉树
    TreeNode* root = buildTree(treeValues); // 构建二叉树
    Solution1 s1;// 创建Solution类的实例
    Solution2 s2;
    int result1 = s1.sumOfLeftLeaves(root);// 传入二叉树的根节点
    int result2 = s2.sumOfLeftLeaves(root);
    cout << "二叉树所有左子叶之和(递归法)是: " << result1 << endl;
    cout << endl;
    cout << "二叉树所有左子叶之和(迭代法)是: " << result2 << endl;
    cout << endl;
    return 0;
}

ps:以上皆是本人在探索算法旅途中的浅薄见解,诚挚地希望得到各位的宝贵意见与悉心指导,若有不足或谬误之处,还请多多指教。 

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值