Day18 二叉树 LeetCode 513 112 113 106 105

48 阅读5分钟

513. 找树左下角的值

心得

  • 考虑层序求第一个即可,理解最深,最左

题解

  • 迭代最好理解

  • 对于递归法,

    • 本题注意理解最深最左,最深要求深度最大,最左保证遍历顺序先左后右即,所以前中后序都可以,左右顺序都是一致的
    • 对于中的逻辑本题没有的,所以三种遍历顺序均可,然后保证先左后右即可
    • 同时需要回溯,往下过程中需要回去再往下,所以需要回溯,精简版本通过值传递隐藏了回溯过程,实际是存在的
// 层序
class Solution {
public:
    int findBottomLeftValue(TreeNode* root) {
        queue<TreeNode*> que;
        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();
                if (i == 0) result = node->val;
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
        }
        return result;
    }
};

// 递归——前序
class Solution {
public:
    int maxDepth = INT_MIN;
    int result = 0;
    void traversal(TreeNode* cur, int depth) {
        if (cur == nullptr) return ;
        if (cur->left == nullptr && cur->right == nullptr) {
            if (depth > maxDepth) {
                maxDepth = depth;
                result = cur->val;
            }
        }
        if (cur->left) {
            depth++;
            traversal(cur->left, depth);
            depth--; // 回溯
        }
        if (cur->right) {
            depth++;
            traversal(cur->right, depth);
            depth--; // 回溯
        }
        return;
    }

    int findBottomLeftValue(TreeNode* root) {
        traversal(root, 0);
        return result;
    }
};
// 递归——精简版
void traversal(TreeNode* root, int depth) {
    if (root->left == NULL && root->right == NULL) {
        if (depth > maxDepth) {
            maxDepth = depth;
            result = root->val;
        }
        return;
    }
    if (root->left) {
        traversal(root->left, depth + 1); // 隐藏着回溯,值传递,不会改变变量
    }
    if (root->right) {
        traversal(root->right, depth + 1); // 隐藏着回溯,值传递,不会改变变量
    }
    return;
}

112. 路径总和

写递归函数是否需要返回值

  • 本题,这种只要找到一条路径,就立即返回,不需要完全找完,这种需要返回值递归返回
  • 需要搜索一棵二叉树不需要处理递归返回值,则不需要返回值,如113
  • 需要搜索一棵二叉树且需要处理递归返回值,则需要返回值,如236

心得

  • 递归法,考虑其实由于中没有处理逻辑,前中后序遍历均可,形参隐藏回溯,多总结,多思考
  • 直接AC

题解

  • 写的直接精简版本,可以考虑复原回溯过程的代码
  • 迭代用的栈,不仅记录指针而且记录路径总和
// 递归-体现回溯,第一次进递归时,已经减去根结点值,所以后面都是判断0,
class Solution {
private:
    bool traversal(TreeNode* cur, int count) {
        if (!cur->left && !cur->right && count == 0) return true; // 遇到叶子节点,并且计数为0
        if (!cur->left && !cur->right) return false; // 遇到叶子节点直接返回

        if (cur->left) { // 左
            count -= cur->left->val; // 递归,处理节点;
            if (traversal(cur->left, count)) return true;
            count += cur->left->val; // 回溯,撤销处理结果
        }
        if (cur->right) { // 右
            count -= cur->right->val; // 递归,处理节点;
            if (traversal(cur->right, count)) return true;
            count += cur->right->val; // 回溯,撤销处理结果
        }
        return false;
    }

public:
    bool hasPathSum(TreeNode* root, int sum) {
        if (root == NULL) return false;
        return traversal(root, sum - root->val);
    }
};
// 递归——精简版
class Solution {
public:
    bool hasPathSum(TreeNode* root, int targetSum) {
        if (root == nullptr) return false;
        if (!root->left && !root->right) {
            if (targetSum == root->val) {
                return true;
            }
        }
        return hasPathSum(root->left, targetSum - root->val) || hasPathSum(root->right, targetSum - root->val);
    }
};
// 迭代法
class Solution {
public:
    bool hasPathSum(TreeNode* root, int targetSum) {
        stack<pair<TreeNode*, int>> st;
        if (root == nullptr) return false;
        st.push(pair<TreeNode*, int>(root, root->val));
        while (!st.empty()) {
            pair<TreeNode*, int> node = st.top();
            st.pop();
            if (!node.first->left && !node.first->right && targetSum == node.second) {
                return true;
            }
            if (node.first->left) st.push(pair<TreeNode*, int>(node.first->left, node.second + node.first->left->val));
            if (node.first->right) st.push(pair<TreeNode*, int>(node.first->right, node.second + node.first->right->val));
        }
        return false;
    }

};

113. 路径总和 II

心得

  • 想的是用引用传参vector和vector>,其实用全局变量会方便很多

题解

  • 思路同112,不过添加vector保存位置
  • 注意回溯,vector路径也要回溯
class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    void traversal(TreeNode* cur, int count) {
        // 不同添加,在调用时就已经添加本层节点
        if (!cur->left && !cur->right && count == 0) { // 叶子结点
            result.push_back(path);
        }
        if (!cur->left && !cur->right) return;
        if (cur->left) {
            path.push_back(cur->left->val);
            count -= cur->left->val;
            traversal(cur->left, count);
            count += cur->left->val;
            path.pop_back();
        }
        if (cur->right) {
            path.push_back(cur->right->val); // 先加入左孩子结点
            count -= cur->right->val;
            traversal(cur->right, count);
            count += cur->right->val; // 回溯
            path.pop_back(); // 回溯
        }
        return;

    }
    vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
        result.clear();
        path.clear();
        if (root == nullptr) return result;
        path.push_back(root->val);
        traversal(root, targetSum - root->val);
        return result;
    }
};

