代码随想录算法训练营第十八天| 513.找树左下角的值 112. 路径总和 113.路径总和ii 106.从中序与后序遍历序列构造二叉树 105.从前序与中序遍历序列构造二叉树

513.找树左下角的值

思路

首先自己的想法是层序遍历
但还是跟着敲了一遍递归

代码

递归

class Solution {
public:
    int maxDepth = INT_MIN;
    int result;
    void travelsal(TreeNode* root, int depth) {
        if(root->left == NULL && root->right == NULL) {
            if(depth > maxDepth) {
                maxDepth = depth;
                result = root->val;
            }
            return;
        }

        if(root->left) {
            depth++;
            travelsal(root->left,depth);
            depth--;
        }
        if(root->right) {
            depth++;
            travelsal(root->right,depth);
            depth--;
        }
        return;
    }
    int findBottomLeftValue(TreeNode* root) {
        travelsal(root,0);
        return result;
    }
};

层序遍历

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;
    }
};

总结

112. 路径总和

思路

  1. 递归函数什么时候需要返回值?什么时候不需要返回值?这里总结如下三点:
    a. 如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。(这种情况就是本文下半部分介绍的113.路径总和ii)
    b. 如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。 (这种情况我们在236. 二叉树的最近公共祖先
    c. 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(本题的情况)

  2. 累加变累减:不要去累加然后判断是否等于目标和,那么代码比较麻烦,可以用递减,让计数器count初始为目标和,然后每次减去遍历路径节点上的数值。
    如果最后count == 0,同时到了叶子节点的话,说明找到了目标和。

  3. 因为终止条件是判断叶子节点,所以递归的过程中就不要让空节点进入递归了。

  4. 迭代法有点复杂

代码

class Solution {
public:
    bool travelsal(TreeNode* cur, int count) {
        if(!cur->left && !cur->right && count == 0) return true;
        if(!cur->left && !cur->right) return false;

        if(cur->left) {
            if(travelsal(cur->left,count- cur->left->val)) return true;
        }
        if(cur->right) {
            // 注意回溯逻辑
            if(travelsal(cur->right,count- cur->right->val)) return true;
        }
        return false;
    }
    bool hasPathSum(TreeNode* root, int targetSum) {
        if(root == NULL) return false;
        return travelsal(root,targetSum - root->val);
    }
};

迭代

class Solution {
public:
    
    bool hasPathSum(TreeNode* root, int targetSum) {
        if(root == NULL) return false;

        stack<pair<TreeNode*, int>> st;

        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->right) {
                st.push(pair<TreeNode*,int> (node.first->right,node.second + node.first->right->val));

            }

            if(node.first->left) {
                st.push(pair<TreeNode*,int> (node.first->left,node.second + node.first->left->val));

            }

        }
        return false;
    }
};

总结

  1. 注意回溯逻辑:travelsal(cur->left,count- cur->left->val)这里包含的意思是把减掉数字的count的值传进去,但是等他回溯到上一层节点时,count是没变的,做到的了回溯。也可以手动先减后加完成回溯逻辑

113.路径总和ii

思路

和上一题不同,上一题是要找是否存在一条路径,这题要搜索整个树,找到所有结果

代码

class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void travelsal(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;
            travelsal(cur->left,count);
            count += cur->left->val;
            path.pop_back();
        }

        if(cur->right) {
            path.push_back(cur->right->val);
            count -= cur->right->val;
            travelsal(cur->right,count);
            count += cur->right->val;
            path.pop_back();
        }
        return;
    }
public:
    vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
        result.clear();
        path.clear();

        if(root == NULL ) return result;
        path.push_back(root->val);
        travelsal(root, targetSum - root->val);
        return result;
    }
};

总结

  1. 如果需要搜索整颗二叉树,那么递归函数就不要返回值,如果要搜索其中一条符合条件的路径,递归函数就需要返回值,因为遇到符合条件的路径了就要及时返回。“ (ref:二叉树:递归函数什么时候需要返回值?

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

思路

此时有一个很重的点,就是中序数组大小一定是和后序. 数组的大小相同的(这是必然)。

  1. 第一步:如果数组大小为零的话,说明是空节点了。
  2. 第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
  3. 第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
  4. 第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
  5. 第五步:切割后序数组,切成后序左数组和后序右数组
  6. 第六步:递归处理左区间和右区间

代码

class Solution {
private:
    TreeNode* travelsal(vector<int>& inorder, vector<int>& postorder) {
        if(postorder.size() == 0) return NULL;

        int rootValue = postorder[postorder.size() - 1];
        TreeNode* root = new TreeNode(rootValue);

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

        int delimiterIndex;
        for(delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {
            if(inorder[delimiterIndex] == rootValue) break;
        }

        // 左闭右开
        vector<int> leftInorder(inorder.begin(), inorder.begin()+delimiterIndex);
        vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end());

        postorder.resize(postorder.size() -1);

        vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
        vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end());

        root->left = travelsal(leftInorder, leftPostorder);
        root->right = travelsal(rightInorder, rightPostorder);

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

        return travelsal(inorder,postorder);
    }
};

总结

  1. 用下标索引效率会更好
  2. 注意左闭右开,循环不变量

105.从前序与中序遍历序列构造二叉树

思路

和后序思路一样

代码

class Solution {
private:
    TreeNode* travelsal(vector<int>& preorder, vector<int>& inorder) {
        if(preorder.size() == 0) return NULL;

        int rootValue = preorder[0];
        TreeNode* root = new TreeNode(rootValue);

        int delimiterIdx;
        for (delimiterIdx = 0; delimiterIdx < inorder.size(); delimiterIdx++) {
            if(inorder[delimiterIdx] == rootValue) {
                // 均无重复元素才可以这样做
                break;
            }
        }

        // 左闭右开
        // [0,delimiterIdx)
        vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIdx);
        // [delimiterIdx+1,end)
        vector<int> rightInorder(inorder.begin() + delimiterIdx + 1, inorder.end());
        // [1,)
        vector<int> leftPreorder(preorder.begin()+1, preorder.begin() + 1 + leftInorder.size());
        vector<int> rightPreorder(preorder.begin() + 1 + leftInorder.size(), preorder.end() );

        root->left = travelsal(leftPreorder, leftInorder);
        root->right = travelsal(rightPreorder, rightInorder);

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

总结

  1. 这种方法因为每一层都new了vector所以不如索引方法好,但是思路很清楚
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值