这里将6道backtracking的题目放在这里,backtracking是有一定技巧的,后面会总结
分别是:一: LeetCode39 Combination Sum 二:LeetCode40 Combination Sum II 三:leetcode22 Generate Parentheses 四:leetcode77 Combinations 五:leetcode 17 Letter Combinations of a Phone Number 六:leetcode78 Subsets 七:leetcode 51 N-Queens 八:leetcode 46 Permutations 九:leetcode47 Permutations II 十:leetcode79 Word Search
一: LeetCode39 Combination Sum
题目:
Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
The same repeated number may be chosen from C unlimited number of times.
Note:
For example, given candidate set 2,3,6,7
and target 7
,
A solution set is:
[7]
[2, 2, 3]
链接:https://leetcode.com/problems/combination-sum/
分析:此题刚开始想到的类似于背包问题,然而背包的话,打印结果比较困难,因此只能使用回溯。注意此题给的C是一个set,因此元素互不相同
class Solution { public: void dfs(int dep, int maxDep, vector<int> &cand, int target){ // 核心 if(target < 0) return; if(dep == maxDep){ if(target == 0){ vector<int> temp; for(int i = 0; i < maxDep; i++){ for(int j = 0; j < num[i]; j++) temp.push_back(cand[i]); } ret.push_back(temp); } return; } for(int i = 0; i <= target/cand[dep]; i++){ num[dep] = i; dfs(dep+1, maxDep, cand, target-i*cand[dep]); } } vector<vector<int> > combinationSum(vector<int> &candidates, int target) { int n = candidates.size(); if(n == 0) return ret; sort(candidates.begin(), candidates.end()); // 有序输出 num.resize(n); ret.clear(); dfs(0, n, candidates, target); return ret; } private: vector<vector<int> > ret; // 返回的结果 vector<int> num; // 用来存储每个数字的次数 };二:LeetCode40 Combination Sum II
题目:
Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
Each number in C may only be used once in the combination.
Note:
For example, given candidate set 10,1,2,7,6,1,5
and target 8
,
A solution set is:
[1, 7]
[1, 2, 5]
[2, 6]
[1, 1, 6]
链接:https://leetcode.com/problems/combination-sum-ii/
分析:此题是上一题的变形,只需对每个元素取0或者1次,但是出现的问题是集合中可能有重复的元素,因此需要该进,该进点在于当前元素与前一个元素相等时,只有当前一个元素取的时候,必须取当前元素
class Solution { public: void dfs(int dep, int maxDep, int target, vector<int> &cand){ if(target < 0) return; if(dep == maxDep){ if(target == 0){ vector<int> temp; for(int i = 0; i < maxDep; i++) { for(int j = 0; j < num[i]; j++) temp.push_back(cand[i]); } ret.push_back(temp); } return; } for(int i = 0; i <= 1; i++){ if(dep-1 >= 0 && cand[dep]==cand[dep-1] && num[dep-1] == 1)i = 1; //有相同元素,前面一个元素取到的时候,后面一个元素必须取 num[dep] = i; dfs(dep+1, maxDep, target-i*cand[dep], cand); } } vector<vector<int> > combinationSum2(vector<int> &candidates, int target) { int n = candidates.size(); if(n == 0) return ret; sort(candidates.begin(), candidates.end()); num.resize(n); ret.clear(); dfs(0, n, target, candidates); return ret; } private: vector<int> num; // 用来存储每个数字的次数 vector<vector<int> > ret; // // 返回的结果 };
三:lettcode 22 Generate Parentheses
题目:
Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.
For example, given n = 3, a solution set is:
"((()))", "(()())", "(())()", "()(())", "()()()"
分析:此题为backtracking,backtracking函数书写的一般规则:(1)函数参数一般要包括位置或者其它(如本题中的还可以剩余左括号个数及左边有多少个左括号没有关闭),这些都是为函数内容作为判断条件,要选择好。 (2)函数开头是函数终止条件(如本题中已经没有左括号可以使用了,故return),并将结束得到的一个结果(元素可以不取的话,则将临时结果变量作为函数的参数<如subset>,每个元素都要取一个结果,则作为类成员)放入result中 (3)就是函数的后半部分,为递归,函数该位置可以放那些元素(循环),每种元素放入后递归下一个位置的元素,当然包括参数的变化。
(1)参数:pos:迭代位置,共有2*n个元素,表示迭代到第几个位置了, left表示左边已经有多少个左括号没有关闭,当left==0时就不能迭代右括号了,,remain表示还可以加入左括号的个数,当remain==0的时候,就不能迭代左括号了。。(2)终止条件:当remain==0时,只能加入右括号了,并将这一结果加入最后的结果中 (3)每个位置都只能放入左右括号两种情况,分别取这两种情况,然后进行下一次迭代。
class Solution { public: void dfs(int pos, string &str, int left, int remain, int n){ // left表示左边有多少个(没有关闭 if(remain == 0){ // 左括号使用完毕 故全部为右括号 for(int i = pos; i < 2*n; i++) str[i] = ')'; result.push_back(str); return; } if(remain > 0){ // remain!=0 说明还可以使用左括号 str[pos] = '('; dfs(pos+1, str, left+1, remain-1, n); } if(left != 0){ // left!=0 表示左边还有左括号没有关闭,故可以使用) str[pos] = ')'; dfs(pos+1, str, left-1, remain, n); } } vector<string> generateParenthesis(int n) { string str; str.resize(2*n); dfs(0, str, 0, n, n); return result; } private: vector<string> result; };
四:leetcode 77 Combinations
题目:
Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.
For example,
If n = 4 and k = 2, a solution is:
[ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]
分析:亦即backtracking,(1)参数, 位置dep表示迭代到集合中第几个元素,count表示结果中已有的元素棵树(2)函数的终止条件,已经取了k个元素count==k,将得到的结果加入result中。(3)迭代,每个元素都有不取(只剩下k个元素了 必须取,就不执行)与取(加入临时结果中)两种情况,两种情况处理后分别迭代下一个位置。
class Solution { private: vector<vector<int> > result; vector<int> tmp; public: void dfs(int dep, const int &n, int k, int count){ if(count == k){ // 已选择元素等于k 故return result.push_back(tmp); return; } if(dep < n-k+count) // 当dep >= n-k+count时 元素必须加入进来了 dfs(dep+1, n, k, count); // 不选取该元素 tmp[count] = dep+1; // 选取了该元素 dfs(dep+1, n, k, count+1); // 遍历下一个元素 } vector<vector<int> > combine(int n, int k) { if(k > n) k = n; tmp.resize(k); dfs(0, n, k, 0); return result; } };
五:leetcode 17 Letter Combinations of a Phone Number
题目:
Given a digit string, return all possible letter combinations that the number could represent.
A mapping of digit to letters (just like on the telephone buttons) is given below.
Input:Digit string "23" Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].分析:每个数字所代表的该位置可以取其映射的字符串中的一个。(1)参数:dep表示迭代到了数字串的第几个位置,(2)终止条件:迭代的位置==maxDep,将得到的一个结果加入最后的result中,(3)每个数字映射的字符串都可以取一次,故for循环,取了每个元素后再迭代一个位置。
string NumToStr[10] = {"", "","abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; class Solution { public: int charToInt(char c){ int i; stringstream ss; ss << c; ss >> i; return i; } void dfs(int dep, int maxDep, const string &digits){ // dep为参数 if(dep == maxDep){ // 终止条件 result.push_back(str); } int index = charToInt(digits[dep]); //int index = digits[dep] - '0'; for(int i = 0; i < NumToStr[index].size(); i++){ // 递归迭代 str[dep] = NumToStr[index][i]; dfs(dep+1, maxDep, digits); } } vector<string> letterCombinations(string digits) { int n = digits.size(); if(n == 0) return result; str.resize(n); dfs(0, n, digits); return result; } private: string str; vector<string> result; };
六:leetcode78 Subsets
题目:
Given a set of distinct integers, S, return all possible subsets.
Note:
For example,
If S = [1,2,3]
, a solution is:
[ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]分析:此题就是求子集合。(1)参数:迭代的位置dep (2)终止条件,迭代结束,将中间结果加入最终结果 (3)递归,每次都有取或者不取该元素,两种情况都处理后再分别迭代下一个位置。当然此题也可用迭代和位操作进行解决,可以看我之前的 blog:http://blog.csdn.net/lu597203933/article/details/44902267
class Solution { public: void dfs(int dep, int maxDep, vector<int> &S, vector<int> tmp){ // dep第几个位置 maxDep一共位置 tmp存放中间的一个结果 if(dep == maxDep){ // 终止条件 result.push_back(tmp); // 将中间一个结果加入到最后结果中 return; } dfs(dep+1, maxDep, S, tmp); // 不取该元素进行迭代 tmp.push_back(S[dep]); // 取该元素 dfs(dep+1, maxDep, S, tmp); // 再迭代 } vector<vector<int> > subsets(vector<int> &S) { sort(S.begin(), S.end()); vector<int> tmp; dfs(0, S.size(), S, tmp); return result; } private: vector<vector<int> > result; };
七:leetcode 51 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:
[ [".Q..", // Solution 1 "...Q", "Q...", "..Q."], ["..Q.", // Solution 2 "Q...", "...Q", ".Q.."] ]分析:此题为常见的八皇后问题, 即任意两个皇后都不能处于同一行、同一列或同一斜线上,典型的回溯问题。代码中用一个visited来标记每次取时总共有哪些位置不能取。。
class Solution { public: void dfs(int dep, int maxDep){ if(dep == maxDep){ // 终止条件 vector<string> tmp; for(int i = 0; i < maxDep; i++){ string str(maxDep,'.'); str[tmpResult[i]] = 'Q'; tmp.push_back(str); } result.push_back(tmp); } vector<int> visited; visited.resize(maxDep); for(int i = 0; i < dep; i++){ // 当前有哪些位置不能取 visited[tmpResult[i]] = 1; if(tmpResult[i]+dep-i < maxDep)visited[tmpResult[i]+dep-i] = 1; if(tmpResult[i]-dep+i >= 0)visited[tmpResult[i]-dep+i] =1; } for(int i = 0; i < maxDep; i++){ // 取能去的位置 并对下一个位置进行回溯 if(!visited[i]){ tmpResult[dep] = i; dfs(dep+1, maxDep); } } } vector<vector<string> > solveNQueens(int n) { tmpResult.resize(n); dfs(0, n); return result; } private: vector<vector<string> > result; vector<int> tmpResult; };
八:leetcode 46 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]
.
分析:此题和上面的回溯没有什么区别,关键注意在每次for循环结束时,进行下次for循环要将该点设为未被访问过,不然会影响后面位置的结果
class Solution { public: void dfs(int dep, int maxDep, vector<int> &num, vector<int> visited){ if(dep == maxDep){ result.push_back(tmpResult); } for(int i = 0; i < num.size(); i++){ if(!visited[i]){ visited[i] = 1; tmpResult[dep] = num[i]; dfs(dep+1, maxDep, num, visited); visited[i] = 0; // for结束 应该标志为未访问了,不然会影响后面位置的结果 } } } vector<vector<int> > permute(vector<int> &num) { tmpResult.resize(num.size()); vector<int> visited; visited.resize(num.size()); dfs(0, num.size(), num, visited); return result; } private: vector<vector<int> > result; vector<int> tmpResult; };
题目:
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]
.
分析:此题要求元素可以重复,那么我们会发现对于每个位置第二次选数的时候必须不能和第一次的一样,当一样时我们就continue。。用了一个vector<int>count来记录第几次选数。。。。如 【1 1 1 3】,第一个位置第一次选择了1(i==0),那么for后面的每次循环(i=2,3...n-1)就不能选择1了。
class Solution { public: void dfs(int dep, int maxDep, vector<int> &num, vector<int> visited){ if(dep == maxDep){ result.push_back(tmpResult); return; } for(int i = 0; i < num.size(); i++){ if(i == 0)count[dep] = 0; if(!visited[i]){ count[dep]++; if(count[dep] > 1 && tmpResult[dep] == num[i]) continue; // 每个位置第二次选数时和第一次一样 则continue visited[i] = 1; // 每个位置第二次选择的数时和第一次不能一样 一样就continue tmpResult[dep] = num[i]; dfs(dep+1, maxDep, num, visited); visited[i] = 0; // for结束 应该标志为未访问了 } } } vector<vector<int> > permuteUnique(vector<int> &num) { sort(num.begin(), num.end()); tmpResult.resize(num.size()); count.resize(num.size()); vector<int> visited; visited.resize(num.size()); dfs(0, num.size(), num, visited); return result; } private: vector<vector<int> > result; vector<int> tmpResult; // 记录暂态结果 vector<int> count; // 每个位置第几次选数 };
十:leetcode79 Word Search
题目:
Given a 2D board and a word, find if the word exists in the grid.
The word can be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.
For example,
Given board =
[ ["ABCE"], ["SFCS"], ["ADEE"] ]word =
"ABCCED"
, -> returns
true
,
"SEE"
, -> returns
true
,
"ABCB"
, -> returns
false
.
链接:https://leetcode.com/problems/word-search/
分析:这题就是dfs的思想,需要回溯。
class Solution { public: bool dfs(int xi, int yi, string &word, int index, vector<vector<char> > &board, const int &m, const int &n, int **visited){ visited[xi][yi] = 1; // 该结点已经访问过了 if(index + 1 < word.size()){ if(xi-1 >= 0 && visited[xi-1][yi]==0 && board[xi-1][yi] == word[index+1]){ if(dfs(xi-1, yi, word, index+1, board, m, n, visited))return true; //深度遍历 visited[xi-1][yi] = 0; // 这条路行不通 设为未访问 以不影响下面的遍历 } if(xi+1 <m && visited[xi+1][yi]==0 && board[xi+1][yi] == word[index+1]){ if(dfs(xi+1, yi, word, index+1, board, m, n, visited))return true; visited[xi+1][yi] = 0; } if(yi-1 >= 0 && visited[xi][yi-1]==0 && board[xi][yi-1] == word[index+1]){ if(dfs(xi, yi-1, word, index+1, board, m, n,visited)) return true; visited[xi][yi-1] = 0; } if(yi+1 < n && visited[xi][yi+1]==0 && board[xi][yi+1] == word[index+1]){ if(dfs(xi, yi+1, word, index+1, board, m, n,visited)) return true; visited[xi][yi+1] = 0; } return false; }else return true; } void initVisited(int ** visited, const int &m, const int &n){ for(int i = 0; i < m; i++) memset(visited[i], 0, sizeof(int)*n); } bool exist(vector<vector<char> > &board, string word) { int m = board.size(); int n = board[0].size(); int **visited = new int*[m]; for(int i = 0; i < m; i++) visited[i] = new int[n]; for(int i = 0; i < m; i++){ // 找到其实的i和j for(int j = 0; j < n; j++){ if(word[0] == board[i][j]){ initVisited(visited, m, n); if(dfs(i, j, word, 0, board, m, n,visited)) return true; } } } for(int i = 0; i < m; i++) delete []visited[i]; delete []visited; return false; } };