1. Palindrome Partitioning

https://leetcode.com/problems/palindrome-partitioning/

Given a string s, partition s such that every substring of the partition is a palindrome.

Return all possible palindrome partitioning of s.

For example, given s = "aab",
Return

  1. [
  2. ["aa","b"],
  3. ["a","a","b"]
  4. ]
  1. /**
  2. * author : Jianxin Zhou
  3. * email:zhoujx0219@163.com
  4. *
  5. * 该题dfs函数原型如下:
  6. * void partitionHelper(const string &s, vector<vector<string>> &result, vector<string> &path, int pos)
  7. *
  8. * 以aaba举例。
  9. * 1. 首先a为回文,然后对aba进行dfs
  10. * 2. 之后回溯到a时,以aa为回文,然后对ba做dfs
  11. * 3. 回溯到aa,试图以aab为回文,失败;试图以aaba为回文失败;结束。
  12. *
  13. * 注意:如果能顺利的找到一组回文,那么pos最终会等于s.size(),此时可以push到result。
  14. * 如果找不到,例如之前的aaba不是回文,那么就会直接退出循环,没有机会执行下一步递归,也就没有pos等于s.size了。
  15. *
  16. * 实际上,此类题与真正的dfs的差别在于,dfs在回溯时,不会进行剪枝操作。而此类题,由于需要求出所有方案,所以需要剪枝。
  17. *
  18. */
  19.  
  20. class Solution {
  21. public:
  22. vector<vector<string>> partition(string s) {
  23. vector<vector<string>> result;
  24. vector<string> path;
  25. partitionHelper(s, result, path, 0);
  26. return result;
  27. }
  28.  
  29. private:
  30. void partitionHelper(const string &s, vector<vector<string>> &result, vector<string> &path, int pos) {
  31. // base case
  32. if (pos == s.size()) {
  33. result.push_back(path);
  34. return;
  35. }
  36.  
  37. for (int i = pos; i < s.size(); i++) {
  38. if (isPalindrome(s, pos, i)) {
  39. path.push_back(s.substr(pos, i - pos + 1));
  40. partitionHelper(s, result, path, i + 1);
  41. path.pop_back();
  42. }
  43. }
  44. }
  45.  
  46. bool isPalindrome(const string &s, int start, int end) {
  47. while (start < end) {
  48. if (s[start] == s[end]) {
  49. start++;
  50. end--;
  51. } else {
  52. break;
  53. }
  54. }
  55.  
  56. return start >= end;
  57. }
  58. };

2. Permutations

https://leetcode.com/problems/permutations/

Given a collection of numbers, return all possible permutations.

For example,

[1,2,3] have the following permutations:

[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], and [3,2,1].

具体可参加我之前写的文章:[LintCode] Permutations

  1. /**
  2. * 思路:dfs。
  3. *
  4. * 以123举例,
  5. * 1. 首先以1作为head,然后对23做dfs
  6. * 2. 回溯到1, 以2作为head,对13做dfs
  7. * 3. 最后回溯到2,以3作为head,对12做dfs
  8. *
  9. * 注意:例如以2为head,对其余元素做dfs时,那么2不能再取,因此在进行下一轮dfs时,需要标记2为以访问过
  10. *
  11. */
  12.  
  13. class Solution {
  14. public:
  15. vector<vector<int>> permute(vector<int>& nums) {
  16. vector<vector<int>> result;
  17. vector<int> path;
  18.  
  19. bool visited[nums.size()];
  20. for(int i = 0; i < nums.size(); i++) {
  21. visited[i] = false;
  22. }
  23.  
  24. sort(nums.begin(), nums.end());
  25. dfs(nums, result, path, visited);
  26. return result;
  27. }
  28.  
  29. private:
  30. void dfs(const vector<int> &nums, vector<vector<int>> &result, vector<int> &path, bool visited[]) {
  31. // base case
  32. if (path.size() == nums.size()) {
  33. result.push_back(path);
  34. return;
  35. }
  36.  
  37. for (int i = 0; i < nums.size(); i++) {
  38. if (visited[i] == false) {
  39. path.push_back(nums[i]);
  40. visited[i] = true;
  41. dfs(nums, result, path, visited);
  42. path.pop_back();
  43. visited[i] = false;
  44. }
  45.  
  46. }
  47. }
  48. };

