一、树的遍历

【非递归版】

1. 后序

  1. class Solution
  2. {
  3. public:
  4. vector<int> postorderTraversal(TreeNode *root)
  5. {
  6. vector<int> answer;
  7. stack<pair<TreeNode*,int>> s;
  8. s.push(make_pair(root,));
  9. while(!s.empty())
  10. {
  11. TreeNode *now=s.top().first;
  12. if(now==NULL)
  13. {
  14. s.pop();
  15. }
  16. else
  17. {
  18. switch(s.top().second++)
  19. {
  20. case :
  21. s.push(make_pair(now->left,));
  22. break;
  23. case :
  24. s.push(make_pair(now->right,));
  25. break;
  26. case :
  27. s.pop();
  28. answer.push_back(now->val);
  29. break;
  30. }
  31. }
  32. }
  33. return answer;
  34. }
  35. };

这里用pair中的first存放指针,second存放一个int值。这个int值是用来标记当前节点的孩子(子树)的访问情况,值取0表示该节点的左孩子、右孩子都尚未访问;值取1表示左孩子已访问,右孩子尚未访问;值取2表示左右孩子都已访问过。这样,当值为2时就可以把当前结点的值输出(后序的定义)。

update1: 记得判断栈顶元素对应的first指针非空

2.先序/前序

如果按基于上面后序的代码模式来改写,也可以轻松的改为先序。只是没有必要这么繁琐。后面有个不基于改写的、简化版的。

改写版的代码如下

  1. class Solution
  2. {//trying to change postorder to preorder
  3. public:
  4. vector<int> preorderTraversal(TreeNode *root)
  5. {
  6. vector<int> answer;
  7. stack<pair<TreeNode*,int>> s;
  8. s.push(make_pair(root,));
  9. while(!s.empty())
  10. {
  11. TreeNode *now=s.top().first;
  12. if(now==NULL)
  13. {
  14. s.pop();
  15. }
  16. else
  17. {
  18. if(s.top().second==)//this can guarantee the node will not be repeatedly pushed into answer.
  19. answer.push_back(now->val);
  20. switch(s.top().second++)
  21. {
  22. case :
  23. s.push(make_pair(now->left,));
  24. break;
  25. case :
  26. s.push(make_pair(now->right,));
  27. break;
  28. case :
  29. s.pop();
  30. //answer.push_back(now->val);
  31. break;
  32. }
  33. }
  34. }
  35. return answer;
  36. }
  37. };
  1. 这样的改写是有效的,但是其实不必这么麻烦。因为对后序来说,必须要保存一个int值记录状态信息,从而在节点的左右子树都访问过后才能输出该节点的value。但是对前序而言,实际上只要遇到节点就输出其value即可,然后把该节点左右孩子压入栈,之后该节点就没有存在价值了。因此,前序不用像后序那样用一个int来记录状态信息。
  2.  
  3. 这里需要注意的是,在将节点的左右孩子压入栈时,应该先压入其右孩子,再压入其左孩子。

Explanation

简化版代码如下:

  1. class Solution
  2. {
  3. public:
  4. vector<int> preorderTraversal(TreeNode* root)
  5. {
  6. vector<int> answer;
  7. stack<TreeNode*> s;
  8. s.push(root);
  9. while(!s.empty())
  10. {
  11. TreeNode* now=s.top();
  12. s.pop();//don't forget!
  13. if(now!=NULL)
  14. {
  15. answer.push_back(now->val);
  16. s.push(now->right);
  17. s.push(now->left);
  18. }
  19. }
  20. return answer;
  21. }
  22. };

update 8.9

  1. vector<int> preorderTraversal(TreeNode* root) {
  2. vector<int> result;
  3. stack<TreeNode*> s;
  4. s.push(root);
  5. while (!s.empty()) {
  6. TreeNode* cur = s.top();
  7. if (cur == NULL) {
  8. s.pop();
  9. } else {
  10. result.push_back(cur->val);
  11. s.pop();
  12. s.push(cur->right);
  13. s.push(cur->left);
  14. }
  15. }
  16. return result;
  17. }

3. 中序

同样,对于中序也可以用后序那种模板式的代码来改写。改写版代码如下:

  1. class Solution
  2. {
  3. public:
  4. vector<int> inorderTraversal(TreeNode* root)
  5. {
  6. vector<int> answer;
  7. stack<pair<TreeNode*,int>> s;
  8. s.push(make_pair(root,));
  9. while(!s.empty())
  10. {
  11. TreeNode* now=s.top().first;
  12. if(now==NULL)
  13. s.pop();
  14. else
  15. {
  16. switch(s.top().second++)
  17. {
  18. case :
  19. s.push(make_pair(now->left,));
  20. break;
  21. case :
  22. s.pop();
  23. answer.push_back(now->val);
  24. s.push(make_pair(now->right,));
  25. break;
  26. }
  27. }
  28. }
  29. return answer;
  30. }
  31. };

【递归版】

  1. class Solution
  2. {
  3. public:
  4. vector<int> inorderTraversal(TreeNode* root)
  5. {
  6. vector<int> answer;
  7. helper(root,answer);
  8. return answer;
  9. }
  10. void helper(TreeNode* root,vector<int> &answer)
  11. {
  12. if(root!=NULL)
  13. {
  14. helper(root->left,answer);
  15. answer.push_back(root->val);
  16. helper(root->right,answer);
  17. }
  18. }
  19. };

