Day17 二叉树 LeetCode 110 257 404

28 阅读4分钟

110. 平衡二叉树

心得

  • 计算左右高度差然后求绝对值高度差用来判断是否平衡,判断逻辑写在了主函数,递归函数仅仅写获取高度,相当于只判断一次,忽略判断左右子树本身平衡二叉树的情况
  • 应牢记递归思路,判断始终是迭代递归下去的,而不是单层,要想好单层逻辑

题解

  • 对于深度,递归法,用前序遍历,只有从上往下,中左右才能逐步往下求深度
  • 高度后序遍历,只有左右中层层网上才能求高度,只有求得左右子树情况才能用来处理逻辑
  • 对于迭代,层序遍历无法求得左右子树高度,用栈来模拟后序遍历,但是处逻辑需要用单独函数(用栈来计算左右子树的高度,通过最大深度即为高度)然后对高度进行逻辑判断,最后中右左入栈出栈即可
  • 理论上递归都可以用迭代来求解,但是本题有点复杂,而且迭代存在重复计算,效率并不高,不推荐使用迭代
class Solution {
public:
    int getHeight(TreeNode* node) {
        if (node == nullptr) return 0;
        int leftHeight = getHeight(node->left);
        int rightHeight = getHeight(node->right);
        if (leftHeight == -1) return -1;
        if (rightHeight == -1) return -1;
        return abs(leftHeight - rightHeight) > 1 ? -1 : 1 + max(leftHeight, rightHeight);
    }
    bool isBalanced(TreeNode* root) {
        if (root == nullptr) return true;
        return getHeight(root) == -1 ? false : true;
    }
};

257. 二叉树的所有路径

心得

  • 看到题目想的是深度优先,但是想的是中序,但是这样无法得到路径,每种应该都尝试过一下即能想到大致思路
  • 返回结果看起来复杂,箭头连接,需要处理

题解

  • 考虑递归遍历,由于路径从上往下,而且需要回溯,保存位置,只能采用前序,先保存中,然后下去左右,然后回溯遍历右侧

  • 注意本次递归的三步走与之前写法有较大差异,

    • 首先终止条件区分之前的高度深度,他们传入叶子结点的下一个结点即空节点,终止判断空即可,但是本题只要叶子结点不用往下遍历,所以终止条件判断是够为叶子结点
    • 其次中序的处理过程放在首位,考虑的是终止条件处理需要加入的叶子结点,否则最后一个无法保存
    • 最后需要回溯才能找到上次的位置,而且一个迭代对应一次回溯,不可递归2次回溯一次
  • 精简版本隐藏了回溯细节,二刷来理解

    • 核心在于形参类型值拷贝string path而不是引用的地址拷贝string & path
    • 在每次path + “→”过程中,返回时,并不会导致原来path的改变,即完成了回溯
class Solution {
public:
    void traversal(TreeNode* node, vector<int>& path, vector<string>& result) {
        path.push_back(node->val); // 中, 写在这里因为下面的逻辑不加入叶子结点,结果需要最后的叶子结点所以提前保存
        if (node->left == nullptr && node->right == nullptr) { // 终止条件,没有当前结点node==nullptr判断,而是叶子结点判断,由于
                                                                // 其逻辑逻辑是加入叶子结点,下面控制不入非空
            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 (node->left) {
            traversal(node->left, path, result); // 左
            path.pop_back(); // 回溯
        }
        if (node->right) {
            traversal(node->right, path, result); // 右
            path.pop_back(); // 回溯
        }
    }
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> result;
        vector<int> path;
        if (root == nullptr) return result;
        traversal(root, path, result);
        return result;
    }
};

// 递归部分可精简
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); // 左
        if (cur->right) traversal(cur->right, path + "->", result); // 右
}

404. 左叶子之和

心得

  • 理解题目左叶子的定义,首先是叶子结点,其次是左,想的是要到回溯父节点,然后判断,但是一想回溯就感觉很麻烦,就没深入考虑,其实思路类似的,就是用父节点来判断,但是不用回溯,直接遍历过程中的终止条件上移一层即可

题解

  • 明确叶子结点定义,利用父结点来判断其是不是左叶子结点,区别之前二叉树每次通过孩子结点判断终止条件做法
  • 递归采用前中后序都可以,后序便于理解,迭代法判断条件一致
// 递归——后序
class Solution {
public:
    int sumOfLeftLeaves(TreeNode* root) {
        if (root == nullptr) return 0;
        // 当前为叶子结点,必然其左叶子为0,其实不写也行,多一层递归
        if (root->left == nullptr && root->right == nullptr) return 0; 
        int leftSum = sumOfLeftLeaves(root->left); // 左
				// 这里位置其实在哪都可以,下面的leftsum只是借来的,如果在该变量前使用则新建个变量接收即可
        if (root->left && !root->left->left && !root->left->right) { 
            leftSum = root->left->val;
        }
        int rightSum = sumOfLeftLeaves(root->right); // 右
        return leftSum + rightSum; // 中
    }
};
// 精简
class Solution {
public:
    int sumOfLeftLeaves(TreeNode* root) {
        if (root == NULL) return 0;
        int leftValue = 0;
        if (root->left != NULL && root->left->left == NULL && root->left->right == NULL) {
            leftValue = root->left->val;
        }
        return leftValue + sumOfLeftLeaves(root->left) + sumOfLeftLeaves(root->right);
    }
};
// 迭代法,
class Solution {
public:
    int sumOfLeftLeaves(TreeNode* root) {
        if (root == nullptr) return 0;
        stack<TreeNode*> st;
        if (root != nullptr) st.push(root);
        int sum = 0;
        while (!st.empty()) {
            TreeNode* node = st.top();
            st.pop();
            if (node->left && !node->left->left && !node->left->right) {
                sum += node->left->val;
            }
           if (node->left) st.push(node->left);
           if (node->right) st.push(node->right);
        }
        return sum;
    }
};