3. Permutations II

https://leetcode.com/problems/permutations-ii/

Given a collection of numbers that might contain duplicates, return all possible unique permutations.

For example,

[1,1,2] have the following unique permutations:

[1,1,2], [1,2,1], and [2,1,1].

要点在于保证相同的数不在同一位置出现两次以上,可以参见我写的这篇文章:[LintCode] Permutations II

  1. class Solution {
  2. public:
  3. /**
  4. * @param nums: A list of integers.
  5. * @return: A list of unique permutations.
  6. */
  7. vector<vector<int> > permuteUnique(vector<int> &nums) {
  8. // write your code here
  9. vector<vector<int>> paths;
  10. if (nums.empty()) {
  11. return paths;
  12. }
  13.  
  14. sort(nums.begin(), nums.end());
  15. bool *visited = new bool[nums.size()]();
  16. vector<int> path;
  17. permuteUniqueHelper(nums, visited, path, paths);
  18. return paths;
  19. }
  20.  
  21. private:
  22. void permuteUniqueHelper(const vector<int> &nums,
  23. bool visited[],
  24. vector<int> &path,
  25. vector<vector<int>> &paths) {
  26. if (path.size() == nums.size()) {
  27. paths.push_back(path);
  28. return;
  29. }
  30.  
  31. for (int ix = 0; ix < nums.size(); ix++) {
  32. if (visited[ix] == true || ix > 0 && nums[ix - 1] == nums[ix] && visited[ix - 1] == false) {
  33. continue;
  34. }
  35.  
  36. visited[ix] = true;
  37. path.push_back(nums[ix]);
  38. permuteUniqueHelper(nums, visited, path, paths);
  39. visited[ix] = false;
  40. path.pop_back();
  41. }
  42. }
  43. };

4 Subsets

https://leetcode.com/problems/subsets/

Given a set of distinct integers, nums, return all possible subsets.

Note:

  • Elements in a subset must be in non-descending order.
  • The solution set must not contain duplicate subsets.

For example,

If nums = [1,2,3], a solution is:

  1. [
  2. [3],
  3. [1],
  4. [2],
  5. [1,2,3],
  6. [1,3],
  7. [2,3],
  8. [1,2],
  9. []
  10. ]
  1. /**
  2. * 思路:找方案,一般都是使用搜索。
  3. *
  4. * 以123为例,在递归还没有开始前,先把空集push到result中,之后:
  5. * 1. 以1位head,对23做dfs,所以pos需要加1,用于分支限界(1 12 13 123)
  6. * 2. 回溯到1,以2为head,对3做dfs (2 23)
  7. * 3. 回溯到3,以3为head,之后循环结束。 (3)
  8. *
  9. *
  10. */
  11.  
  12. class Solution {
  13. public:
  14. vector<vector<int>> subsets(vector<int>& nums) {
  15. // ensure that elements in a subset must be in non-descending order.
  16. sort(nums.begin(), nums.end());
  17.  
  18. vector<vector<int>> res;
  19. vector<int> path;
  20. dfs(nums, res, path, 0);
  21. return res;
  22. }
  23.  
  24. private:
  25. void dfs(const vector<int> &nums, vector<vector<int>> &res, vector<int> &path, int pos) {
  26. res.push_back(path);
  27.  
  28. for (int i = pos; i < nums.size(); i++) {
  29. path.push_back(nums[i]);
  30. dfs(nums, res, path, i + 1);
  31. path.pop_back();
  32. }
  33. }
  34. };

5. Subsets II

https://leetcode.com/problems/subsets-ii/

Given a collection of integers that might contain duplicates, nums, return all possible subsets.

Note:

  • Elements in a subset must be in non-descending order.
  • The solution set must not contain duplicate subsets.

For example,

If nums = [1,2,2], a solution is:

  1. [
  2. [2],
  3. [1],
  4. [1,2,2],
  5. [2,2],
  6. [1,2],
  7. []
  8. ]