Divide & Conquer版】(在树相关的问题中更常用

preorder:

  1. class Solution {
  2. public:
  3. vector<int> preorderTraversal(TreeNode* root) {
  4. vector<int> result;
  5. if (root == NULL) {
  6. return result;
  7. }
  8.  
  9. // Divide
  10. vector<int> left = preorderTraversal(root->left);
  11. vector<int> right = preorderTraversal(root->right);
  12.  
  13. // Conquer
  14. result.push_back(root->val);
  15. result.insert(result.end(), left.begin(), left.end());
  16. result.insert(result.end(), right.begin(), right.end());
  17. return result;
  18. }
  19. };

Morris版

二、N-Queens

  1. class Solution
  2. {
  3. public:
  4. vector<vector<string>> solveNQueens(int n)
  5. {
  6. vector<vector<string>> result;
  7. N=n;
  8. columns=vector<int>(n,);
  9. anti_diag=vector<int>(*n,);
  10. main_diag=vector<int>(*n,);
  11. C=vector<int>(n,);
  12. dfs(result,);
  13. return result;
  14. }
  15. private:
  16. vector<int> columns;
  17. vector<int> main_diag;
  18. vector<int> anti_diag;
  19. vector<int> C;
  20. int N;
  21.  
  22. void dfs(vector<vector<string>> &result,int row)//如果把result也定义在class里当成员变量,那dfs就只用row一个参数就行了。
  23. {
  24. if(row==N)
  25. {
  26. vector<string> tmp;
  27. for(int i=;i<N;i++)
  28. {
  29. string str(N,'.');
  30. str[C[i]]='Q';
  31. tmp.push_back(str);
  32. }
  33. result.push_back(tmp);
  34. return;
  35. }
  36. for(int j=;j<N;j++)
  37. {
  38. bool ok=columns[j]== && anti_diag[j+row]== && main_diag[row-j+N]==;
  39. if(!ok) continue;
  40. C[row]=j;
  41. columns[j]=anti_diag[row+j]=main_diag[row-j+N]=;
  42. dfs(result,row+);
  43. columns[j]=anti_diag[row+j]=main_diag[row-j+N]=;
  44. }
  45. }
  46. };
  1. 需要注意的:除了每条横线、竖线上不能有1个以上的queen之外,斜线上也不能有1个以上的queen
  2.  
  3. 斜线具体需要分为平行于主对角线的这2n-1条线和平行于反对角线的2n-1条线。
  4.  
  5. 对于平行于主对角线main_diag的这些斜线上的点而言,行与列之和(row+j)是定值,其和分布在0~2N-。
  6.  
  7. 对于平行于反对角线anti_diag的这些斜线上的点而言,行与列之差(row-j)是定值,其差分布在-(N-)~(N-)。为了将其转换为数组下标方便计算,将其加上N,这样就转换为分布在1~2N-.
  8.  
  9. 在写N皇后问题的dfs函数时意识到,其实有时候dfs里的参数很多主要是因为这些变量是定义在主角函数里的(比如这里的solveNQueens)。如果把这些变量都定义在class里面(通常放到private区域里),dfs就不用传这么多参数了。

Explanation

N-Queens II

  1. class Solution
  2. {
  3. public:
  4. int totalNQueens(int n)
  5. {
  6. N=n;
  7. columns=vector<int>(n,);
  8. anti_diag=vector<int>(*n,);
  9. main_diag=vector<int>(*n,);
  10. C=vector<int>(n,);
  11. int count=;
  12. dfs(count,);
  13. return count;
  14. }
  15. private:
  16. vector<int> columns;
  17. vector<int> main_diag;
  18. vector<int> anti_diag;
  19. vector<int> C;
  20. int N;
  21. //int count=0;
  22. void dfs(int &count,int row)
  23. {
  24. if(row==N)
  25. {
  26. count++;
  27. return;
  28. }
  29. for(int j=;j<N;j++)
  30. {
  31. bool ok=columns[j]== && anti_diag[row+j]== && main_diag[row-j+N]==;
  32. if(!ok) continue;
  33. columns[j]=anti_diag[j+row]=main_diag[row-j+N]=;
  34. C[row]=j;
  35. dfs(count,row+);
  36. columns[j]=anti_diag[row+j]=main_diag[row-j+N]=;
  37. }
  38. }
  39. };

需要注意的是,if(!ok)时应当continue,不是return。

三、

1. Maximum Depth of Binary Tree

[Divide & Conquer 版本][推荐]

  1. class Solution {
  2. public:
  3. int maxDepth(TreeNode* root) {
  4. if (root == NULL) {
  5. return ;
  6. }
  7. int left = maxDepth(root->left);
  8. int right = maxDepth(root->right);
  9. return max(left, right) + ;
  10. }
  11. };

[简洁版本] 时间复杂度 O(n), 空间复杂度O(logn)

  1. class Solution
  2. {
  3. public:
  4. int maxDepth(TreeNode* root)
  5. {
  6. if(root==NULL) return ;
  7. return max(maxDepth(root->left),maxDepth(root->right))+;
  8. }
  9. };

[模版式dfs版本]

  1. class Solution
  2. {
  3. public:
  4. int maxDepth(TreeNode* root)
  5. {
  6. if(!root) return ;
  7. helper(root,);
  8. return max;
  9. }
  10. private:
  11. int max=;
  12. void helper(TreeNode* root, int count)
  13. {
  14. if(root==NULL)
  15. {
  16. if(count->max)
  17. max=count-;
  18. return;
  19. }
  20. helper(root->left,count+);
  21. helper(root->right,count+);
  22. }
  23. };

非递归版:基于前序遍历的基础之上,加上一个记录各节点高度的unordered_map和一个变量max即可。(所以说,熟练掌握树的非递归遍历后,可以在其基础上略加修改即可达到很多其他要求。)

  1. class Solution
  2. {
  3. public:
  4. int maxDepth(TreeNode* root)
  5. {
  6. if(!root) return ;
  7. stack<TreeNode*> s;
  8. s.push(root);
  9. unordered_map<TreeNode*,int> umap;
  10. umap[root]=;
  11. while(!s.empty())
  12. {
  13. TreeNode* now=s.top();
  14. s.pop();
  15. if(now!=NULL)
  16. {
  17. if(!now->left && !now->right)
  18. {
  19. if(umap[now]>max)
  20. max=umap[now];
  21. }
  22. s.push(now->right);
  23. if(now->right!=NULL)//可删掉,不影响
  24. umap[now->right]=umap[now]+;
  25. s.push(now->left);
  26. if(now->left!=NULL)//可删掉,不影响
  27. umap[now->left]=umap[now]+;
  28. }
  29. }
  30. return max;
  31. }
  32. private:
  33. int max=;
  34. };

2. Minimum Depth of Binary Tree

递归版:

[模版式版本]

  1. class Solution
  2. {
  3. public:
  4. int minDepth(TreeNode* root)
  5. {
  6. if(root==NULL) return ;
  7. helper(root,);
  8. return minD;
  9. }
  10. private:
  11. int minD=INT_MAX;
  12. void helper(TreeNode* root,int count)
  13. {
  14. if(!root->left && !root->right)
  15. {
  16. if(count<minD)
  17. minD=count;
  18. return;
  19. }
  20. if(root->left)
  21. helper(root->left,count+);
  22. if(root->right)
  23. helper(root->right,count+);
  24. }
  25. };

注意:求minDepth时有很多小细节容易忽略和写错,要多注意。maxDepth里可以通过求所有NULL节点的上一层的高度的最大值来作为maxD,但是minDepth不能用此法,因为一个节点的最小高度不等于min(minDepth(root->left),minDepth(root->right))+1()

update 8.10:

  1. class Solution {
  2. public:
  3. int minDepth(TreeNode* root) {
  4. if (root == NULL) {
  5. return ;
  6. }
  7. return getMin(root);
  8. }
  9. int getMin(TreeNode* root) {
  10. if (root == NULL) {
  11. return INT_MAX;
  12. }
  13. if (root->left == NULL && root->right == NULL) {
  14. return ;
  15. }
  16. return min(getMin(root->left), getMin(root->right)) + ;
  17. }
  18. };

这里用了一个trick。若root==NULL,则视为其minDepth是INT_MAX。通过这种方式来忽略那些只有左子树或只有右子树为空的节点的干扰。

非递归版:(与求maxDepth的大体类似)

  1. class Solution
  2. {
  3. public:
  4. int minDepth(TreeNode* root)
  5. {
  6. if(root==NULL) return ;
  7. stack<TreeNode*> s;
  8. s.push(root);
  9. unordered_map<TreeNode*,int> umap;
  10. umap[root]=;
  11. int minD=INT_MAX;
  12. while(!s.empty())
  13. {
  14. TreeNode* now=s.top();
  15. s.pop();
  16. if(now!=NULL)
  17. {
  18. if(!now->left && !now->right)
  19. {
  20. if(umap[now]<minD)
  21. minD=umap[now];
  22. }
  23. s.push(now->right);
  24. umap[now->right]=umap[now]+;
  25. s.push(now->left);
  26. umap[now->left]=umap[now]+;
  27. }
  28. }
  29. return minD;
  30. }
  31. };

四、

1. Same Tree

  1. class Solution {
  2. public:
  3. bool isSameTree(TreeNode* p,TreeNode* q)
  4. {
  5. if(p==NULL && q==NULL)
  6. return true;
  7. if(p==NULL || q==NULL) //能运行到这说明上个if的判定不成立,即p和q并不都为NULL.当一个为NULL一个不为NULL时,说明结构不同,即不相等。
  8. return false;
  9. if(p->val!=q->val)
  10. return false;
  11. return isSameTree(p->left,q->left) && isSameTree(p->right,q->right);
  12. }
  13. };

2. Symmetric Tree

这里面直接就调用了same tree的代码(微加修改),isSameTree函数的改动之处就在于最后是p的left与q的right比较,p的right与q的left比较。

  1. class Solution
  2. {
  3. public:
  4. bool isSymmetric(TreeNode* root)
  5. {
  6. if(root==NULL) return true;
  7. return isSameTree(root->left,root->right);
  8. }
  9. private:
  10. bool isSameTree(TreeNode* p,TreeNode* q)
  11. {
  12. if(p==NULL && q==NULL) return true;
  13. if(p==NULL || q==NULL) return false;
  14. if(p->val!=q->val) return false;
  15. return isSameTree(p->left,q->right)&&isSameTree(p->right,q->left);//left和right比较
  16. }
  17. };

五、

1. Populating Next Right Pointers in Each Node

  1. class Solution
  2. {
  3. public:
  4. void connect(TreeLinkNode* root)
  5. {
  6. if(root==NULL) return;
  7. if(root->left!=NULL)
  8. root->left->next=root->right;
  9. if(root->right!=NULL)
  10. root->right->next=root->next==NULL?NULL:root->next->left;
  11. connect(root->left);
  12. connect(root->right);
  13. }
  14. };

2. Populating Next Right Pointers in Each Node II

  1. class Solution
  2. {
  3. public:
  4. void connect(TreeLinkNode* root)
  5. {
  6. TreeLinkNode* tempChild=new TreeLinkNode();
  7. while(root)//root从上到下遍历每一层(纵向)
  8. {
  9. TreeLinkNode* cur=tempChild;
  10. while(root)//root从左到右遍历某个特定的层(横向)
  11. {
  12. if(root->left){cur->next=root->left;cur=cur->next;}
  13. if(root->right){cur->next=root->right;cur=cur->next;}
  14. root=root->next;//root向右移动
  15. }
  16. root=tempChild->next;//root跳到下一层
  17. tempChild->next=NULL;
  18. }
  19. }
  20. };

六、

1. Convert Sorted Array to Binary Search Tree

  1. class Solution
  2. {
  3. public:
  4. TreeNode* sortedArrayToBST(vector<int>& num)
  5. {
  6. return helper(num,,num.size()-);
  7. }
  8. private:
  9. TreeNode* helper(vector<int> &num,int start,int end)
  10. {
  11. if(start>end) return NULL;
  12. int mid=start+(end-start)/;
  13. TreeNode* root=new TreeNode(num[mid]);
  14. root->left=helper(num,start,mid-);
  15. root->right=helper(num,mid+,end);
  16. return root;
  17. }
  18. };

值得注意的是,如果把helper参数列表里的vector<int> &num的引用去掉,变成vector<int> num, 那么会发生TLE. 所以在用vector之类的容器时加引用还是很重要的。

2. Convert Sorted List to Binary Search Tree

  1. class Solution
  2. {
  3. public:
  4. TreeNode* sortedListToBST(ListNode* head)
  5. {
  6. int count=calLen(head);
  7. return helper(head,,count-);
  8. }
  9. private:
  10. int calLen(ListNode* head)
  11. {
  12. int count=;
  13. while(head!=NULL)
  14. {
  15. head=head->next;
  16. count++;
  17. }
  18. return count;
  19. }
  20. TreeNode* helper(ListNode* head, int start, int end)
  21. {
  22. if(start>end) return NULL;
  23. int mid=start+(end-start)/;
  24. ListNode *p=head;
  25. for(int i=start;i<mid;i++)
  26. p=p->next;
  27. TreeNode* root=new TreeNode(p->val);
  28. root->left=helper(head,start,mid-);
  29. root->right=helper(p->next,mid+,end);
  30. return root;
  31. }
  32. };

注意细节问题。

ref:remlostime

七、Balanced Binary Tree

code 1: Divide & Conquer

  1. class Solution {
  2. public:
  3. bool isBalanced(TreeNode* root) {
  4. return maxDepth(root) != -;
  5. }
  6. int maxDepth(TreeNode* root) {
  7. if (root == NULL) {
  8. return ;
  9. }
  10. int left = maxDepth(root->left);
  11. int right = maxDepth(root->right);
  12. if (left == - || right == - || abs(left - right) > ) {
  13. return -;
  14. }
  15. return max(left, right) + ;
  16. }
  17. };

code 2:

  1. class Solution
  2. {
  3. public:
  4. bool isBalanced(TreeNode* root)
  5. {
  6. if(!root) return true;
  7. return (isBalanced(root->left) && isBalanced(root->right) && balancedNode(root));
  8. }
  9. private:
  10. bool balancedNode(TreeNode* root)
  11. {
  12. int dif=height(root->left)-height(root->right);
  13. if(dif>=- && dif<=) return true;
  14. else return false;
  15. }
  16. int height(TreeNode* root)
  17. {
  18. if(root==NULL) return ;
  19. return(max(height(root->left),height(root->right))+);
  20. }
  21. };

八、

1. Sum Root to Leaf Numbers

非递归版本:

  1. class Solution
  2. {
  3. public:
  4. int sumNumbers(TreeNode* root)
  5. {
  6. if(root==NULL) return ;
  7. postorderTraversal(root);
  8. return result;
  9. }
  10. private:
  11. int result=;
  12. void postorderTraversal(TreeNode* root)
  13. {
  14. stack<pair<TreeNode*,int>> s;
  15. s.push(make_pair(root,));
  16. string str;
  17. while(!s.empty())
  18. {
  19. TreeNode* now=s.top().first;
  20. if(now==NULL) s.pop();
  21. else
  22. {
  23. switch(s.top().second++)
  24. {
  25. case :
  26. str+=to_string(now->val);
  27. s.push(make_pair(now->left,));
  28. break;
  29. case :
  30. s.push(make_pair(now->right,));
  31. break;
  32. case :
  33. s.pop();
  34. if(!now->left && !now->right)
  35. result+=stoi(str);
  36. str.resize(str.size()-);
  37. break;
  38.  
  39. }
  40. }
  41. }
  42. }
  43. };

利用后序遍历,在每次第3次遇到一个节点(case:2) 且该节点为叶子节点时,使result累加上此时过往路径对应的整数(其实这里的过往路径相当于遍历一遍当前栈内元素)。

递归版本还没理解好,先放在这。

  1. class Solution {
  2. public:
  3. int sumNumbers(TreeNode *root) {
  4. return dfs(root, );
  5. }
  6. private:
  7. int dfs(TreeNode *root, int sum) {
  8. if (root == nullptr) return ;
  9. if (root->left == nullptr && root->right == nullptr)
  10. return sum * + root->val;
  11. return dfs(root->left, sum* + root->val) + dfs(root->right, sum* + root->val);
  12. }
  13. };

2. Path Sum

非递归版本:(和上题类似,只不过计算path sum的方式不同。)

  1. class Solution
  2. {
  3. public:
  4. bool hasPathSum(TreeNode* root,int sum)
  5. {
  6. return postorderTraversal(root,sum);
  7. }
  8. private:
  9. int tmp=;
  10. bool postorderTraversal(TreeNode* root,int target)
  11. {
  12. stack<pair<TreeNode*,int>> s;
  13. s.push(make_pair(root,));
  14. string str;
  15. while(!s.empty())
  16. {
  17. TreeNode* now=s.top().first;
  18. if(now==NULL)
  19. s.pop();
  20. else
  21. {
  22. switch(s.top().second++)
  23. {
  24. case :
  25. tmp+=now->val;
  26. s.push(make_pair(now->left,));
  27. break;
  28. case :
  29. s.push(make_pair(now->right,));
  30. break;
  31. case :
  32. s.pop();
  33. if(!now->left && !now->right)
  34. if(tmp==target)
  35. return true;
  36. tmp-=now->val;
  37. break;
  38. }
  39. }
  40. }
  41. return false;
  42. }
  43. };

 递归版本:(再理解一下)

  1. class Solution {
  2. public:
  3. bool hasPathSum(TreeNode *root, int sum) {
  4. if (root == NULL) return false;
  5. if (root->left == NULL && root->right == NULL) // leaf
  6. return sum == root->val;
  7. return hasPathSum(root->left, sum - root->val) || hasPathSum(root->right, sum - root->val);
  8. }
  9. };

写递归程序主要就是看递归出口以及如何缩小规模变成一个完全一样只是规模略小的子问题。

像这里就是由求root为根的一颗子树的hasPathSum转换为求分别以root->left和root->right为根的两颗子树的hasPathSum,其中target相应的减去root->val。

递归出口就是对于leaf节点(左右子树都为空)和NULL节点的处理情况。

update 8.10

  1. bool hasPathSum(TreeNode* root, int sum) {
  2. if (root == NULL) {
  3. return false;
  4. }
  5. if (root->left == NULL && root->right == NULL && root->val == sum) {
  6. return true;
  7. }
  8. return (hasPathSum(root->left, sum - root->val) || hasPathSum(root->right, sum - root->val));
  9. }

递归-模板化版本:(personal style)

  1. class Solution
  2. {
  3. public:
  4. bool hasPathSum(TreeNode* root,int sum)
  5. {
  6. return helper(root,sum);
  7. }
  8. bool helper(TreeNode* root,int target)
  9. {
  10. if(root==NULL) return false;
  11. if(root->left==NULL && root->right==NULL)
  12. return root->val==target;
  13. return helper(root->left,target-root->val)||helper(root->right,target-root->val);
  14. }
  15. };

3. Path Sum II

非递归版本:(非递归用起来确实很方便,但是递归版本必须要熟练掌握)

  1. class Solution
  2. {
  3. public:
  4. vector<vector<int>> pathSum(TreeNode* root,int sum)
  5. {
  6. vector<vector<int>> result;
  7. postorderTraversal(root,result,sum);
  8. return result;
  9. }
  10. void postorderTraversal(TreeNode* root, vector<vector<int>> &result,int target)
  11. {
  12. stack<pair<TreeNode*,int>> s;
  13. s.push(make_pair(root,));
  14. vector<int> tmp;
  15. int sum=;
  16. while(!s.empty())
  17. {
  18. TreeNode* now=s.top().first;
  19. if(now==NULL) s.pop();
  20. else
  21. {
  22. switch(s.top().second++)
  23. {
  24. case :
  25. tmp.push_back(now->val);
  26. sum+=now->val;
  27. s.push(make_pair(now->left,));
  28. break;
  29. case :
  30. s.push(make_pair(now->right,));
  31. break;
  32. case :
  33. s.pop();
  34. if(!now->left&&!now->right)
  35. {
  36. if(sum==target)
  37. result.push_back(tmp);
  38. }
  39. sum-=now->val;
  40. tmp.pop_back();
  41. break;
  42. }
  43. }
  44. }
  45. }
  46. };

递归版本:

  1. class Solution {
  2. public:
  3. vector<vector<int>> pathSum(TreeNode* root,int sum)
  4. {
  5. vector<vector<int>> result;
  6. helper(result,root,sum);
  7. return result;
  8. }
  9. void helper(vector<vector<int>> &result,TreeNode* root,int target)
  10. {
  11. if(root==NULL) return;
  12. tmp.push_back(root->val);
  13. if(root->left==NULL && root->right==NULL)
  14. {
  15. if(root->val==target)
  16. result.push_back(tmp);
  17. }
  18. helper(result,root->left,target-root->val);
  19. helper(result,root->right,target-root->val);
  20. tmp.pop_back();
  21. }
  22. vector<int> tmp;
  23. };

九、Flatten Binary Tree to Linked List

这道题目要求有说in-place, 怎么样算in-place?

非递归版本:

  1. class Solution
  2. {
  3. public:
  4. void flatten(TreeNode* root)
  5. {
  6. preorderTraversal(root);
  7. }
  8. void preorderTraversal(TreeNode* root)
  9. {
  10. if(root==NULL) return;
  11. stack<TreeNode*> s;
  12. s.push(root);
  13. while(!s.empty())
  14. {
  15. TreeNode* now=s.top();
  16. s.pop();
  17. if(now!=NULL)
  18. {//必须要确保压入栈的只能是非NULL的,否则会影响到now->right的指向。now->right只能指向节点,不能是NULL(除非是最后一个节点)。
  19. if(now->right!=NULL)
  20. s.push(now->right);
  21. if(now->left!=NULL)
  22. s.push(now->left);
  23. now->left=NULL;
  24. if(!s.empty())//检查一下是否栈非空,因为要用到s.top().(毕竟上次判断完非空后有pop操作)
  25. now->right=s.top();
  26. }
  27. }
  28. }
  29. };

非递归版本在先序遍历的基础上有一些细节改动,主要是需要确保入栈的只能是非NULL节点。

递归版本:

一开始没有思路,后来看了code ganker的讲解和该博客的代码,原来只需要保存一个pre即可。

  1. class Solution
  2. {
  3. public:
  4. void flatten(TreeNode* root)
  5. {
  6. helper(root,NULL);
  7. }
  8. TreeNode* helper(TreeNode* root,TreeNode* pre)
  9. {
  10. if(root==NULL) return pre;
  11. if(pre!=NULL)
  12. pre->right=root;
  13. pre=root;//每访问一个节点,pre就指向该节点。
  14. TreeNode* left=root->left;
  15. root->left=NULL;
  16. TreeNode* right=root->right;
  17. pre=helper(left,pre);
  18. pre=helper(right,pre);
  19. return pre;
  20. }
  21. };

本质上是在前序遍历的递归代码基础上改的。每遍历一个节点,pre都会更新并指向该节点。所以倒数第3行中pre=helper(left,pre); 执行完后,pre的值即为先序遍历root的左子树的最后一个点。这个需要注意一下,加深理解。

十、

1. Construct Binary Tree from Preorder and Inorder Traversal

  1. class Solution
  2. {
  3. public:
  4. TreeNode* buildTree(vector<int> &preorder,vector<int> &inorder)
  5. {
  6. int len=preorder.size();
  7. return helper(preorder,,len-,inorder,,len-);
  8. }
  9. TreeNode* helper(vector<int> &preorder,int s1,int e1,vector<int>& inorder,int s2,int e2)
  10. {
  11. if(s1>e1||s2>e2) return NULL;
  12. TreeNode* node=new TreeNode(preorder[s1]);
  13. int index=find(inorder,node->val);
  14. node->left=helper(preorder,s1+,index-s2+s1,inorder,s2,index-);
  15. node->right=helper(preorder,index-s2+s1+,e1,inorder,index+,e2);
  16. return node;
  17. }
  18. int find(vector<int> &inorder,int x)
  19. {
  20. for(int i=;i<inorder.size();i++)
  21. {
  22. if(inorder[i]==x)
  23. return i;
  24. }
  25. return -;
  26. }
  27. };

需要注意Line14,15的递归下标别写错。

2. Construct Binary Tree from Inorder and Postorder Traversal

  1. class Solution
  2. {
  3. public:
  4. TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder)
  5. {
  6. int len=inorder.size();
  7. return helper(inorder,,len-,postorder,,len-);
  8. }
  9. TreeNode* helper(vector<int> &inorder,int s1,int e1,vector<int> &postorder,int s2,int e2)
  10. {
  11. if(s1>e1||s2>e2) return NULL;//别忘了递归出口
  12. TreeNode* node=new TreeNode(postorder[e2]);
  13. int index=find(inorder,node->val);
  14. node->left=helper(inorder,s1,index-,postorder,s2,index--s1+s2);
  15. node->right=helper(inorder,index+,e1,postorder,index-s1+s2,e2-);
  16. return node;
  17. }
  18. int find(vector<int> &inorder,int x)
  19. {
  20. for(int i=;i<inorder.size();i++)
  21. if(inorder[i]==x) return i;
  22. return -;
  23. }
  24. };

