第六章 二叉树 part02

翻转二叉树

题目描述

给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

image

解题思路

递归法

通过递归地交换每个节点的左右子树,从而实现整个二叉树的反转。从根节点开始,先交换其左右子树,然后分别递归地处理交换后的左右子树,直到处理完所有节点。

迭代法

深度优先遍历

使用栈来非递归地反转二叉树。从根节点开始,将节点压入栈中,然后循环从栈中取出节点,交换其左右子树,并将非空的左右子按序节点压回栈中,直至处理完所有节点。

统一写法

通过在栈中使用空指针作为标记,区分已入栈但尚未处理的节点和已处理过的节点。当遇到空指针标记时,表示栈顶的节点需要进行左右子树的交换。

层序遍历

通过广度优先搜索(BFS)的方式,逐层处理树的节点。在每一层中,先交换节点的左右子树,然后将非空的左右子节点加入队列,以便处理下一层。

代码实现

测试地址:https://leetcode.cn/problems/invert-binary-tree/description/

递归写法:

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
		//递归终止条件
        if (root == nullptr)
            return root;
		//交换当前节点的左右子树
        swap(root->left, root->right);
		//递归反转左子树
        invertTree(root->left);
		//递归反转右子树
        invertTree(root->right);
		//返回反转后的根节点
        return root;
    }
};

栈写法:

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        stack<TreeNode*> st;
        // 如果根节点为空,直接返回空指针
        if (root == nullptr)
            return root;
        // 将根节点压入栈中
        st.push(root);
        // 当栈不为空时,循环执行以下操作
        while (!st.empty()) {
            // 弹出栈顶节点
            TreeNode* node = st.top();
            st.pop();
            // 交换当前节点的左右子树
            swap(node->left, node->right);
            // 如果当前节点的右子树存在,将其压入栈中
            if (node->right)
                st.push(node->right);
            // 如果当前节点的左子树存在,将其压入栈中
            if (node->left)
                st.push(node->left);
        }
        // 返回反转后的根节点
        return root;
    }
};

统一迭代写法:

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        // 如果根节点为空,直接返回空指针
        if (root == nullptr)
            return root;
        // 创建一个栈用于辅助遍历
        stack<TreeNode*> st;
        // 将根节点压入栈中
        st.push(root);
        // 当栈不为空时,循环执行以下操作
        while (!st.empty()) {
            // 取出栈顶元素
            TreeNode* node = st.top();
            // 如果栈顶元素不为空
            if (node != NULL) {
                // 弹出栈顶元素
                st.pop();
                // 如果右子节点存在,将其压入栈中
                if (node->right)
                    st.push(node->right);
                // 如果左子节点存在,将其压入栈中
                if (node->left)
                    st.push(node->left);
                // 再次将当前节点压入栈中,标记为待处理节点
                st.push(node);
                // 压入一个空指针,作为待处理节点的标记
                st.push(NULL);
            } else {
                // 弹出空指针标记
                st.pop();
                // 取出下一个待处理节点
                node = st.top();
                st.pop();
                // 交换当前节点的左右子树
                swap(node->left, node->right);
            }
        }
        // 返回反转后的根节点
        return root;
    }
};

层序遍历写法:

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        // 如果根节点为空,直接返回空指针
        if (root == nullptr)
            return root;
        // 创建一个队列用于辅助遍历
        queue<TreeNode*> que;
        // 将根节点加入队列
        que.push(root);
        // 当队列不为空时,循环执行以下操作
        while (!que.empty()) {
            // 获取当前队列的大小,即当前层的节点数
            int size = que.size();
            // 遍历当前层的所有节点
            for (int i = 0; i < size; i++) {
                // 取出队列的头部节点
                TreeNode* node = que.front();
                que.pop();
                // 交换当前节点的左右子树
                swap(node->left, node->right);
                // 如果左子节点存在,将其加入队列
                if (node->left)
                    que.push(node->left);
                // 如果右子节点存在,将其加入队列
                if (node->right)
                    que.push(node->right);
            }
        }
        // 返回反转后的根节点
        return root;
    }
};

对称二叉树

题目描述

