代碼隨想錄算法訓練營|第十八天|513. 找树左下角的值、112. 路径总和、113. 路径总和II、106. 从中序与后序遍历序列构造二叉树、105. 从前序与中序遍历序列构造二。刷题心得(c++)

讀題

513. 找树左下角的值

自己看到题目的第一想法

這題一開始看到感覺是不是只能是左葉子的解法,看到卡哥說迭代比較簡單,也看到思路的一段話”找出最底下最左邊的值”,那的確用迭代會簡單很多,因為迭代的思路就是層序遍歷,並且值都是左到右,所以只要在迭代時,把最左邊的值放入到結果當中,就好了,整體思路還是比較簡單的,但遞迴的方式要想一下怎麼做才好。

看完代码随想录之后的想法

看完之後對於遞迴如何找到最左邊的值就比較清楚了,就是當我的深度大於最大深度的第一個值就是最左邊的值,因為優先往左遍歷,所以這樣遞迴就可以找到這個最左邊的值了。

112. 路径总和

自己看到题目的第一想法

看完感覺跟查詢所有路徑一樣,不過自己對於路徑這個部分不熟悉,雖然思考了可能的解法,但思路一直沒有打通,一開始的思路如下,看完代碼隨想錄之後,看一下差多少,有哪邊是自己沒有掌握的,盡力去掌握

看完代码随想录之后的想法

看完之後,對於二叉樹以及遞迴有更深的理解,在整體遞迴以及回朔的過程中,將完整的code攤開來看,其實理解上就沒有太困難,後續再做路徑總和II的時候,思緒就非常清晰,知道自己現在要做甚麼,可以不用做甚麼,對於遞迴的流程也有更深的感覺。

113. 路径总和II

自己看到题目的第一想法

看完之後其實對於這個題目就是要做一些轉換,加上儲存path的部分,假設走到葉節點並且符合條件的話,就在result儲存path

並且不用回傳值,因為這棵樹我們需要完整走完,並且在實現的過程中,我有刻意將回朔的過程顯示出來,所以在整體的實現邏輯上就會比較清晰。

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

看完代码随想录之后的想法

看完之後,照著卡哥的六步驟去建立出框家,之後再去實踐其實就簡單很多了,不過我認為還需要再琢磨一下,讓自己比較清楚的去完成。

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

自己看到题目的第一想法

基本思路跟後續一致,所以只是程式稍微修改一下而已。

513. 找树左下角的值 - 實作

思路

迭代思路

  1. 建立一個暫存數值的Queue,用來儲存每一層的數值
  2. 進入回圈後,第一個回圈判斷queue是否為空,如果為空代表樹已經遍歷完成
    1. 紀錄目前的queue size,根據queue size進入第二個回圈
      1. 使用node暫存que.pop出來的數值
      2. 假設當前為最左邊,更新最左邊的value
      3. 將node的左右子樹加入到queue當中
  3. 回傳結果

遞迴思路

  1. 設定全域變數
    1. maxDepth 為int的最小值
    2. result
  2. 傳入值,root、depth → 紀錄目前深度
  3. 假設目前為葉子點,深度大於maxDepth 則將當前的val寫入result → 到達最深的第一個節點一定是最左邊的節點
  4. 左邊開始遞迴
  5. 右邊開始遞迴

Code

迭代思路

class Solution {
public:
    int findBottomLeftValue(TreeNode* root) {
        queue<TreeNode*> que;
        if(root == NULL) return 0;
        int leftvalue = 0;
        que.push(root);
        while(!que.empty()) {
            int size = que.size();
            for(int i = 0; i < size; i++){
                TreeNode* node = que.front();
                que.pop();
                if(i == 0){
                    leftvalue = node->val;
                }
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
        }
        return leftvalue;
    }
};

遞迴思路

class Solution {
public:
    int maxDepth = INT_MIN;
    int result;
    void leftValue(TreeNode* cur, int depth) {
        if(!cur->left && !cur->right) {
            if(depth > maxDepth) {
                maxDepth = depth;
                result = cur->val;
            }
        }
        if(cur->left) {
            depth++;
            leftValue(cur->left, depth);
            depth--;
        }
        if(cur->right) {
            depth++;
            leftValue(cur->right, depth);
        }
    }
    int findBottomLeftValue(TreeNode* root) {
        leftValue(root, 0);
        return result;
    }
};

 

112. 路径总和 - 實作

思路

錯誤思路

  1. 傳入值 root、&pathsum、&targetsum
  2. pathsum += root→val
  3. 終止條件
    1. if(root == NULL) return 0;
    2. if(!root→left && !root→right)
      1. if( pathsum == targetsum)
        1. return 1;
      2. else
        1. return 0;
  4. 單遞迴
    1. if(cur→left) if(getpathsum(root→left,pathsum,targetsum) ) return true;
      1. pathsum += node→val;
    2. if(cur→right)getpathsum(root→right,pathsum,targetsum);

正確思路