写递归函数不要忘了写递归出口。

十一、Binary Tree Right Side View

用BFS非递归做的。但是代码有点繁琐。

而且,需要掌握一下递归做法。

  1. class Solution
  2. {
  3. public:
  4. vector<int> rightSideView(TreeNode* root)
  5. {
  6. vector<int> result;
  7. if(!root) return result;
  8. queue<pair<TreeNode*,int>> q;
  9. q.push(make_pair(root,));
  10. TreeNode* pre=root;
  11. int preLevel=;
  12. while(!q.empty())
  13. {
  14. TreeNode* now=q.front().first;
  15. if(preLevel!=q.front().second)
  16. result.push_back(pre->val);
  17. if(now->left==NULL&&now->right==NULL&&q.size()==)
  18. result.push_back(now->val);
  19. preLevel=q.front().second;
  20. pre=now;
  21. q.pop();
  22. if(now->left!=NULL)
  23. q.push(make_pair(now->left,preLevel+));
  24. if(now->right!=NULL)
  25. q.push(make_pair(now->right,preLevel+));
  26. }
  27. return result;
  28. }
  29. };

十二、Recover Binary Search Tree

递归版本:

  1. class Solution
  2. {
  3. public:
  4. void recoverTree(TreeNode* root)
  5. {
  6. helper(root);
  7. int tmp=p1->val;
  8. p1->val=p2->val;
  9. p2->val=tmp;
  10. }
  11. void helper(TreeNode* root)
  12. {
  13. if(root==NULL) return;
  14. helper(root->left);
  15. if(pre>root->val && flag==)
  16. {
  17. flag=;
  18. p1=prePtr;
  19. p2=root;
  20. }
  21. else if(pre>root->val && flag==)
  22. p2=root;
  23. pre=root->val;
  24. prePtr=root;
  25. helper(root->right);
  26. }
  27. int pre=INT_MIN;
  28. TreeNode* p1=NULL,*p2=NULL;
  29. TreeNode* prePtr=NULL;
  30. int flag=;
  31. };