同一位置上,前面取过的数,后面就不要再重复取了,当然当i = pos时,这个数必然是第一次取。

  1. class Solution {
  2. public:
  3. vector<vector<int>> subsetsWithDup(vector<int> &nums) {
  4. sort(nums.begin(), nums.end());
  5. vector<vector<int>> res;
  6. vector<int> path;
  7. dfs(nums, res, path, 0);
  8. return res;
  9. }
  10.  
  11. private:
  12. void dfs(const vector<int> &nums, vector<vector<int>> &res, vector<int> &path, int pos) {
  13. res.push_back(path);
  14.  
  15. for (int i = pos; i < nums.size(); i++) {
  16. if (i != pos && nums[i] == nums[i - 1]) {
  17. continue;
  18. }
  19.  
  20. path.push_back(nums[i]);
  21. dfs(nums, res, path, i + 1);
  22. path.pop_back();
  23. }
  24. }
  25. };

6 Restore IP Addresses

https://leetcode.com/problems/restore-ip-addresses/

Given a string containing only digits, restore it by returning all possible valid IP address combinations.

For example:

Given "25525511135",

return ["255.255.11.135", "255.255.111.35"]. (Order does not matter)

  1. /**
  2. * 该题思路与求回文划分相似
  3. */
  4.  
  5. class Solution {
  6. public:
  7. vector<string> restoreIpAddresses(string s) {
  8. vector<string> res;
  9.  
  10. size_t len = s.size();
  11. if (len < 4 || len > 12) {
  12. return res;
  13. }
  14.  
  15. vector<string> path;
  16. dfs(s, res, path, 0);
  17. return res;
  18. }
  19.  
  20. private:
  21. void dfs(const string &s, vector<string> &res, vector<string> &path, int pos) {
  22. // base case
  23. if (path.size() == 4) {
  24. if (pos != s.size()) {
  25. return;
  26. }
  27.  
  28. string returnElem;
  29. for (const auto &elem : path) {
  30. returnElem += elem;
  31. returnElem += ".";
  32. }
  33. returnElem.erase(returnElem.end() - 1);
  34.  
  35. res.push_back(returnElem);
  36. return;
  37. }
  38.  
  39. for (int i = pos; i < s.size() && i < pos + 3; i++) {
  40. string tmp = s.substr(pos, i - pos + 1);
  41. if (isValid(tmp)) {
  42. path.push_back(tmp);
  43. dfs(s, res, path, i + 1);
  44. path.pop_back();
  45. }
  46. }
  47. }
  48.  
  49. bool isValid(const string &s) {
  50. // 排除 055 之类的数字
  51. if (s[0] == '0' && s.size() > 1) {
  52. return false;
  53. }
  54.  
  55. int digit = atoi(s.c_str());
  56. return 0 <= digit && digit <= 255;
  57. }
  58. };

7 N-Queens

http://www.lintcode.com/en/problem/n-queens/#

The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.

Given an integer n, return all distinct solutions to the n-queens puzzle.

Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space respectively.

For example,