  1. 傳入值 root, targetSum
  2. 終止條件
    1. if( !root→left && !root→right && count == 0) return true;
    2. if( !root→left && !root→right && count != 0) return false; // 遇到葉子節點判斷是否為零,不為零代表此條路徑不符合
  3. 單遞迴邏輯
    1. if(cur→left)
      1. count -= cur→left→val;
      2. if(getpathsum(root,count) return true → 假設有一個葉子節點的路徑符合targetSum則直接原地返回
      3. count += cur→left→val; → 因為以下的葉子節點的路徑
    2. if(cur→right)
      1. count -= cur→right→val;
      2. if(getpathsum(root,count) return true → 假設有一個葉子節點的路徑符合targetSum則直接原地返回
      3. count += cur→right→val;
    3. return false

思考: 在這個思路當中,其實遞迴在第一次時,觀察程式的執行過程會發現一直往左走,假設遇到終止條件,那就會返回a.ii ,假設為false就會執行到回朔的部分,在這裡我對於遞迴的感覺又深了點。

Code

class Solution {
public:
    bool checkPathSum(TreeNode* root, int targetSum) {
        //遍歷到葉子節點進行判斷,假設沒有到葉子節點,則持續遞迴
        if(!root->left && !root->right && targetSum == 0) return true;
        if(!root->left && !root->right && targetSum != 0) return false; 

        if(root->left) {
            //目前非葉子節點,先減去左子樹val,
            targetSum -= root->left->val;
            //進入遞迴,假設找到targetPathSum,則直接一路返回
            if(checkPathSum(root->left, targetSum)) return true;
            //因為有先減去數值所以需要先加回來,才能進入接下來右子樹的遞迴
            targetSum += root->left->val;
        }
        if(root->right) {
            //目前非葉子節點,先減去左子樹val,
            targetSum -= root->right->val;
            //進入遞迴,假設找到targetPathSum,則直接一路返回
            if(checkPathSum(root->right, targetSum)) return true;
						//因為後續不會再次進入遞迴,所以這個部分可加可不加
            //targetSum += root->left->val;
        }
        return false;
    }
    bool hasPathSum(TreeNode* root, int targetSum) {
        if(root == NULL) return false;
        return checkPathSum(root, targetSum - root->val);
    }
};

 

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

思路

  1. 判斷postorder size 是否為空, -> 當遍歷到某一個root 左子樹或右子樹為空,就必須將root->right or left 接向NULL
  2. 當前的root value = postorder最後一個數字(左右中)
  3. 如果postorder.size() == 1 return root → 代表現在走到葉節點return 當前的root 回去就好.
  4. 使用rootValue找出中序數組中的root在哪,並記錄位置
  5. 分割中序數組,使用左閉右開的原則
  6. 分割後序數組,使用左閉右開的原則
  7. 單遞迴原則
    1. root→left 遞迴左中序以及左後序數組
    2. root→right 遞迴右中序以及右後序數組
  8. 遞迴結束 return root。

Code

class Solution {
public:
    TreeNode* traversal(vector<int>& inorder, vector<int>& postorder) {
				// 1. If postorder size is 0, return NULL. -> when traversal left or right child node is null, need to return null.
        if(postorder.size() == 0) return NULL;
        // 2. Postorder last member is current root node.
        int rootValue = postorder[postorder.size() - 1];
        TreeNode* root = new TreeNode(rootValue);

        // leaf node
        if(postorder.size() == 1) return root;

        // 3. Find current root node in the inorder vector.
        int delimiterIndex;
        for(delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {
            if(inorder[delimiterIndex] == rootValue) break; // Found root node in the inorder vector.
        }
        // 4. Cut inorder vector using delimiterIndex. Get inorder_left and inorder_right.
        // half-open interval [0, delimiterIndex)
        vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex);
        // The range is [delimiterIndex + 1, end) because delimiterIndex is the cutting pointer and is not included.
        vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end());
        // 5. Cut postorder vector using inorder_left. Get postorder_left and postorder_right.
        vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
        vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end() - 1);

        // 6. recursion to tree_left and tree_right
        root->left = traversal(leftInorder, leftPostorder);
        root->right = traversal(rightInorder, rightPostorder);

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

 

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

思路

  1. 判斷postorder size 是否為空, -> 當遍歷到某一個root 左子樹或右子樹為空,就必須將root->right or left 接向NULL
  2. 當前的root value = preorder第一個數字(中左右)
  3. 如果postorder.size() == 1 return root → 代表現在走到葉節點return 當前的root 回去就好.
  4. 使用rootValue找出中序數組中的root在哪,並記錄位置
  5. 分割中序數組,使用左閉右開的原則
  6. 分割前序數組,使用左閉右開的原則
  7. 單遞迴原則
    1. root→left 遞迴左中序以及左前序數組
    2. root→right 遞迴右中序以及右前序數組
  8. 遞迴結束 return root。

Code

class Solution {
public:
    TreeNode* traversal(vector<int>& inorder, vector<int>& preorder) {
				// 1. If preorder size is 0, return NULL. -> when traversal left or right child node is null, need to return null.
        if(preorder.size() == 0) return NULL;
        // 2. Preorder first member is current root node.
        int rootValue = preorder[0];
        TreeNode* root = new TreeNode(rootValue);

        // leaf node
        if(preorder.size() == 1) return root;

        // 3. Find current root node in the inorder vector.
        int delimiterIndex;
        for(delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {
            if(inorder[delimiterIndex] == rootValue) break; // Found root node in the inorder vector.
        }
        // 4. Cut inorder vector using delimiterIndex. Get inorder_left and inorder_right.
        // half-open interval [0, delimiterIndex)
        vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex);
        // The range is [delimiterIndex + 1, end) because delimiterIndex is the cutting pointer and is not included.
        vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end());
        // 5. Cut preorder vector using inorder_left. Get preorder_left and preorder_right.
        vector<int> leftPreorder(preorder.begin() + 1, preorder.begin() + 1 + leftInorder.size());
        vector<int> rightPreorder(preorder.begin() + 1 + leftInorder.size(), preorder.end());

        // 6. recursion to tree_left and tree_right
        root->left = traversal(leftInorder, leftPreorder);
        root->right = traversal(rightInorder, rightPreorder);

        // 7. return root. recursion is end.
        return root;
    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if(inorder.size() == 0 || preorder.size() == 0) return NULL;
        return traversal(inorder, preorder);
    }
};
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值