一个重要的特性就是,在本应递增的序列里,如果有两个值x1,x2发生了互换,那么会有两种情况:

case1:两个值不相邻

那么第1次出现left>right的地方,这里的left即为x1.(因为它本应在后面,是大值,现在换到前面来必然导致它大于其右侧的数字)。应用pre指针记录下这个left对应节点。

第2次出现left>right的地方,这里的right即为x2.(同理)。

case2:两个值相邻。则只会出现1次left>right的地方。

所以,综合case1和case2,在第一次遇到left>right时就把left和right分别标记为p1,p2,这样不管会不会有第2次left>right都无所谓,当有第2次left>right时只需更新一下p2即可。

改天把非递归版本补上。

十三、Clone Graph

  1. class Solution
  2. {
  3. public:
  4. typedef UndirectedGraphNode UGNode;
  5. UndirectedGraphNode* cloneGraph(UndirectedGraphNode* node)
  6. {
  7. if(node==NULL) return NULL;
  8. return dfs(node);
  9. }
  10. UGNode* dfs(UGNode* node)
  11. {
  12. UGNode* p=new UGNode(node->label);
  13. umap[p->label]=p;//注意,这里对umap赋值只能是p不能是node,因为在下面加入p的邻居时会用到。
  14. for(int i=;i<node->neighbors.size();i++)
  15. {
  16. if(umap.find(node->neighbors[i]->label)==umap.end())
  17. p->neighbors.push_back(dfs(node->neighbors[i]));
  18. else
  19. p->neighbors.push_back(umap[node->neighbors[i]->label]);
  20. }
  21. return p;
  22. }
  23. unordered_map<int,UGNode*> umap;
  24. };