给你一个二叉树的根节点 root , 检查它是否轴对称。

image

解题思路

递归法

检查两个子树的节点值是否相等,然后递归地检查左子树的左子树和右子树的右子树,以及左子树的右子树和右子树的左子树是否相等。如果所有这些比较都返回true,则树是对称的。

迭代法

使用队列进行层次遍历,每次从队列中取出两个节点进行比较,如果它们的值相等且结构对称,则继续比较下一对节点。如果发现任何不对称的情况,立即返回false。如果所有节点都比较完毕,且没有发现不对称的情况,则返回true,表示树是对称的。

使用栈的写法和队列方式核心思路一致,代码可看下方代码实现。

代码实现

测试地址:https://leetcode.cn/problems/symmetric-tree/description/

递归写法:

class Solution {
public:
    bool compareVal(TreeNode* leftNode, TreeNode* rightNode) {
        // 如果左节点为空而右节点不为空,则不对称
        if (leftNode == nullptr && rightNode != nullptr) {
            return false;
        }
        // 如果左节点不为空而右节点为空,则不对称
        else if (leftNode != nullptr && rightNode == nullptr) {
            return false;
        }
        // 如果左右节点都为空,则对称
        else if (leftNode == nullptr && rightNode == nullptr) {
            return true;
        }
        // 如果左右节点的值不相等,则不对称
        else if (leftNode->val != rightNode->val) {
            return false;
        }
        // 递归比较左节点的左子树和右节点的右子树,以及左节点的右子树和右节点的左子树
        else {
            return compareVal(leftNode->left, rightNode->right) &&
                   compareVal(leftNode->right, rightNode->left);
        }
    }

    bool isSymmetric(TreeNode* root) {
        // 如果根节点为空,则树对称
        if (root == nullptr)
            return true;
        // 调用辅助函数比较根节点的左右子树是否对称
        return compareVal(root->left, root->right);
    }
};

迭代写法(队列):

class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        // 如果根节点为空,则树对称
        if (root == nullptr)
            return true;
        // 使用队列来辅助进行层次遍历
        queue<TreeNode*> que;
        // 将根节点的左右子节点加入队列
        que.push(root->left);
        que.push(root->right);
        // 当队列不为空时,继续检查
        while (!que.empty()) {
            // 从队列中取出两个节点进行比较
            TreeNode* leftNode = que.front();
            que.pop();
            TreeNode* rightNode = que.front();
            que.pop();
            // 如果两个节点都为空,则继续下一轮比较
            if (!leftNode && !rightNode) {
                continue;
            }
            // 如果两个节点有一个为空或者值不相等,则树不对称
            if ((!leftNode || !rightNode ||
                 (leftNode->val != rightNode->val))) {
                return false;
            }
            // 将左节点的左子节点和右节点的右子节点,以及左节点的右子节点和右节点的左子节点成对加入队列
            que.push(leftNode->left);
            que.push(rightNode->right);
            que.push(leftNode->right);
            que.push(rightNode->left);
        }
        // 如果所有节点都比较完毕,且没有发现不对称的情况,则树对称
        return true;
    }
};

迭代写法(栈):

class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        // 如果根节点为空,则树对称
        if (root == nullptr)
            return true;
        // 使用栈来辅助进行迭代遍历
        stack<TreeNode*> st;
        // 将根节点的左右子节点加入栈
        st.push(root->left);
        st.push(root->right);
        // 当栈不为空时,继续检查
        while (!st.empty()) {
            // 从栈中取出两个节点进行比较
            TreeNode* leftNode = st.top();
            st.pop();
            TreeNode* rightNode = st.top();
            st.pop();
            // 如果两个节点都为空,则继续下一轮比较
            if (!leftNode && !rightNode) {
                continue;
            }
            // 如果两个节点有一个为空或者值不相等,则树不对称
            if ((!leftNode || !rightNode ||
                 (leftNode->val != rightNode->val))) {
                return false;
            }
            // 将左节点的左子节点和右节点的右子节点,以及左节点的右子节点和右节点的左子节点成对加入栈
            st.push(leftNode->left);
            st.push(rightNode->right);
            st.push(leftNode->right);
            st.push(rightNode->left);
        }
        // 如果所有节点都比较完毕,且没有发现不对称的情况,则树对称
        return true;
    }