106. 从中序与后序遍历序列构造二叉树

心得

  • 过程想的很清楚,一写各种问题

题解

  • 前序和中序、后序和中序都可以构造出唯一二叉树
  • 本题来说,后序的最后一个一定是根节点,然后以此为界分割前序,然后得前序左右,然后以此左右size一致特点,切割后序,然后递归即可
  • 注意分割时,剔除分割点,后序resize,前序index跳1等
// 递归——详细版
class Solution {
public:
    TreeNode* traversal(vector<int>& inorder, vector<int>& postorder) {
        // 1.终止条件
        if (inorder.size() == 0) return nullptr;
        // 2.确定分割点的值
        int rootvalue = postorder[postorder.size() - 1];
        // 构造结点
        TreeNode* root = new TreeNode(rootvalue);
        // 3.叶子结点直接返回
        if (inorder.size() == 1) return root;
        // 确定分割点
        int delimitValue = 0;
        for (delimitValue = 0; delimitValue < inorder.size(); delimitValue++) {
            if (inorder[delimitValue] == rootvalue) break;
        }
        // 确保统一变量,均采用左闭右开
        // 4.分割中序为左中 右中两半
        vector<int> leftInorder = vector<int>(inorder.begin(), inorder.begin() + delimitValue);
        vector<int> rightInorder = vector<int>(inorder.begin() + delimitValue + 1, inorder.end()); // 中间结点除外
        // 丢弃刚用过的中结点
        postorder.resize(postorder.size() - 1);
        // 5.根据中序中size一致来分割分割后序为左后,右后
        vector<int> leftPostorder = vector<int>(postorder.begin(), postorder.begin() + leftInorder.size());
        vector<int> rightPostorder = vector<int>(postorder.begin() + leftInorder.size(), postorder.end());
        // 6. 递归构建左右子树
        root->left = traversal(leftInorder, leftPostorder);
        root->right = traversal(rightInorder, rightPostorder);
        return root;
    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        if (inorder.size() == 0) return nullptr;
        return traversal(inorder, postorder); 
    }
// 递归——优化版,index来调用
class Solution {
public:
    TreeNode* traversal(vector<int>& inorder, int inorderBegin, int inorderEnd, 
                        vector<int>& postorder, int postorderBegin, int postorderEnd) {
        if (inorderBegin == inorderEnd) return nullptr;
        int rootValue = postorder[postorderEnd - 1];
        TreeNode* root = new TreeNode(rootValue);
        if (inorderEnd - inorderBegin == 1) return root;
        int delimiterIndex = inorderBegin;
        for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++) {
            if (inorder[delimiterIndex] == rootValue) break;
        }
        int leftInorderBegin = inorderBegin;
        int leftInorderEnd = delimiterIndex;
        int rightInorderBegin = delimiterIndex + 1;
        int rightInorderEnd = inorderEnd;
        int leftPostorderBegin = postorderBegin;
        int leftPostorderEnd = postorderBegin + (delimiterIndex - inorderBegin);
        int rightPostorderBegin = leftPostorderEnd;
        int rightPostorderEnd = postorderEnd - 1;
        root->left = traversal(inorder, leftInorderBegin, leftInorderEnd, postorder, leftPostorderBegin, leftPostorderEnd);
        root->right = traversal(inorder, rightInorderBegin, rightInorderEnd, postorder, rightPostorderBegin, rightPostorderEnd);
        return root;
    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        if (inorder.size() == 0) return nullptr;
        return traversal(inorder, 0, inorder.size(), postorder, 0, postorder.size());
    }
};

心得

题解

  • 如后序和中序构造一般,注意细节,注意debug
class Solution {
public:
    TreeNode* traversal(vector<int>& preorder, int preorderBegin, int preorderEnd, vector<int>& inorder, int inorderBegin, int inorderEnd) {
        if (preorderEnd - preorderBegin == 0) return nullptr;
        int rootValue = preorder[preorderBegin];
        TreeNode* root = new TreeNode(rootValue);
        if (preorderEnd - preorderBegin == 1) return root;
        int delimiterIndex = inorderBegin;
        for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++) {
            if (inorder[delimiterIndex] == rootValue) break;
        }
        // 坚持左闭右开
        int leftPreorderBegin = preorderBegin + 1;
        int leftPreorderEnd = preorderBegin + 1 + delimiterIndex - inorderBegin; // 中序size,由于右开,end - start即为大小
        int rightPreorderBegin = leftPreorderEnd;
        int rightPreorderEnd = preorderEnd;
        int leftInorderBegin = inorderBegin;
        int leftInorderEnd = delimiterIndex; // 注意不要+begin,此时index为实际而不是差值
        int rightInorderBegin = delimiterIndex + 1;
        int rightInorderEnd = inorderEnd;
        root->left = traversal(preorder, leftPreorderBegin, leftPreorderEnd, inorder, leftInorderBegin, leftInorderEnd);
        root->right = traversal(preorder, rightPreorderBegin, rightPreorderEnd, inorder, rightInorderBegin, rightInorderEnd);

        return root;
    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if (preorder.size() == 0 || inorder.size() == 0) return nullptr;
        return traversal(preorder, 0, preorder.size(), inorder, 0, inorder.size());
    }
};