需要注意的是,在return时应该是return的新建的copy指针,而不是原来图中的指针。

十四、Number of Islands

简洁版:

  1. class Solution
  2. {
  3. public:
  4. int numIslands(vector<vector<char>> &grid)
  5. {
  6. if(grid.size()== || grid[].size()==) return ;
  7. m=grid.size();
  8. n=grid[].size();
  9. for(int i=;i<m;i++)
  10. {
  11. for(int j=;j<n;j++)
  12. {
  13. if(grid[i][j]=='')
  14. {
  15. count++;
  16. dfs(grid,i,j);
  17. }
  18. }
  19. }
  20. return count;
  21. }
  22. int count=;
  23. int m,n;
  24. void dfs(vector<vector<char>> &grid,int i,int j)
  25. {
  26. if(i< || j< || i>=m || j>=n) return;
  27. if(grid[i][j]!='') return;
  28. grid[i][j]='';
  29. dfs(grid,i+,j);
  30. dfs(grid,i-,j);
  31. dfs(grid,i,j+);
  32. dfs(grid,i,j-);
  33. }
  34. };

这里利用一个trick,就是访问过的'1'都改为’2‘,这样就不用再分配一个visited数组了,节省点空间。

啰嗦版,可无视之:

  1. class Solution
  2. {
  3. public:
  4. int numIslands(vector<vector<char>> &grid)
  5. {
  6. m=grid.size();
  7. if(m==) return ;
  8. n=grid[].size();
  9. visited=vector<vector<int>>(m,vector<int>(n,));
  10. sumOfOnes=calOnes(grid);
  11. for(int i=;i<m;i++)
  12. {
  13. for(int j=;j<n;j++)
  14. {
  15. if(visited[i][j]== && grid[i][j]=='')
  16. {
  17. count++;
  18. dfs(grid,i,j);
  19. }
  20. }
  21. }
  22. return count;
  23. }
  24. private:
  25. int m, n;
  26. int count=;
  27. int sumOfOnes=;
  28. int countOnes=;
  29. vector<vector<int>> visited;
  30. void dfs(vector<vector<char>> &matrix,int row,int col)
  31. {
  32. countOnes++;
  33. visited[row][col]=;
  34. if(countOnes>=sumOfOnes)
  35. return;
  36. if(row+<m && matrix[row+][col]=='' && !visited[row+][col])
  37. dfs(matrix,row+,col);
  38. if(col+<n && matrix[row][col+]=='' && !visited[row][col+])
  39. dfs(matrix,row,col+);
  40. if(row->= && matrix[row-][col]=='' && !visited[row-][col])
  41. dfs(matrix,row-,col);
  42. if(col->= && matrix[row][col-]=='' && !visited[row][col-])
  43. dfs(matrix,row,col-);
  44. }
  45. int calOnes(vector<vector<char>> &matrix)
  46. {
  47. int count=;
  48. for(int i=;i<m;i++)
  49. {
  50. for(int j=;j<n;j++)
  51. {
  52. if(matrix[i][j]=='')
  53. count++;
  54. }
  55. }
  56. return count;
  57. }
  58. };