二叉树的最大深度

题目描述

给定一个二叉树 root ,返回其最大深度。

二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。

image

解题思路

递归后序遍历思路

二叉树的节点深度&高度说明:

  • 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
  • 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)

二叉树的节点深度实际上等同于二叉树的根节点到叶子结点的距离,也就是根节点所在的高度。

因此可以可以使用后序遍历的方式,从下往上递归地计算每个节点的左子树和右子树的最大高度,然后取两者中的最大值并加1(当前节点的高度),从而得到整个二叉树的最大高度,进而得到二叉树的最大深度。

递归前序遍历思路

从根节点开始,自顶向下递归到子节点时增加深度计数,并在返回时减少深度计数(回溯)。在每个节点,它比较当前深度和已知的最大深度,更新最大深度。

迭代法思路

使用广度优先搜索(BFS)来计算二叉树的最大深度。通过队列实现层次遍历,每次处理一层的节点,并在处理完一层后增加深度计数。

代码实现

测试地址:https://leetcode.cn/problems/maximum-depth-of-binary-tree/

递归写法(后序遍历正常版本):

class Solution {
public:
    int maxDepth(TreeNode* cur) {
        // 如果当前节点为空,表示到达叶子节点下方,返回0
        if (cur == nullptr) {
            return 0;
        } else {
            // 递归计算左子树的最大深度(左)
            int leftHeight = maxDepth(cur->left);
            // 递归计算右子树的最大深度(右)
            int rightHeight = maxDepth(cur->right);
            // 取左右子树深度的最大值,并加上当前节点(高度为1),得到当前树的最大深度(中)
            int maxHeight = 1 + max(leftHeight, rightHeight);
            // 返回计算出的最大深度
            return maxHeight;
        }
    }
};

递归写法(后序遍历极简版本):

class Solution {
public:
    int maxDepth(TreeNode* root) {
        return root == nullptr
                   ? 0
                   : max(maxDepth(root->left), maxDepth(root->right)) + 1;
    }
};

递归写法(前序遍历正常写法):

class Solution {
public:
    // 用于存储最大深度的变量
    int result;

    // 递归函数,用于计算每个节点的深度
    void getdepth(TreeNode* node, int depth) {
        // 更新最大深度(中)
        result = depth > result ? depth : result;
        // 如果当前节点是叶子节点,则返回(左)
        if (node->left == nullptr && node->right == nullptr)
            return;
        // 如果左子节点存在,增加深度并递归计算左子树的深度(右)
        if (node->left) {
            depth++;
            getdepth(node->left, depth);
            depth--; // 回溯,减少深度
        }
        // 如果右子节点存在,增加深度并递归计算右子树的深度
        if (node->right) {
            depth++;
            getdepth(node->right, depth);
            depth--; // 回溯,减少深度
        }
        return;
    }

    // 主函数,计算二叉树的最大深度
    int maxDepth(TreeNode* root) {
        result = 0; // 初始化最大深度为0
        if (root == nullptr)
            return result; // 如果树为空,返回0
        getdepth(root, 1); // 从根节点开始计算深度
        return result; // 返回最大深度
    }
};

递归写法(前序遍历极简写法):

class Solution {
public:
    int result;
    void getdepth(TreeNode* node, int depth) {
        result = depth > result ? depth : result;
        if (node->left == nullptr && node->right == nullptr)
            return;
        if (node->left) {
            getdepth(node->left, depth + 1);
        }
        if (node->right) {
            getdepth(node->right, depth + 1);
        }
        return;
    }

    int maxDepth(TreeNode* root) {
        result = 0;
        if (root == nullptr)
            return result;
        getdepth(root, 1);
        return result;
    }
};

迭代法写法:

