代码随想录算法训练营day16 | 513.找树左下角的值、112. 路径总和、113. 路径总和 II、106.从中序与后序遍历序列构造二叉树

碎碎念:加油!!!
参考:代码随想录

513.找树左下角的值

题目链接

513.找树左下角的值

思想

本题可以使用递归法,也可以使用层序遍历迭代法。
**递归法:**可以使用前序遍历,前序遍历的顺序为左中右,也就是说,我们找到的第一个深度最大的叶子节点应该就是树左下角的值。
递归三部曲:

  1. 确定参数和返回值
  2. 终止条件:遍历到叶子节点(左孩子和右孩子都为空),比较深度,修改result。
  3. 单层逻辑:向左遍历,注意深度要回溯;向右遍历,注意回溯;
    本题没有对中的处理逻辑,所以本题前中后序都可以,本题只强调左在右的前面。
    也可以把回溯的过程隐藏在参数里面。

层序遍历迭代法:
只需要记录最后一行的第一个节点的数值

题解

// cpp 递归法
class Solution {
public:
    int result;
    int maxDepth = INT_MIN;
    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) {
            depth++;
            traversal(root->left, depth);
            depth--; // 回溯
        }
        if (root->right) {
            depth++;
            traversal(root->right, depth);
            depth--;
        }
        return;
    }
    int findBottomLeftValue(TreeNode* root) {
        traversal(root, 0);
        return result;
    }
};
# python 迭代法 精简写法
class Solution:
    def traversal(self, node, depth):
        if node.left == None and node.right == None:
            if depth > self.maxDepth:
                self.result = node.val
                self.maxDepth = depth
            return
    
        if node.left:
            self.traversal(node.left, depth+1) # 这句暗含了回溯
        if node.right:
            self.traversal(node.right, depth+1) # depth+1暗含了回溯

    def findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
        self.result = None
        self.maxDepth = float('-inf')
        self.traversal(root, 0)
        return self.result

// cpp 迭代法 层序遍历
class Solution {
public:
    int findBottomLeftValue(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();
                if (i == 0) result = node->val; // 记录每一层的第一个节点的值,最后更新会得到最后一层的第一个节点的值
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
        }
        return result;
    }
};
# python 迭代法 层序遍历
class Solution:
    def findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
        if root is None:
            return 0
        queue = deque()
        queue.append(root)
        result = 0
        while queue:
            size = len(queue)
            for i in range(size):
                node = queue.popleft()
                if i == 0:
                    result = node.val
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
        return result

反思

注意:最靠左侧的节点不一定是左孩子。

112. 路径总和

题目链接

112. 路径总和

思想

本题使用前中后序都可以。
递归三部曲:

  1. 参数和返回值:找到一条路径立刻返回,所以返回值定义成bool类型,返回值设置一个计数器,还有节点。
  2. 终止条件:遇到叶子节点时当前的路径是否符合题意。
  3. 单层递归逻辑:左:向左递归,记得回溯;右:向右递归,记得回溯。要判断一下返回值,如果返回true,要把true结果继续向上返回。

关于是否需要返回值:

  1. 如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。
  2. 如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。
  3. 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径就要及时返回。

本题是第三种情况,找到一条符合条件的路径就要返回。

题解

// cpp 
class Solution {
public:
    bool traversal(TreeNode* cur, int count) {
        // 遍历到叶子节点,且count减到了0
        if (!cur->left && !cur->right && count == 0) return true;
        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;
    }
    bool hasPathSum(TreeNode* root, int targetSum) {
        if (root == NULL) return false;
        return traversal(root, targetSum - root->val);
    }
};
# python 精简写法
class Solution:
    def traversal(self, cur, count):
        if not cur.left and not cur.right and count == 0:
            return True
        if not cur.left and not cur.right:
            return False
        if cur.left:
            if self.traversal(cur.left, count - cur.left.val): return True
        if cur.right:
            if self.traversal(cur.right, count - cur.right.val): return True
        return False

    def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
        if not root:
            return False
        return self.traversal(root, targetSum - root.val)
# python 再精简一点!!
class Solution:
    def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
        if not root:
            return False
        if not root.left and not root.right and targetSum == root.val:
            return True
        return self.hasPathSum(root.left, targetSum - root.val) or self.hasPathSum(root.right,  targetSum - root.val)