一开始犯了很多小错误。比如,只向右和向下搜索,并以访问右下角元素作为递归出口。这是不对的,因为如果遍历的时候第一次遇到的1是第一行最后一个,那就只能遍历最后一列了。

参考别的答案后发现我的略繁琐,不简洁。

十五、Binary Tree Maximum Path Sum

code1: 分治  [new]

  1. class Solution {
  2. public:
  3. int maxPathSum(TreeNode* root) {
  4. int res = INT_MIN;
  5. maxPath(root, res);
  6. return res;
  7. }
  8. int maxPath(TreeNode* root, int &res) {
  9. if (root == NULL) {
  10. return ;
  11. }
  12. int left = max(, maxPath(root->left, res));
  13. int right = max(, maxPath(root->right, res));
  14. res = max(res, left + right + root->val);
  15. return max(left, right) + root->val;
  16. }
  17. };

该题利用一个特点:任何一个path都是先升、后降,一旦降了就不能再升了(只升或只降可以看作特例)。因此每个path都有一个最高点。

所以,函数maxPath(node)可以定义为最高点为node的情况下所能取到的最大和。[注意,这里也能体现一个情况,就是分治函数往往返回值就是结果,dfs函数往往返回值是void]

几个需要解释的点:

这个maxPath函数在Line15返回的是以root为最高点单边儿所能取到的最大值,即不带拱的(只能从root往左或只能从root往右,不能带拱)。