There exist two distinct solutions to the 4-queens puzzle:

  1. [
  2. [".Q..", // Solution 1
  3. "...Q",
  4. "Q...",
  5. "..Q."],
  6.  
  7. ["..Q.", // Solution 2
  8. "Q...",
  9. "...Q",
  10. ".Q.."]
  11. ]
  1. /**
  2. * 思路:一行一行的取数,例如第一行的皇后放在第1个位置,第二行的皇后放在第3个位置,
  3. * 以此类推,直到最后一行的皇后放在正确的位置,如此视为一个方案,push到result中
  4. *
  5. * 显然,本题使用dfs,每一行可取的位置从0-N-1,
  6. * 需要注意的是,每一行在取位置的时候,需要判断有效性(是否可以相互攻击)。
  7. */
  8.  
  9. class Solution {
  10. public:
  11. /**
  12. * Get all distinct N-Queen solutions
  13. * @param n: The number of queens
  14. * @return: All distinct solutions
  15. * For example, A string '...Q' shows a queen on forth position
  16. */
  17. vector<vector<string> > solveNQueens(int n) {
  18. vector<vector<string>> res;
  19. vector<int> visitedCol;
  20.  
  21. if (n <= 0) {
  22. return res;
  23. }
  24.  
  25. dfs(n, res, visitedCol);
  26. return res;
  27. }
  28.  
  29. private:
  30. void dfs(const int n, vector<vector<string>> &res, vector<int> &visitedCol) {
  31. // base case
  32. if (visitedCol.size() == n) {
  33. res.push_back(draw(visitedCol));
  34. return;
  35. }
  36.  
  37. for (int i = 0; i < n; i++) {
  38. if (!isValid(visitedCol, i)) {
  39. continue;
  40. }
  41.  
  42. visitedCol.push_back(i);
  43. dfs(n, res, visitedCol);
  44. visitedCol.pop_back();
  45. }
  46.  
  47. }
  48.  
  49. bool isValid(const vector<int> &visitedCol, const int currentCol) {
  50. size_t currentRow = visitedCol.size();
  51.  
  52. for (int rowIndex = 0; rowIndex < visitedCol.size(); rowIndex++) {
  53. if (currentCol == visitedCol[rowIndex]) {
  54. return false;
  55. }
  56.  
  57. if (currentRow + currentCol == rowIndex + visitedCol[rowIndex]) {
  58. return false;
  59. }
  60.  
  61. if (currentRow - currentCol == rowIndex - visitedCol[rowIndex]) {
  62. return false;
  63. }
  64. }
  65.  
  66. return true;
  67. }
  68.  
  69. vector<string> draw(const vector<int> &visitedCol) {
  70.  
  71. vector<string> ret;
  72. string row;
  73. for (const auto &elem : visitedCol) {
  74. row.clear();
  75.  
  76. for (int i = 0; i < visitedCol.size(); i++) {
  77. if (i == elem) {
  78. row += "Q";
  79. } else {
  80. row += ".";
  81. }
  82. }
  83.  
  84. ret.push_back(row);
  85. }
  86.  
  87. return ret;
  88. }
  89. };

8 Sudoku Solver

https://leetcode.com/problems/sudoku-solver/

Write a program to solve a Sudoku puzzle by filling the empty cells.

Empty cells are indicated by the character '.'.

You may assume that there will be only one unique solution.

A sudoku puzzle...

...and its solution numbers marked in red.

  1. class Solution {
  2. public:
  3. void solveSudoku(vector<vector<char>>& board) {
  4. dfs (board, 0, 0);
  5. }
  6.  
  7. private:
  8. /**
  9. * 该题需要对sudoku中每一个以‘.’标记的方格进行dfs,
  10. * 1. 如果对当前方格的以1-9这9个数字进行遍历,都不合法,那么不会再往下一个方格进行dfs,直接回溯到上一个方格取下一个数。
  11. * 2. 如果当前方格所取的数合法,那么继续对下一个方格进行dfs,依次下去如果一直合法,那么直到走到sudoku中的最后一个需要放数字的方格,
  12. * 尝试完它的所有选择,再往上回溯。
  13. * 然后,在这边我们只需要一个可行解即可,因此只要当前方格合法,往下的dfs返回true,那么即为一个解,直接返回。
  14. *
  15. *
  16. *
  17. */
  18. bool dfs(vector<vector<char>> &board, int x, int y) {
  19.  
  20. for (int i = 0; i < 9; i++) {
  21. for (int j = 0; j < 9; j++) {
  22. //dfs
  23. if (board[i][j] == '.') {
  24. // k从0-9走完才算走完,但是此处我们只要有一个解,就可以返回了,因此在以下循环中设置了return语句
  25. for (int k = 0; k < 9; k++) {
  26. bool flag;
  27.  
  28. if (!isValid(board, i ,j, k)) {
  29. continue;
  30. }
  31.  
  32. board[i][j] = '1' + k;
  33.  
  34. if (j != 8) {
  35. flag = dfs(board, i, j + 1);
  36. } else {
  37. flag = dfs(board, i + 1, 0);
  38. }
  39.  
  40. // 当前合法&&下一轮dfs合法,说明找到解
  41. if (flag) {
  42. return true;
  43. }
  44.  
  45. board[i][j] = '.';
  46. }
  47.  
  48. // 遍历完9个数,仍然找不到合适的解,则返回false
  49. return false;
  50. }
  51. }
  52. }
  53.  
  54. // 当所有各自都走完,自然返回true(注意只有当前合法,才会继续往下走,继续往下走的最终结果是越了sudoku的界限)
  55. return true;
  56.  
  57. }
  58.  
  59. bool isValid(const vector<vector<char>> &board, int x, int y, int k) {
  60. int i, j;
  61. for (i = 0; i < 9; i++) // 检查 y 列
  62. if (i != x && board[i][y] == '1' + k)
  63. return false;
  64.  
  65. for (j = 0; j < 9; j++) // 检查 x 行
  66. if (j != y && board[x][j] == '1' + k)
  67. return false;
  68.  
  69. for (i = 3 * (x / 3); i < 3 * (x / 3 + 1); i++)
  70. for (j = 3 * (y / 3); j < 3 * (y / 3 + 1); j++)
  71. if ((i != x || j != y) && board[i][j] == '1' + k)
  72. return false;
  73.  
  74. return true;
  75. }
  76. };
  1.  
  1.  

