编写一个程序,通过已填充的空格来解决数独问题。
一个数独的解法需遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 '.' 表示。
给定的数独序列只包含数字 1-9 和字符 '.' 。
你可以假设给定的数独只有唯一解。
给定数独永远是 9x9 形式的
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sudoku-solver
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这个和leetcode51很像,但有不同点
class Solution {
public:
void solveSudoku(vector>& board) {
helper(board, 0, 0);
}
bool helper(vector>& board, int i, int j) {
if (i == 9) return true;
if (j >= 9) return helper(board, i + 1, 0);
if (board[i][j] != '.') return helper(board, i, j + 1);
for (char c = '1'; c <= '9'; ++c) {
if (!isValid(board, i , j, c)) continue;
board[i][j] = c;
if (helper(board, i, j + 1)) return true;
board[i][j] = '.';
}
return false;
}
bool isValid(vector>& board, int i, int j, char val) {
for (int x = 0; x < 9; ++x) {
if (board[x][j] == val) return false;
}
for (int y = 0; y < 9; ++y) {
if (board[i][y] == val) return false;
}
int row = i - i % 3, col = j - j % 3;
for (int x = 0; x < 3; ++x) {
for (int y = 0; y < 3; ++y) {
if (board[x + row][y + col] == val) return false;
}
}
return true;
}
};
1、发现没,它是有返回值的,因为他求得不是全部可行解,而是一个可行解即可,具体可看题目。所以通过boolean的返回值,控制可行解的个数。那又是如何控制的呢?
看这里
for (char c = '1'; c <= '9'; ++c) {
if (!isValid(board, i , j, c)) continue;
board[i][j] = c;
if (helper(board, i, j + 1)) return true;
board[i][j] = '.';
}
当在子选择中有一个是返回值为true,就直接返回return true;不在遍历其他子选择。
2、n皇后只需要每行只有一个皇后,即在列中选择一个子选择,这一行的选择就结束了;但这道题不是的,每行都有若干需要确定的“皇后”,因此行列都有遍历,当j>=9遍历下一行,当i==9就结束。可以学习一下对于这种二维数组应该如何应对
同类只求一个解即可的题
给定一个二维网格和一个单词,找出该单词是否存在于网格中。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例:
board =
[
['A','B','C','E'],
['S','F','C','S'],
['A','D','E','E']
]
给定 word = "ABCCED", 返回 true.
给定 word = "SEE", 返回 true.
给定 word = "ABCB", 返回 false
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/word-search
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
原二维数组就像是一个迷宫,可以上下左右四个方向行走,我们以二维数组中每一个数都作为起点和给定字符串做匹配,我们还需要一个和原数组等大小的 visited 数组,是 bool 型的,用来记录当前位置是否已经被访问过,因为题目要求一个 cell 只能被访问一次。如果二维数组 board 的当前字符和目标字符串 word 对应的字符相等,则对其上下左右四个邻字符分别调用 DFS 的递归函数,只要有一个返回 true,那么就表示可以找到对应的字符串,否则就不能找到
class Solution {
public:
bool exist(vector>& board, string word) {
if (board.empty() || board[0].empty()) return false;
int m = board.size(), n = board[0].size();
vector> visited(m, vector(n));
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (search(board, word, 0, i, j, visited)) return true;
}
}
return false;
}
bool search(vector>& board, string word, int idx, int i, int j, vector>& visited) {
if (idx == word.size()) return true;
int m = board.size(), n = board[0].size();
if (i < 0 || j < 0 || i >= m || j >= n || visited[i][j] || board[i][j] != word[idx]) return false;
visited[i][j] = true;
bool res = search(board, word, idx + 1, i - 1, j, visited)
|| search(board, word, idx + 1, i + 1, j, visited)
|| search(board, word, idx + 1, i, j - 1, visited)
|| search(board, word, idx + 1, i, j + 1, visited);
visited[i][j] = false;
return res;
}
};
特点:
1、由于只要一个满足即可,故这种形式很机智
bool res = search(board, word, idx + 1, i - 1, j, visited)
|| search(board, word, idx + 1, i + 1, j, visited)
|| search(board, word, idx + 1, i, j - 1, visited)
|| search(board, word, idx + 1, i, j + 1, visited);
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combination-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
public:
vector> combinationSum(vector& candidates, int target) {
vector> res;
vector out;
combinationSumDFS(candidates, target, 0, out, res);
return res;
}
void combinationSumDFS(vector& candidates, int target, int start, vector& out, vector>& res) {
if (target < 0) return;
if (target == 0) {res.push_back(out); return;}
for (int i = start; i < candidates.size(); ++i) {
out.push_back(candidates[i]);
combinationSumDFS(candidates, target - candidates[i], i, out, res);
out.pop_back();
}
}
};
1、咋一看,好像是可行解的集合是 一维数组,但由于可行解是整数,因此集合是二维数组,而且对于这个out由于是整数不能通过+字符串的形式表示加入子选择,故只能通过真实修改out的值,因此注意要复原
2、注意一下,我们之前的lever参数都比较明确,即该考虑哪一层了,但这一题由于可行解的长度不定,组成元素可以重复出现,因此lever不好确定,但不得不称写出来这个代码的真是大佬(哈哈,可不是我写的哟,仰仗大佬而已,学学),如果你以为既然可以重复选则,那每次子选项不都一样,就是全部数组里的元素,那就错啦,你运行一下会发现,你的解集出现重复的解。所以Lever设置为i,既能保证可以重复选择,有保证即使是重复,重复的元素都聚在一起,这样保证了解的唯一性。
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明:
所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。
示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combination-sum-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
public:
vector> combinationSum2(vector& num, int target) {
vector> res;
vector out;
sort(num.begin(), num.end());
helper(num, target, 0, out, res);
return res;
}
void helper(vector& num, int target, int start, vector& out, vector>& res) {
if (target < 0) return;
if (target == 0) { res.push_back(out); return; }
for (int i = start; i < num.size(); ++i) {
if (i > start && num[i] == num[i - 1]) continue;
out.push_back(num[i]);
helper(num, target - num[i], i + 1, out, res);
out.pop_back();
}
}
};
1、由于不能重复使用数组的元素,故lever由i改成i+1,
2、同时由于本身提供的数组有重复元素,又要保证解的独特性,通过 if (i > start && num[i] == num[i - 1]) continue;限制,比如(1,1,1,2)当start=1,可以第一遍选出[1]的1,第二遍,选[2]的1就出现重复了,因此需要限制。
3、先排序也是很优秀的,方便使用 num[i] == num[i - 1],让相等的挨着
给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutations
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
public:
vector> permute(vector& num) {
vector> res;
vector out, visited(num.size(), 0);
permuteDFS(num, 0, visited, out, res);
return res;
}
void permuteDFS(vector& num, int level, vector& visited, vector& out, vector>& res) {
if (level == num.size()) {res.push_back(out); return;}
for (int i = 0; i < num.size(); ++i) {
if (visited[i] == 1) continue;
visited[i] = 1;
out.push_back(num[i]);
permuteDFS(num, level + 1, visited, out, res);
out.pop_back();
visited[i] = 0;
}
}
};
给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutations-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
public:
vector> permuteUnique(vector& nums) {
vector> res;
vector out, visited(nums.size(), 0);
sort(nums.begin(), nums.end());
permuteUniqueDFS(nums, 0, visited, out, res);
return res;
}
void permuteUniqueDFS(vector& nums, int level, vector& visited, vector& out, vector>& res) {
if (level >= nums.size()) {res.push_back(out); return;}
for (int i = 0; i < nums.size(); ++i) {
if (visited[i] == 1) continue;
if (i > 0 && nums[i] == nums[i - 1] && visited[i - 1] == 0) continue;
visited[i] = 1;
out.push_back(nums[i]);
permuteUniqueDFS(nums, level + 1, visited, out, res);
out.pop_back();
visited[i] = 0;
}
}
};
1、保证解的唯一性,由于每次都可以从数组中选择任一个元素,因此采用标志的方式
2、注意恢复的时候不仅解要恢复,标志也要恢复
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/subsets
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
我们可以一位一位的网上叠加,比如对于题目中给的例子 [1,2,3] 来说,最开始是空集,那么我们现在要处理1,就在空集上加1,为 [1],现在我们有两个自己 [] 和 [1],下面我们来处理2,我们在之前的子集基础上,每个都加个2,可以分别得到 [2],[1, 2],那么现在所有的子集合为 [], [1], [2], [1, 2],同理处理3的情况可得 [3], [1, 3], [2, 3], [1, 2, 3], 再加上之前的子集就是所有的子集合了,
class Solution {
public:
vector > subsets(vector &S) {
vector > res;
vector out;
sort(S.begin(), S.end());
getSubsets(S, 0, out, res);
return res;
}
void getSubsets(vector &S, int pos, vector &out, vector > &res) {
res.push_back(out);
for (int i = pos; i < S.size(); ++i) {
out.push_back(S[i]);
getSubsets(S, i + 1, out, res);
out.pop_back();
}
}
};
1、当元素含有重复元素,但要求结果不重复时,要先排序注意使用 if (i > start && num[i] == num[i - 1]) continue;限制,该语句含义要根据题意修改,但本质原则不变。start为该层次可以选择元素的起始位置, num[i] == num[i - 1]为原序列,目的是,保证除了首元素,别的子选择不能选择和前一个子选择相同的元素
2、如果要保证只有一个解,用bool函数,且注意遍历时子选择一个即可,使用if (helper(board, i, j + 1)) return true;且函数默认返回值为false.
3、可以重复选择元素 且要保证解不重复,先排序,可以重复选择,可以使用lever不变。
4、行列都有遍历,当j>=9遍历下一行,当i==9就结束。可以学习一下对于这种二维数组应该如何应对
5、保证不会选择可行解之前选择过的元素,使用标记的方式,同时复原时,标记也要复原。同时visited也要当成实参的一部分。
6、排列、组合的区别是,排列的子选择是全部元素,组合是lever之后的。
https://www.cnblogs.com/grandyang/p/4358848.html
https://www.cnblogs.com/grandyang/p/4419386.html
https://www.cnblogs.com/grandyang/p/4419259.html
https://www.cnblogs.com/grandyang/p/4421852.html