而Line14里 left + right + root->val 是指的以当前点为最高点的允许带拱的情况下有可能取到的最大值。

reference

//刚才写又忘了写递归出口了。小细节、corner case 很重要!!!

code2:

  1. class Solution
  2. {
  3. public:
  4. int maxPathSum(TreeNode* root)
  5. {
  6. dfs(root);
  7. return maxNum;
  8. }
  9. int maxNum=INT_MIN;
  10. int dfs(TreeNode* root)
  11. {//每个dfs里默认是求sum一定会带上root的
  12. if(root==NULL) return ;
  13. int l=dfs(root->left);
  14. int r=dfs(root->right);
  15. int sum=root->val;
  16. if(l>) sum+=l;
  17. if(r>) sum+=r;
  18. maxNum=max(maxNum,sum);
  19. return max(l,r)>?root->val+max(l,r):root->val;//须格外注意
  20. }
  21. };

refer to soulMach. 代码默认是每次执行dfs函数时都必须把该节点带上来算入sum。

在最后返回时需要注意,该节点是被其父节点调用的,所以返回的时候不能有分叉,即只能返回一个让父节点遍历下来时不会发生分叉的情况,因此不能让sum把left和right都加上,最多加一个。

十六、Validate Binary Search Tree

  1. class Solution
  2. {
  3. public:
  4. bool isValidBST(TreeNode* root)
  5. {
  6. inorder(root);
  7. if(flag==) return false;
  8. else return true;
  9. }
  10. private:
  11. void inorder(TreeNode* root)
  12. {
  13. if(root==NULL) return;
  14. inorder(root->left);
  15. if(!firstNode && pre>=root->val)
  16. flag=;
  17. firstNode=;
  18. pre=root->val;
  19. inorder(root->right);
  20. }
  21. int pre=INT_MIN;
  22. int flag=;
  23. int firstNode=;
  24. };

