讀題
513. 找树左下角的值
自己看到题目的第一想法
這題一開始看到感覺是不是只能是左葉子的解法,看到卡哥說迭代比較簡單,也看到思路的一段話”找出最底下最左邊的值”,那的確用迭代會簡單很多,因為迭代的思路就是層序遍歷,並且值都是左到右,所以只要在迭代時,把最左邊的值放入到結果當中,就好了,整體思路還是比較簡單的,但遞迴的方式要想一下怎麼做才好。
看完代码随想录之后的想法
看完之後對於遞迴如何找到最左邊的值就比較清楚了,就是當我的深度大於最大深度的第一個值就是最左邊的值,因為優先往左遍歷,所以這樣遞迴就可以找到這個最左邊的值了。
112. 路径总和
自己看到题目的第一想法
看完感覺跟查詢所有路徑一樣,不過自己對於路徑這個部分不熟悉,雖然思考了可能的解法,但思路一直沒有打通,一開始的思路如下,看完代碼隨想錄之後,看一下差多少,有哪邊是自己沒有掌握的,盡力去掌握
看完代码随想录之后的想法
看完之後,對於二叉樹以及遞迴有更深的理解,在整體遞迴以及回朔的過程中,將完整的code攤開來看,其實理解上就沒有太困難,後續再做路徑總和II的時候,思緒就非常清晰,知道自己現在要做甚麼,可以不用做甚麼,對於遞迴的流程也有更深的感覺。
113. 路径总和II
自己看到题目的第一想法
看完之後其實對於這個題目就是要做一些轉換,加上儲存path的部分,假設走到葉節點並且符合條件的話,就在result儲存path
並且不用回傳值,因為這棵樹我們需要完整走完,並且在實現的過程中,我有刻意將回朔的過程顯示出來,所以在整體的實現邏輯上就會比較清晰。
106. 从中序与后序遍历序列构造二叉树
看完代码随想录之后的想法
看完之後,照著卡哥的六步驟去建立出框家,之後再去實踐其實就簡單很多了,不過我認為還需要再琢磨一下,讓自己比較清楚的去完成。
105. 从前序与中序遍历序列构造二叉树
自己看到题目的第一想法
基本思路跟後續一致,所以只是程式稍微修改一下而已。
513. 找树左下角的值 - 實作
思路
迭代思路
- 建立一個暫存數值的Queue,用來儲存每一層的數值
- 進入回圈後,第一個回圈判斷queue是否為空,如果為空代表樹已經遍歷完成
- 紀錄目前的queue size,根據queue size進入第二個回圈
- 使用node暫存que.pop出來的數值
- 假設當前為最左邊,更新最左邊的value
- 將node的左右子樹加入到queue當中
- 紀錄目前的queue size,根據queue size進入第二個回圈
- 回傳結果
遞迴思路
- 設定全域變數
- maxDepth 為int的最小值
- result
- 傳入值,root、depth → 紀錄目前深度
- 假設目前為葉子點,深度大於maxDepth 則將當前的val寫入result → 到達最深的第一個節點一定是最左邊的節點
- 左邊開始遞迴
- 右邊開始遞迴
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. 路径总和 - 實作
思路
錯誤思路
- 傳入值 root、&pathsum、&targetsum
- pathsum += root→val
- 終止條件
- if(root == NULL) return 0;
- if(!root→left && !root→right)
- if( pathsum == targetsum)
- return 1;
- else
- return 0;
- if( pathsum == targetsum)
- 單遞迴
- if(cur→left) if(getpathsum(root→left,pathsum,targetsum) ) return true;
- pathsum += node→val;
- if(cur→right)getpathsum(root→right,pathsum,targetsum);
- if(cur→left) if(getpathsum(root→left,pathsum,targetsum) ) return true;
正確思路
- 傳入值 root, targetSum
- 終止條件
- if( !root→left && !root→right && count == 0) return true;
- if( !root→left && !root→right && count != 0) return false; // 遇到葉子節點判斷是否為零,不為零代表此條路徑不符合
- 單遞迴邏輯
- if(cur→left)
- count -= cur→left→val;
- if(getpathsum(root,count) return true → 假設有一個葉子節點的路徑符合targetSum則直接原地返回
- count += cur→left→val; → 因為以下的葉子節點的路徑
- if(cur→right)
- count -= cur→right→val;
- if(getpathsum(root,count) return true → 假設有一個葉子節點的路徑符合targetSum則直接原地返回
- count += cur→right→val;
- return false
- if(cur→left)
思考: 在這個思路當中,其實遞迴在第一次時,觀察程式的執行過程會發現一直往左走,假設遇到終止條件,那就會返回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. 从中序与后序遍历序列构造二叉树 - 實作
思路
- 判斷postorder size 是否為空, -> 當遍歷到某一個root 左子樹或右子樹為空,就必須將root->right or left 接向NULL
- 當前的root value = postorder最後一個數字(左右中)
- 如果postorder.size() == 1 return root → 代表現在走到葉節點return 當前的root 回去就好.
- 使用rootValue找出中序數組中的root在哪,並記錄位置
- 分割中序數組,使用左閉右開的原則
- 分割後序數組,使用左閉右開的原則
- 單遞迴原則
- root→left 遞迴左中序以及左後序數組
- root→right 遞迴右中序以及右後序數組
- 遞迴結束 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. 从前序与中序遍历序列构造二叉树 - 實作
思路
- 判斷postorder size 是否為空, -> 當遍歷到某一個root 左子樹或右子樹為空,就必須將root->right or left 接向NULL
- 當前的root value = preorder第一個數字(中左右)
- 如果postorder.size() == 1 return root → 代表現在走到葉節點return 當前的root 回去就好.
- 使用rootValue找出中序數組中的root在哪,並記錄位置
- 分割中序數組,使用左閉右開的原則
- 分割前序數組,使用左閉右開的原則
- 單遞迴原則
- root→left 遞迴左中序以及左前序數組
- root→right 遞迴右中序以及右前序數組
- 遞迴結束 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);
}
};