class Solution {
public:    int maxDepth(TreeNode* root) {
        // 如果根节点为空,返回0
        if (root == nullptr)
            return 0;
        int depth = 0; // 用于记录当前深度
        queue<TreeNode*> que; // 使用队列进行层次遍历
        que.push(root); // 将根节点加入队列
        // 当队列不为空时,继续遍历
        while (!que.empty()) {
            int size = que.size(); // 当前层的节点数
            depth++; // 增加深度
            // 遍历当前层的所有节点
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front(); // 取出队列头部的节点
                que.pop(); // 移除队列头部的节点
                // 如果左子节点存在,将其加入队列
                if (node->left)
                    que.push(node->left);
                // 如果右子节点存在,将其加入队列
                if (node->right)
                    que.push(node->right);
            }
        }
        return depth; // 返回最大深度
    }
};

二叉树的最小深度

题目描述

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

说明: 叶子节点是指没有子节点的节点。

image

解题思路

递归后序遍历思路

递归地计算每个节点的左子树和右子树的最小高度,如果一个节点只有单边子树(左或右),则其最小深度应为该子树的最小高度加1。

递归先序遍历思路

从根节点开始,每次递归到子节点时增加深度计数。在每个节点,如果它是叶子节点,则比较当前深度和已知的最大深度,更新最小深度。

迭代法思路

使用BFS来计算二叉树的最小深度,通过队列实现层次遍历,每次处理一层的节点,并在处理过程中检查是否到达了叶子节点。一旦找到第一个叶子节点,即返回当前的深度作为最小深度

代码实现

测试地址

递归后续遍历写法:

class Solution {
public:
    int minDepth(TreeNode* root) {
        // 如果根节点为空,返回0
        if (root == nullptr)
            return 0;
        // 如果左子节点为空,但右子节点存在,则最小深度为右子树的最小高度加1
        if (root->left == nullptr && root->right) {
            return 1 + minDepth(root->right);
        }
        // 如果右子节点为空,但左子节点存在,则最小深度为左子树的最小高度加1
        if (root->left && root->right == nullptr) {
            return 1 + minDepth(root->left);
        }
        // 如果左右子节点都存在,则最小深度为左右子树最小高度的最小值加1
        return 1 + min(minDepth(root->left), minDepth(root->right));
    }
};

递归前序遍历写法:

class Solution {
public:
    // 用于存储最小深度的变量
    int result;

    // 递归函数,用于计算每个节点的深度
    void getdepth(TreeNode* node, int depth) {
        // 如果节点为空,返回
        if (node == nullptr)
            return;
        // 如果当前节点是叶子节点,更新最小深度
        if (node->left == nullptr && node->right == nullptr) {
            result = min(result, depth);
        }
        // 如果左子节点存在,递归计算左子树的深度
        if (node->left) {
            getdepth(node->left, depth + 1);
        }
        // 如果右子节点存在,递归计算右子树的深度
        if (node->right) {
            getdepth(node->right, depth + 1);
        }
        return;
    }

    // 主函数,计算二叉树的最小深度
    int minDepth(TreeNode* root) {
        // 如果根节点为空,返回0
        if (root == nullptr)
            return 0;
        result = INT_MAX; // 初始化最小深度为最大整数值
        getdepth(root, 1); // 从根节点开始计算深度
        return result; // 返回最小深度
    }
};

迭代法:

class Solution {
public:
    int minDepth(TreeNode* root) {
        // 如果根节点为空,返回0
        if (root == NULL) return 0;
        int depth = 0; // 用于记录当前深度
        queue<TreeNode*> que; // 使用队列进行层次遍历
        que.push(root); // 将根节点加入队列
        // 当队列不为空时,继续遍历
        while(!que.empty()) {
            int size = que.size(); // 当前层的节点数
            depth++; // 增加深度
            // 遍历当前层的所有节点
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front(); // 取出队列头部的节点
                que.pop(); // 移除队列头部的节点
                // 如果左子节点存在,将其加入队列
                if (node->left) que.push(node->left);
                // 如果右子节点存在,将其加入队列
                if (node->right) que.push(node->right);
                // 如果当前节点是叶子节点(左右孩子都为空),则返回当前深度
                if (!node->left && !node->right) {
                    return depth;
                }
            }
        }
        return depth; // 返回最小深度
    }
};

  • 7
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值