小结1

做搜索的题目,最关键的是要知道对什么对象进行dfs,例如,在sudoku中是对每一个以“.”标记的方格进行dfs,在回文划分中,是对每一个划分的位置进行dfs,在8妃问题中,是对每一行妃子可以在的位置进行dfs。

其次,dfs时,我们需要判断所取的每一个解是否是有效的,最好写一个函数来专门做这件事情。只要当当前对象dfs的数值有效时,才会继续往对下一个对象进行dfs,否则就直接向上回溯了(这点可以参见sudoku中的解释)。

最后,对于每次dfs时,可以对范围进行分支限界。例如回文划分、subset等。

小结2

值得注意的是:到底要对多少对象进行dfs,有时候是很明显的,例如8妃和sudoku问题,8妃就是对8行依次dfs,sudoku就是对所有方格进行dfs。但有时,总共要对多少对象进行dfs并不明显。dfs的递归基要处理的就是dfs完多少个对象就一定要返回(不然就无限dfs下去了)。当然,在sudoku问题中,方格的循环走完返回,这是一个隐含的递归基。

总结:dfs函数中,递归基处理的是dfs多少个对象就要返回。而每次dfs的for循环,往往是每一次dfs的范围。当递归栈最顶层的那个dfs循环走完,搜素就完成了。

小结3

在图论中,往往是从某一个点开始往下dfs,dfs的范围是当前node的所有neighbor,与我们通常的搜索问题不同的是,图论中的dfs在回溯时不会剪枝,总之,找到一条路径就结束了。

 

 

