目錄
- 題目
- 思路一:深度遞歸
- 思路二:廣度迭代
- 關于回溯
題目
給定一個二叉樹,返回所有從根節點到葉子節點的路徑。
說明: 葉子節點是指沒有子節點的節點。
示例:
輸入:
輸出: [“1->2->5”, “1->3”]
解釋: 所有根節點到葉子節點的路徑為: 1->2->5, 1->3
思路一:深度遞歸
void traversal(TreeNode* cur , vector<string>& paths,string path){if(cur == NULL) return;path+=to_string(cur->val);//如果是葉子結點,返回將path送入paths,然后return回去if(!cur->left && !cur->right){paths.push_back(path);}//如果不是葉子結點,將該結點加入路徑,然后繼續遍歷左右子結點else{path+= "->";traversal(cur->left,paths,path);traversal(cur->right,paths,path);}return;}
之前我一直在思考一個問題,就是如果我已經完成了一條路徑,那么必定要向上回溯到上面的結點,那么此時的path又該是怎樣的呢?
在我一開始的認為中,path會直接在原有的已完成的一條路徑上再次添加->,但仔細一想,并不是這樣。
以下面的樹為例:
1
/ \
2 3
\
5
遍歷的結點 | 輸入前的path | 輸入后的path | paths |
---|---|---|---|
1 | 1-> | ||
2 | 1-> | 1->2-> | |
2的左孩子NULL | 1->2-> | 1->2-> | |
5 | 1->2-> | 1->2->5 | 1->2->5 |
2 | (return的時候,返回到原來的函數,參數仍然是原來函數的參數)1-> | 1->2 | 1->2->5 |
1 | (return的時候,返回到原來的函數,參數仍然是原來函數的參數) | 1-> | 1->2->5 |
3 | 1-> | 1->3 | 1->2->5,1->3 |
1 | (return的時候,返回到原來的函數,參數仍然是原來函數的參數) | 1-> | 1->2->5,1->3 |
最后return根結點。
返回到上一級的時候path會被重置為上一級函數的path,而vector中的值不會被修改,因為我們使用的是&,vector中的值已經被修改了。
AC代碼:
/*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/
class Solution {
public:void traversal(TreeNode* cur , vector<string>& paths,string path){if(cur == NULL) return;path+=to_string(cur->val);//如果是葉子結點,返回將path送入paths,然后就該if(!cur->left && !cur->right){paths.push_back(path);}//如果不是葉子結點,將該結點加入路徑,然后繼續遍歷左右子結點else{path+= "->";traversal(cur->left,paths,path);traversal(cur->right,paths,path);}return;}vector<string> binaryTreePaths(TreeNode* root) {vector<string> paths;string path ="";traversal(root,paths,path);return paths;}
};
感覺對于回溯的思想還是很陌生,等二叉樹的題目練完了就去找幾個回溯的練練手。
遞歸就是函數的嵌套調用,函數調用的時候會將參數存入棧中,所以返回到上一級函數時,參數會自動撥正。
思路二:廣度迭代
維護一個隊列,存儲結點以及根到該結點的路徑。
一開始這個隊列里面只有根結點。
每一步迭代中,我們取出隊列中的首結點,如果它是葉子結點,則將它對應的路徑加入到答案中。
如果它不是葉子結點,則將它的所有孩子結點加入到隊列的末尾。當隊列為空時廣度優先搜索結束。
/*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/
class Solution {
public:vector<string> binaryTreePaths(TreeNode* root) {vector<string> paths;if(root == NULL){return paths;}queue<TreeNode*> node_queue;queue<string> path_queue;node_queue.push(root);path_queue.push(to_string(root->val));while(!node_queue.empty()){TreeNode* node = node_queue.front();string path = path_queue.front();node_queue.pop();path_queue.pop();if(node->left == NULL && node->right == NULL){paths.push_back(path);}else{if(node->left !=NULL){node_queue.push(node->left);path_queue.push(path+"->"+to_string(node->left->val));}if(node->right !=NULL){node_queue.push(node->right);path_queue.push(path+"->"+to_string(node->right->val));}}}return paths;}
};
關于回溯
class Solution {
public:void traversal(TreeNode* cur,vector<int>& path,vector<string>& paths){path.push_back(cur->val);//如果是葉子結點if(cur->left == NULL && cur->right ==NULL){string spath;for(int i=0;i<path.size()-1;i++){spath+= to_string(path[i]);spath+="->";}spath+=to_string(path[path.size()-1]);paths.push_back(spath);}//不是葉子結點if(cur->left){traversal(cur->left,path,paths);path.pop_back();//回溯}if(cur->right){traversal(cur->right,path,paths);path.pop_back();//回溯}}vector<string> binaryTreePaths(TreeNode* root) {vector<string> paths;vector<int> path;if(root == NULL) return paths;traversal(root,path,paths);return paths;}
};
注意這里對子路徑path使用了&,所以一旦改變之后,就無法返回到上一個函數的參數了,所以需要進行pop_back進行回溯。