我这代码看起来并不美观。第一次写时掉进去一个很隐蔽的坑:第一个节点的value就是INT_MIN。由于过度rely on INT_MIN,导致这个test case过不了。后来就专门加了个flag:firstNode,使得对第一个节点不置flag=1。不过很不美观。

更美观的做法是不用一个int值的pre,改为用TreeNode* pre.这样可令其初值为NULL,作为对第一个节点的判断依据。代码可参考此

BST的充要条件是:中序遍历序列是升序序列。

--26 problems in all

============================================================================================================

树相关

backtracking相关

dfs相关

图的遍历相关

nowcoder related

cc150 related

leetcode Ch3-DFS & Backtracking I的更多相关文章

  1. Leetcode总结之Backtracking

    本文我们就Leetcode中的一个类型的题目backtracking进行一系列的总结和归纳.backtracking这个方法本质是建立在递归的基础上,不断尝试新的路径,这里关键是每次尝试完以后需要退回 ...

  2. leetcode 39 dfs leetcode 40 dfs

    leetcode 39 先排序,然后dfs 注意先整全局变量可以减少空间利用 class Solution { vector<vector<int>>ret; vector&l ...

  3. 【LeetCode】DFS 总结

    DFS(深度优先搜索) 常用来解决可达性的问题. 两个要点: 栈:用栈来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点.可以使用递归栈. 标记:和 BFS 一样同样需要对已经遍历过的节点进行 ...

  4. Leetcode题解 - DFS部分题目代码+思路(756、1034、1110、491、721、988)

    756. 金字塔转换矩阵 """ 学到的新知识: from collections import defaultditc可以帮我们初始化字典,不至于取到某个不存在的值的时 ...

  5. Leetcode题解 - DFS部分简单题目代码+思路(113、114、116、117、1020、494、576、688)

    这次接触到记忆化DFS,不过还需要多加练习 113. 路径总和 II - (根到叶子结点相关信息记录) """ 思路: 本题 = 根到叶子结点的路径记录 + 根到叶子结点 ...

  6. Leetcode 78. Subsets (backtracking) 90 subset

    using prev class Solution { List<List<Integer>> res = new ArrayList<List<Integer&g ...

  7. DFS、BFS和Backtracking模板

    区别与联系 区别 DFS多用于连通性问题因为其运行思想与人脑的思维很相似,故解决连通性问题更自然,采用递归,编写简便(但我个人不这样觉得...) DFS的常数时间开销会较少.所以对于一些能用DFS就能 ...

  8. leetcode@ [211] Add and Search Word - Data structure design

    https://leetcode.com/problems/add-and-search-word-data-structure-design/ 本题是在Trie树进行dfs+backtracking ...

  9. leetcode算法总结

    算法思想 二分查找 贪心思想 双指针 排序 快速选择 堆排序 桶排序 搜索 BFS DFS Backtracking 分治 动态规划 分割整数 矩阵路径 斐波那契数列 最长递增子序列 最长公共子系列 ...

  10. [LeetCode] 489. Robot Room Cleaner 扫地机器人

    Given a robot cleaner in a room modeled as a grid. Each cell in the grid can be empty or blocked. Th ...

随机推荐

  1. C#集合通论

    前言 写这篇文章的最初动力是来自于一次笔试经历.有一道笔试题大概是这样的:程序使用一个txt文件来存储操作记录.存储记录是多行字符串,每一行代表一次操作记录,格式如下:用户名+操作事项名称+操作时间. ...

  2. 基于libcurl实现REST风格http/https的get和post

    c/c++开发中经常要用到http/https协议,直接使用socket工作量很大,要是使用socket实现https,那更不可思议,开源的c/c++的http客户端框架,libcurl是首选,而且也 ...

  3. Nginx 反向代理(http转htpps,并支持80端口继续提交post请求)

    项目是一个web server + 多个client的形式,client由用户安装在自己的电脑上 由http升级为https后,我们通过在Nginx做了80端口重定向443的配置,使用户通过访问htt ...

  4. 【JVM调优系列】----CPU过高的分析与解决方案

    1.首先通过top命令查询当前进程所占cpu的一个比重

  5. springboot主从数据库

    是从springmvc的思路上来做的,主要就是配置主.从DataSource,再继承AbstractRoutingDataSource,重写determineCurrentLookupKey方法,通过 ...

  6. bzoj 4561: [JLoi2016]圆的异或并

    Description 在平面直角坐标系中给定N个圆.已知这些圆两两没有交点,即两圆的关系只存在相离和包含.求这些圆的异或面 积并.异或面积并为:当一片区域在奇数个圆内则计算其面积,当一片区域在偶数个 ...

  7. Fanvas是一个把swf转为html5 canvas动画的系统

      https://github.com/Tencent/Fanvas   使用方法:     代码: <!DOCTYPE html> <html> <head> ...

  8. iOS开源项目周报0302

    由OpenDigg 出品的iOS开源项目周报第十期来啦.我们的iOS开源周报集合了OpenDigg一周来新收录的优质的iOS开源项目,方便iOS开发人员便捷的找到自己需要的项目工具等.TodayMin ...

  9. [转]SAPUI5 (01) - OpenUI5环境搭建

    本文转自:http://blog.csdn.net/stone0823/article/details/53750094 版权声明:本文为博主原创文章,转载请注明出处:http://blog.csdn ...

  10. 使用 csc.exe 编译C#代码

    csc.exe是C#的编译器,可以将C#代码编译为IL中间语言代码(exe.dll),然后再通过.net framework中的clr内的JIT(即时编译器)将中间语言代码编译为机器语言,然后再由机器 ...