[算法专题] 深度优先搜索&回溯剪枝的更多相关文章

  1. Leetcode之深度优先搜索&回溯专题-679. 24 点游戏(24 Game)

    Leetcode之深度优先搜索&回溯专题-679. 24 点游戏(24 Game) 深度优先搜索的解题详细介绍,点击 你有 4 张写有 1 到 9 数字的牌.你需要判断是否能通过 *,/,+, ...

  2. Leetcode之深度优先搜索&回溯专题-491. 递增子序列(Increasing Subsequences)

    Leetcode之深度优先搜索&回溯专题-491. 递增子序列(Increasing Subsequences) 深度优先搜索的解题详细介绍,点击 给定一个整型数组, 你的任务是找到所有该数组 ...

  3. Leetcode之深度优先搜索&回溯专题-980. 不同路径 III(Unique Paths III)

    Leetcode之深度优先搜索&回溯专题-980. 不同路径 III(Unique Paths III) 深度优先搜索的解题详细介绍,点击 在二维网格 grid 上,有 4 种类型的方格: 1 ...

  4. Leetcode之深度优先搜索&回溯专题-638. 大礼包(Shopping Offers)

    Leetcode之深度优先搜索&回溯专题-638. 大礼包(Shopping Offers) 深度优先搜索的解题详细介绍,点击 在LeetCode商店中, 有许多在售的物品. 然而,也有一些大 ...

  5. 回溯算法 DFS深度优先搜索 (递归与非递归实现)

    回溯法是一种选优搜索法(试探法),被称为通用的解题方法,这种方法适用于解一些组合数相当大的问题.通过剪枝(约束+限界)可以大幅减少解决问题的计算量(搜索量). 基本思想 将n元问题P的状态空间E表示成 ...

  6. [算法入门]——深度优先搜索(DFS)

    深度优先搜索(DFS) 深度优先搜索叫DFS(Depth First Search).OK,那么什么是深度优先搜索呢?_? 样例: 举个例子,你在一个方格网络中,可以简单理解为我们的地图,要从A点到B ...

  7. 算法总结—深度优先搜索DFS

    深度优先搜索(DFS) 往往利用递归函数实现(隐式地使用栈). 深度优先从最开始的状态出发,遍历所有可以到达的状态.由此可以对所有的状态进行操作,或列举出所有的状态. 1.poj2386 Lake C ...

  8. [算法&数据结构]深度优先搜索(Depth First Search)

    深度优先 搜索(DFS, Depth First Search) 从一个顶点v出发,首先将v标记为已遍历的顶点,然后选择一个邻接于v的尚未遍历的顶点u,如果u不存在,本次搜素终止.如果u存在,那么从u ...

  9. 【2018.07.29】(深度优先搜索/回溯)学习DFS算法小记

    参考网站:https://blog.csdn.net/ldx19980108/article/details/76324307 这个网站里有动态图给我们体现BFS和DFS的区别:https://www ...

随机推荐

  1. 面向对象开发C++快速入门视频教程 C++基础加实战视频教程

    课程目录: ├<C++面向对象高级开发(上)> │ ├1.C++编程简介.mp4 │ ├2.头文件与类的声明.mp4 │ ├3.构造函数.mp4 │ ├4.参数传递与返回值.mp4 │ ├ ...

  2. JS StartMove源码-简单运动框架

    这几天学习js运动应用课程时,开始接触一个小例子:“仿Flash的图片轮换播放器”,其中使用的StartMove简单运动框架我觉得挺好用的.这个源码也简单,理解其原理,自己敲即便也就熟悉了. 用的时候 ...

  3. Install weblogic in silent mode

    使用静默(silent)模式来安装weblogic,在需要将安装脚本化,或无法使用图形界面的时候非常有用. 下面按照自己在实际工作中碰到的例子,来慢慢总结不同版本和平台weblogic的静默安装方法. ...

  4. Laravel5 (cli)命令行执行脚本及定时任务

    Artisan是Laravel自带的命令行接口名称,它提供了很多有用的命令想要查看所有可用的Artisan命令,可使用list命令查看: 1 php artisan list 每个命令都可以用help ...

  5. 无监督学习算法-Apriori进行关联分析

    关联分析 是无监督讯息算法中的一种,Apriori主要用来做_关联分析_,_关联分析_可以有两种形式:频繁项集或者关联规则.举个例子:交易订单 序号 商品名称 1 书籍,电脑 2 杯子,手机,手机壳, ...

  6. ubuntu6.4系统安装JIRA-7.8

    一.系统环境: system version:ubuntu6.4 openjdk version  (java版本) :1.8.0_191  mysql version:14.14 jira vers ...

  7. Linux下Mysql安装(tar安装)

    1.为数据库创建软件目录以及数据存放目录 #mysql软件目录 mkdir /software/ #mysql数据文件目录 mkdir /data/mysql 2.上传mysql-XXXXXX.tar ...

  8. HDU2028

    #include <bits/stdc++.h> using namespace std; ; int gcd(int a, int b) { ? b:gcd(b, a%b); } int ...

  9. Netty学习路线总结

    序 之前开过品味性能系列.Mysql学习系列,颇为曲高和寡.都是讲理论,很少有手把手深入浅出的文章.不过确实我就这脾气,文雅点的说法叫做"伪雅",下里巴人叫做"装逼&qu ...

  10. boost asio 学习(九) boost::asio 网络封装

    http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting- started-with-boostasio?pg=10 9. A ...