反思

本题没有中间的处理,所以前中后序都行,我们不关心中间。

113. 路径总和 II

题目链接

113. 路径总和 II

思想

本题和上一题很相似,区别在于本题要找出所有符合条件的路径。本题要搜索整棵二叉树,递归函数不需要返回值。

题解

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);
            return;
        }
        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 == NULL) return result;
        path.push_back(root->val);
        traversal(root, targetSum - root->val);
        return result;
    }
};
class Solution:
    def __init__(self):
        self.path = []
        self.result = []
    def traversal(self, cur, count):
        if not cur.left and not cur.right and count == 0:
            self.result.append(self.path[:])  #path[:] 表示从列表 path 的起始位置到结束位置(即整个列表)的一个浅拷贝(shallow copy)
            return
        if not cur.left and not cur.right:
            return

        if cur.left:
            self.path.append(cur.left.val)
            self.traversal(cur.left, count - cur.left.val)
            self.path.pop()
        if cur.right:
            self.path.append(cur.right.val)
            self.traversal(cur.right, count - cur.right.val)
            self.path.pop()
        return 
    
    def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
        if not root:
            return self.result
        self.path.append(root.val)
        self.traversal(root, targetSum - root.val)
        return self.result
        

反思

从上面两道题中感受搜索整棵树和某一条路径的差别。
python:self.path 是一个可变对象(如列表),而每次调用 self.result.append(self.path) 时,实际上是将 self.result 列表中添加了 self.path 的引用而不是副本。可以通过使用 self.path[:] 或者 list(self.path) 来实现浅拷贝。

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

题目链接

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

思想

明确中序和后序的遍历顺序。
中序和前后序遍历中的任意一个都可以唯一确定一个二叉树。
在这里插入图片描述
递归三部曲:

  1. 递归函数的参数和返回值:参数两个数组,返回值是节点
  2. 终止条件:后序为空,return NULL;
    2.1. 后序数组的size为0,返回空节点
    2.2. 后序数组最后一个元素为节点元素
    2.3. 寻找中序数组位置作为切割点
    2.4. 切割中序数组:用的index切
    2.5. 切割后序数组:用切完中序数组得到的左中序数组的大小、右中序数组的大小切。切割得到左后序数组,右后序数组。
  3. 单层逻辑:递归处理左中序、左后序;递归处理右中序、右后序。

题解

// cpp
class Solution {
public:
    TreeNode* traversal (vector<int>& inorder, vector<int>& postorder){
        // 2.1 后序数组的size为0,返回空节点
        if (postorder.size() == 0) return NULL;  

        // 2.2 后序数组最后一个元素为节点元素
        int rootValue = postorder[postorder.size() - 1];
        TreeNode* root = new TreeNode(rootValue);

        if (postorder.size() == 1) return root;

        // 2.3 寻找中序数组位置作为切割点
        int delimiterIndex;
        for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {
            if (inorder[delimiterIndex] == rootValue) break;
        }

        // 2.4 切割中序数组:用delimiterIndex切,得到左中序和右中序,左闭右开
        vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex);
        vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end());

        // 2.5 切割后序数组:用左中序和右中序的大小切,得到左后序和右后序
        postorder.resize(postorder.size() - 1); // 舍弃末尾元素,因为这个元素是中间节点,已经用过了
        vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
        vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end());
        // 3 单层逻辑
        root->left = traversal(leftInorder, leftPostorder);
        root->right = traversal(rightInorder, rightPostorder);
        return root;

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

    }
};
# python
class Solution:
    def traversal(self, inorder, postorder):
        if not postorder:
            return None
        
        rootValue = postorder[-1]
        root = TreeNode(rootValue)

        delimiterIndex = inorder.index(rootValue)

        leftInorder = inorder[:delimiterIndex]
        rightInorder = inorder[delimiterIndex+1:]

        leftPostorder = postorder[:len(leftInorder)]
        rightPostorder = postorder[len(leftInorder):len(postorder) - 1]

        root.left = self.traversal(leftInorder, leftPostorder)
        root.right = self.traversal(rightInorder, rightPostorder)

        return root
        


    def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]:
        if len(inorder) == 0 or len(postorder) == 0:
            return None
        return self.traversal(inorder, postorder)

反思

注意切数组的时候要有一个规定的切法,比如规定左闭右开。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值