1. 矩阵中的路径

吃过晚饭,继续开始。算是一个新的专题:搜索与回溯算法。第七题,矩阵中的路径。读了题,发现应该是回溯法,尝试了好久,勉勉强强敲出来,很惊喜是时间快于百分之99.1的答案。

  1. bool myF(vector<vector<char>> &board, string &word, int wordIndex, int x, int y);
  2. bool exist(vector<vector<char>> &board, string word) {
  3. //遍历
  4. for (int i = 0; i < board.size(); ++i) {
  5. for (int j = 0; j < board[i].size(); ++j) {
  6. if (myF(board, word, 0, i, j))
  7. return true;
  8. }
  9. }
  10. return false;
  11. }
  12. bool myF(vector<vector<char>> &board, string &word, int wordIndex, int x, int y) {
  13. //当前位置的字母是否符合
  14. if (board[x][y] != word[wordIndex])
  15. return false;
  16. //最后一个也满足,成功
  17. if (word.size() - 1 == wordIndex)
  18. return true;
  19. //回溯用
  20. char temp = board[x][y];
  21. board[x][y] = 0;
  22. wordIndex++;
  23. //走的通
  24. if (
  25. (x > 0 && myF(board, word, wordIndex, x - 1, y)) ||
  26. (y > 0 && myF(board, word, wordIndex, x, y - 1)) ||
  27. (x < board.size() - 1 && myF(board, word, wordIndex, x + 1, y)) ||
  28. (y < board[0].size() - 1 && myF(board, word, wordIndex, x, y + 1))
  29. )
  30. return true;
  31. //走不通,回溯
  32. board[x][y] = temp;
  33. return false

2. 树的子结构

开始第八题:树的子结构。想法:首先遍历第一棵树,看看第二根数的根节点在不在第一棵树上面。如果在,第二步就是去看以这个点为根节点的树是否包括第二棵树,方法显而易见用回溯。
IDE优化,判断树是否为NULL时候,会提醒改为nullptr。
有点沮丧,通过48个中的47个!先贴代码,晚上回来看,去锻炼下身体。

  1. bool myF(TreeNode *A, TreeNode *B) {
  2. if (B == nullptr) return true;
  3. if (A == nullptr || A->val != B->val) return false;
  4. return myF(A->left, B->left) && myF(A->right, B->right);
  5. }
  6. bool isSubStructure(TreeNode *A, TreeNode *B) {
  7. if (A == nullptr || B == nullptr)
  8. return false;
  9. if (A->val == B->val)
  10. return myF(A, B);
  11. else
  12. return isSubStructure(A->left, B) || isSubStructure(A->right, B);

无语,终于,推倒按照思路重新来,并学了下各个解析写的,设定一个标记变量res以后重新开始,这次终于对了。

  1. bool myF2(TreeNode *A, TreeNode *B) {
  2. if (B == nullptr)
  3. return true;
  4. if (A == nullptr)
  5. return false;
  6. if (A->val != B->val)
  7. return false;
  8. return myF2(A->left, B->left) && myF2(A->right, B->right);
  9. }
  10. bool isSubStructure2(TreeNode *A, TreeNode *B) {
  11. bool res = false;
  12. if (A != nullptr && B != nullptr) {
  13. if (A->val == B->val)
  14. res = myF2(A, B);
  15. if (!res)
  16. res = isSubStructure(A->left, B);
  17. if (!res)
  18. res = isSubStructure(A->right, B);
  19. }
  20. return res;

3. 二叉树的镜像

晚饭后,继续。第九题:二叉树的镜像。这题简单,设置一个temp,递归!

  1. TreeNode* mirrorTree(TreeNode* root) {
  2. if (root == nullptr)
  3. return nullptr;
  4. TreeNode* temp = root->left;
  5. root->left = mirrorTree(root->right);
  6. root->right = mirrorTree(tmp);
  7. return root;
  8. }

4. 对称的二叉树

第十题:对称的二叉树。判断一棵树是不是对称二叉树。判断就是三点:节点值是否相等,节点左值是否等于节点右值,右值是否等于左值。用递归。很简单一个题,却费力很久,一个初始条件判断错误,导致提交后系统提示使用了空指针。还需多多练习

  1. bool myF(TreeNode *A, TreeNode *B) {
  2. if (A == nullptr && B != nullptr)
  3. return false;
  4. if (A != nullptr && B == nullptr)
  5. return false;
  6. if (A == nullptr && B == nullptr)
  7. return true;
  8. if (A->val != B->val)
  9. return false;
  10. return (myF(A->left, B->right) && myF(A->right, B->left));
  11. }
  12. bool isSymmetric(TreeNode *root) {
  13. if (root == nullptr)
  14. return true;
  15. return myF(root->right, root->left);

5. 从上到下打印二叉树

继续,第十一题:从上到下打印二叉树。这题数据结构基础题,用一个队列。很轻松,但中间有一个小失误,while判断反了,还是要心细。

  1. vector<int> levelOrder(TreeNode *root) {
  2. queue<TreeNode *> queue;
  3. vector<int>vector(0);
  4. if (root== nullptr)
  5. return vector;
  6. queue.push(root);
  7. while (!queue.empty()){
  8. TreeNode* node=queue.front();
  9. queue.pop();
  10. vector.push_back(node->val);
  11. if (node->left!= nullptr)
  12. queue.push(node->left);
  13. if (node->right!= nullptr)
  14. queue.push(node->right);
  15. }
  16. return vector;
  17. }

6. 从上到下打印二叉树2.0

继续,第十二题:从上到下打印二叉树2.0。这个比较难,按层打印。尝试了一早上,没做出来。看了解析,发现一个很好理解的方法,自己尝试但是没有实现,这个解析做出来了,用一个nextLevel变量和一个remaining变量来记录每一层的信息。看懂以后自己很快敲出来。

  1. vector<vector<int>> levelOrder(TreeNode *root) {
  2. queue<TreeNode *> queue;
  3. vector<vector<int>> vec;
  4. if (root == nullptr)
  5. return vec;
  6. queue.push(root);
  7. int nextLevel = 0;
  8. int remaining = 1;
  9. vector<int> temp;
  10. while (!queue.empty()) {
  11. TreeNode *node = queue.front();
  12. temp.push_back(node->val);
  13. queue.pop();
  14. remaining--;
  15. if (node->left) {
  16. queue.push(node->left);
  17. nextLevel++;
  18. }
  19. if (node->right) {
  20. queue.push(node->right);
  21. nextLevel++;
  22. }
  23. if (remaining == 0) {
  24. vec.push_back(temp);
  25. temp.clear();
  26. remaining = nextLevel;
  27. nextLevel = 0;
  28. }
  29. }
  30. return vec;
  31. }

7. 从上到下打印二叉树3.0

有点感觉,继续下一道!第十三题:从上到下打印二叉树3.0。这个也是按层打印,但是是蛇形打印。想设置一个bool的tag,循环true和false进行蛇形输出,第一次失败,情况是这样:
d3JsjP53dCXqe5uz__thumbnail.png
第二次也失败,看来这条路不通。
UAYo7rxt2fJcemYW__thumbnail.png
看了解析,发现不是路不通,方法可用,问题在于应该使用双端队列deque!百度deque用法后继续尝试。

  1. vector<vector<int>> levelOrder(TreeNode *root) {
  2. if (!root)
  3. return {};
  4. vector<vector<int> > res;
  5. vector<int> temp;
  6. deque<TreeNode *> store; // 使用 deque
  7. store.push_back(root);
  8. TreeNode *curr;
  9. bool tag = true;
  10. int remaining = 1, nextLevel = 0;
  11. while (!store.empty()) {
  12. if (tag) // 奇数层
  13. {
  14. curr = store.front();
  15. temp.push_back(curr->val);
  16. store.pop_front(); // 弹出顶部元素
  17. remaining--;
  18. if (curr->left) {
  19. store.push_back(curr->left); //从左往右向底部添加下一层
  20. nextLevel++;
  21. }
  22. if (curr->right) {
  23. store.push_back(curr->right); //从左往右向底部添加下一层
  24. nextLevel++;
  25. }
  26. } else // 偶数层
  27. {
  28. curr = store.back();
  29. temp.push_back(curr->val);
  30. store.pop_back(); // 弹出底部元素
  31. remaining--;
  32. if (curr->right) {
  33. store.push_front(curr->right); //从右往左向顶部添加
  34. nextLevel++;
  35. }
  36. if (curr->left) {
  37. store.push_front(curr->left); //从右往左向顶部添加
  38. nextLevel++;
  39. }
  40. }
  41. if (remaining == 0) {
  42. res.push_back(temp);
  43. temp.clear();
  44. remaining = nextLevel;
  45. nextLevel = 0;
  46. tag = !tag;
  47. }
  48. }
  49. return res;
  50. }

8. 二叉树中和为某一值的路径

应该是用深度优先算。上递归和回溯。试了一下,做不出来。翻看解析,其实和自己的思路差不多,自己没有将递归结束的条件写清楚。而且注意,如果遍历到一条路径的叶节点了,然后需要切换到下一条路径的叶节点(比如题目示例中,我们遍历完了 5-4-11-7,现在要从 7 切换到 右边的 2),我们需要先从 temp 中把 7 pop_back。这样才能保证切换了之后,temp里的值会是:5,4,11,2;而不是:5,4,11,7,2。

  1. class Solution {
  2. private:
  3. vector<vector<int> > res;
  4. vector<int> temp;
  5. public:
  6. vector<vector<int>> pathSum(TreeNode *root, int sum) {
  7. if (root == nullptr)
  8. return {};
  9. preorder(root, sum);
  10. return res;
  11. }
  12. void preorder(TreeNode *root, int sum) {
  13. if (root == nullptr)
  14. return;
  15. temp.push_back(root->val);
  16. sum = sum - root->val;
  17. if (sum == 0)
  18. res.push_back(temp);
  19. preorder(root->left, sum); // 左
  20. preorder(root->right, sum); // 右
  21. temp.pop_back(); // 函数退出之前先弹出当前节点
  22. }
  23. }

但是没有通过,感觉是题目错了?
aiYzc4GGvQb1ntSk__thumbnail.png

9. 字符串的排列

今天再接再厉,争取再啃下一题,第十五题:字符串的排列。这题出在这里,是要用到树和递归,但是考虑到会给出重复的字符,这样可以用减枝法来优化试试。失败了,发现自己枚举到时候没法写出来。看了好多解析,有一种自己还是比较认可的。用回溯和无序的set来做:将字符串分为两部分,第一部分为第一个字符,第二部分为后面所有字符。求整个字符串的全排列,可以看成两步,第一步求所有可能出现在第一个位置的字符,即把第一个字符和后面所有字符交换。第二步,固定第一个字符,求后面所有字符排列。比如字符串“abc”,第一部分为字符a,第二部分为“bc”,首先a在第一个字符,求字符串“bc”的所有全排列得到:abc和acb。然后交换a和b,b放在第一个字符,求”ac”所有全排列得到:bac和bca。还原a和b后再交换a和c,c放在第一个字符,求“ba”所有全排列得到:cba和cab。用了好久才理解,前面凭空想是完全处理不来递归和回溯的过程,必须在纸上以树状图画出来。而且对于重复元素的减枝法的处理也是点睛之笔。

  1. class Solution {
  2. public:
  3. vector<string> permutation(string s) {
  4. if (s.empty())
  5. return {};
  6. dfs(s, 0);
  7. return res;
  8. }
  9. private:
  10. vector<string> res;
  11. void dfs(string s, int x) {
  12. if(x == s.size() - 1) {
  13. res.push_back(s); // 加入res中
  14. return;
  15. }
  16. set<int> st;
  17. for(int i = x; i < s.size(); i++) {
  18. if(st.find(s[i]) != st.end())
  19. continue; // 重复
  20. st.insert(s[i]);
  21. swap(s[i], s[x]); // 交换,将 s[i] 固定在第 x 位
  22. dfs(s, x + 1); // 开启第 x + 1 位字符
  23. swap(s[i], s[x]); // 恢复交换
  24. }
  25. }
  26. };

10. 二叉搜素树的第k大节点

第十六题:二叉搜索树的第 k 大节点。而二叉搜索树的中序遍历为递增序列。按照右根左即可从大到小遍历,然后设置i记录第k大,二者相等即是答案。

  1. class Solution {
  2. public:
  3. int kthLargest(TreeNode *root, int k) {
  4. myF(root, k);
  5. return res;
  6. }
  7. private:
  8. int res, i;
  9. void myF(TreeNode *root, int k) {
  10. if (root == nullptr)
  11. return;
  12. myF(root->right, k);//右
  13. ++i;//计数
  14. if (i == k)//相等,把节点值保存并推出循环
  15. {
  16. res = root->val;
  17. return;
  18. }
  19. myF(root->left, k); //左
  20. }
  21. };