【代码随想录】第七章-回溯算法
第七章-回溯算法
1 组合
77.组合
给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。
输入:n = 4, k = 2 输出:[[2,4] , [3,4], [2,3], [1,2], [1,3], [1,4],]
思路:
正常回溯,注意形参是否为引用,如果引用状态则需要手动进行增加删除。
class Solution {
List<List<Integer>> list=new ArrayList<>();
int[] visited;
public List<List<Integer>> combine(int n, int k) {
List<Integer> level=new ArrayList<>();
visited=new int[n+1];
dfs(1,n,level,k);
return list;
}
public void dfs(int start,int n, List<Integer>level,int k){
if(level.size()==k){
list.add(new ArrayList(level));
return;
}
for(int i=start;i<=n;i++){
if(visited[i]==0){
visited[i]=1;
level.add(i);
dfs(i,n,level,k);
visited[i]=0;
level.remove(level.size()-1);
}
}
}
}
39.组合总和
给你一个无重复元素的整数数组candidates和一个目标整数target,找出candidates中可以使数字和为目标数target的所有不同组合,并以列表形式返回。你可以按任意顺序返回这些组合。candidates中的同一个数字可以无限制重复被选取。如果至少一个数字的被选数量不同,则两种组合是不同的。对于给定的输入,保证和为target的不同组合数少于150个。
输入:candidates = [2,3,6,7], target = 7 输出:[[2,2,3],[7]]
思路:
注意快速剪枝
class Solution {
List<List<Integer>> list = new ArrayList<>();
List<Integer> level = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
// Arrays.sort(candidates);
int sum = 0;
dfs(candidates, sum, target, 0);
return list;
}
public void dfs(int[] candidates, int sum, int target, int index) {
if (sum > target) return;
if (sum == target) list.add(new ArrayList(level));
for (int i = index; i < candidates.length; i++) {
level.add(candidates[i]);
dfs(candidates, sum + candidates[i], target, i);
level.remove(level.size() - 1);
}
}
}
40.组合总和II
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的每个数字在每个组合中只能使用一次。
输入:root = [3,9,20,null,null,15,7] 输出:[[3],[9,20],[15,7]]
思路:
Method1:使用标志数组去重遍历
先对candidates进行排序,然后用index来指名重复使用,其他就是正常回溯。与39不同的地方是,这里只能使用一次,那就需要用访问标志数组来表示有没有访问过。i>0&&candidates[i]==candidates[i-1]&&visited[i-1]==0,为什么visited[i-1]==0,因为如果前面有个元素和他一致,前面先访问,然后走不通回溯之后,前面的元素的访问标志就变为0了。这就是和前一个能区别开的原因。
class Solution {
List<List<Integer>> list = new ArrayList<>();
int[] visited;
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
int len = candidates.length;
visited = new int[len + 1];
List<Integer> level = new ArrayList<>();
int sum = 0;
dfs(sum, candidates, target, level, 0);
return list;
}
public void dfs(int sum, int[] candidates, int target, List<Integer> level, int index) {
if (sum > target) return;
if (sum == target) {
list.add(new ArrayList(level));
return;
}
for (int i = index; i < candidates.length; i++) {
if (i > 0 && candidates[i] == candidates[i - 1]
&& visited[i - 1] == 0) {
continue;
}
visited[i] = 1;
level.add(candidates[i]);
dfs(sum + candidates[i], candidates, target, level, i+1);
level.remove(level.size() - 1);
visited[i] = 0;
}
}
}
Method2:使用HashSet去重遍历
class Solution {
List<List<Integer>> list = new ArrayList<>();
List<Integer> level = new ArrayList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
int len = candidates.length;
dfs(0, candidates, target, 0);
return list;
}
public void dfs(int sum, int[] candidates, int target, int index) {
if (sum > target) return;
if (sum == target) {
list.add(new ArrayList(level));
return;
}
HashSet<Integer> used = new HashSet<>();
for (int i = index; i < candidates.length; i++) {
if (used.contains(candidates[i])) continue;
used.add(candidates[i]);
level.add(candidates[i]);
dfs(sum + candidates[i], candidates, target,i+1);
level.remove(level.size() - 1);
}
}
}
216.组合总和III
找出所有相加之和为n的k个数的组合。组合中只允许含有1-9的正整数,并且每种组合中不存在重复的数字。
输入: k = 3, n = 7 输出: [[1,2,4]]
思路:
正常递归遍历,注意入列条件有两个。
class Solution {
List<List<Integer>> list=new ArrayList<>();
int []visited;
public List<List<Integer>> combinationSum3(int k, int n) {
List<Integer> level=new ArrayList<>();
visited=new int[10];
int sum=0;
dfs(n,sum,level,1,k);
return list;
}
public void dfs(int n,int sum,List<Integer> level,int index,int k){
if(sum>n) return;
if(sum==n&&level.size()==k){
list.add(new ArrayList(level));
return;
}
for(int i=index;i<=9;i++){
if(visited[i]==0){
visited[i]=1;
level.add(i);
dfs(n,sum+i,level,i,k);
level.remove(level.size()-1);
visited[i]=0;
}
}
}
}
17.电话号码的字母组合
给定一个仅包含数字2-9的字符串,返回所有它能表示的字母组合。给出数字到字母的映射如下(与电话按键相同)。注意1不对应任何字母。
输入:“23” 输出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].
思路:
针对digits的每个数字进行深搜,用index标记访问到哪个,然后递归遍历搜索,在搜索完后把当前删掉就能进行下一次搜索。比如那“23”来举例,先搜索2对应的s即abc,用char c遍历abc,在a的层级时,c当前是a,进行深度遍历,当前index在0,也就是在数字2的位置,然后通过递归index+1遍历到3,3对应的str是def,d加入level,也就是说level是ad,然后level的长度跟“23”的长度一样了,退出循环,接下来把d删除,再遍历d后面的e,f,一次类推,level变为ae,退出遍历的时候把e删除…
class Solution {
HashMap<Integer, String> map = new HashMap<>();
List<String> list=new ArrayList<>();
public List<String> letterCombinations(String digits) {
map.put(2,"abc");
map.put(3,"def");
map.put(4,"ghi");
map.put(5,"jkl");
map.put(6,"mno");
map.put(7,"pqrs");
map.put(8,"tuv");
map.put(9,"wxyz");
List<Character> level=new ArrayList<>();
char[] nums=digits.toCharArray();
int len=nums.length;
if(len==0) return list;
dfs(nums,len,level,0);
return list;
}
public void dfs(char[] nums,int len,List<Character> level,int index){
if(level.size()==len){
StringBuilder sb=new StringBuilder();
for(Character c:level) sb.append(c);
list.add(sb.toString());
return;
}
for(int i=index;i<len;i++){
String s=map.get(nums[i]-'0');
for(char c:s.toCharArray()){
level.add(c);
dfs(nums,len,level,i+1);
level.remove(level.size()-1);
}
}
}
}
2 分割回文串
131.分割回文串
给你一个字符串s,请你将s分割成一些子串,使每个子串都是回文串。返回s所有可能的分割方案。
输入:s = “aab” 输出:[[“a”,“a”,“b”],[“aa”,“b”]]
思路:
注意sb的创建,如果当前是回文串,为了一一检查,在当前的字符串上依旧用index+1创建新的字符串,只有在回溯退出时,才会往sb中添加新的字符串。
退出条件时当index遍历到最后一个字符的时候将当前层的level退出。
class Solution {
List<List<String>> list=new ArrayList<>();
public List<List<String>> partition(String s) {
List<String> level =new ArrayList<>();
dfs(s,level,0,new StringBuffer());
return list;
}
public boolean isHuiWen(String s){
int len=s.length();
int low=0;int high=len-1;
while(low<high){
if(s.charAt(low)==s.charAt(high)){
low++;
high--;
}
else return false;
}
return true;
}
public void dfs(String s,List<String> level,int index,StringBuffer sb){
if(index==s.length()){
list.add(new ArrayList(level));
return;
}
for(int i=index;i<s.length();i++){
sb.append(s.charAt(i));
if(isHuiWen(sb.toString())){
level.add(sb.toString());
dfs(s,level,i+1,new StringBuffer());
level.remove(level.size()-1);
}
}
}
}
93.复原IP地址
有效IP地址正好由四个整数(每个整数位于0到255之间组成,且不能含有前导0),整数之间用’.‘分隔。给定一个只包含数字的字符串s,用以表示一个IP地址,返回所有可能的有效IP地址,这些地址可以通过在s中插入’.'来形成。你不能重新排序或删除s中的任何数字。你可以按任何顺序返回答案。
输入:s = “25525511135” 输出:[“255.255.11.135”,“255.255.111.35”]
思路:
两点需要注意,一、退出条件,当句号为三个的时候检查最后一组数字,如果符合就加入list,否则返回,二、用StringBuffer的insert加入句号后,i的增值应该越过句号,为i+2。
class Solution {
List<String> list=new ArrayList<>();
public List<String> restoreIpAddresses(String s) {
if(s.length()>12) return list;
StringBuffer sb=new StringBuffer(s);
dfs(sb,0,0);
return list;
}
public void dfs(StringBuffer sb,int index,int count){
if(count==3){
if(isVaild(sb,index,sb.length()-1)){
list.add(sb.toString());
}
return;
}
for(int i=index;i<sb.length();i++){
if(isVaild(sb,index,i)){
sb.insert(i+1,".");
dfs(sb,i+2,count+1);
sb.deleteCharAt(i+1);
}
else break;
}
}
public boolean isVaild(StringBuffer s,int low,int high){
if(low>high) return false;
if(s.charAt(low)=='0'&&low!=high) return false;
String sub=s.substring(low,high+1);
if(Integer.parseInt(sub)>255) return false;
return true;
}
}
78.子集
给你一个整数数组nums,数组中的元素互不相同。返回该数组所有可能的子集(幂集)。解集不能包含重复的子集。你可以按任意顺序返回解集。
输入:nums = [1,2,3] 输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
思路:
正常递归
class Solution {
List<List<Integer>> list=new ArrayList<>();
List<Integer> level=new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
if(nums.length==0) return list;
dfs(nums,0);
return list;
}
public void dfs(int[] nums,int index){
list.add(new ArrayList(level));
if(index>=nums.length) return;
for(int i=index;i<nums.length;i++){
level.add(nums[i]);
dfs(nums,i+1);
level.remove(level.size()-1);
}
}
}
90.子集II
给你一个整数数组nums,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。解集不能包含重复的子集。返回的解集中,子集可以按任意顺序排列。
输入:nums = [1,2,2] 输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]
思路:
Method1:使用标志数组去重遍历
如果不要重复的,检查前一个的标志数组,如果和前一个的值一样并且为访问就跳过。i>0&&nums[i]== nums [i-1]&&visited[i-1]==0,为什么visited[i-1]==0,因为如果前面有个元素和他一致,前面先访问,然后走不通回溯之后,前面的元素的访问标志就变为0了。这就是和前一个能区别开的原因。
class Solution {
List<List<Integer>> list = new ArrayList<>();
List<Integer> level = new ArrayList<>();
int[] visited;
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
visited = new int[nums.length + 1];
dfs(nums, 0);
return list;
}
public void dfs(int[] nums, int index) {
list.add(new ArrayList(level));
if(index>=nums.length) return;
for (int i = index; i < nums.length; i++) {
if (i > 0 && nums[i-1] == nums[i] && visited[i-1] == 0) {
continue;
}
visited[i]=1;
level.add(nums[i]);
dfs(nums, i + 1);
level.remove(level.size() - 1);
visited[i]=0;
}
}
}
Method2:使用Hashset去重
class Solution {
List<List<Integer>> list = new ArrayList<>();
List<Integer> level = new ArrayList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
dfs(nums, 0);
return list;
}
public void dfs(int[] nums, int index) {
list.add(new ArrayList(level));
if(index>=nums.length) return;
HashSet<Integer> used = new HashSet<>();
for (int i = index; i < nums.length; i++) {
if (used.contains(nums[i])) continue;
used.add(nums[i]);
level.add(nums[i]);
dfs(nums, i + 1);
level.remove(level.size() - 1);
}
}
}
491.非递减子序列
给你一个整数数组nums,找出并返回所有该数组中不同的递增子序列,递增子序列中至少有两个元素。你可以按任意顺序返回答案。
输入:nums = [4,6,7,7] 输出:[[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]]
思路:
使用hashset去重。
class Solution {
List<List<Integer>> list = new ArrayList<>();
List<Integer> level = new ArrayList<>();
public List<List<Integer>> findSubsequences(int[] nums) {
if (nums.length == 0) return list;
dfs(nums, 0);
return list;
}
public void dfs(int[] nums, int index) {
if (level.size() >= 2) list.add(new ArrayList(level));
if (index >= nums.length) return;
HashSet<Integer> used = new HashSet<>();
for (int i = index; i < nums.length; i++) {
if ((!level.isEmpty() && nums[i] < level.get(level.size() - 1))
|| used.contains(nums[i])) {
continue;
}
used.add(nums[i]);
level.add(nums[i]);
dfs(nums, i + 1);
level.remove(level.size() - 1);
}
}
}
3 全排列
46.全排列
给定一个不含重复数字的数组nums,返回其所有可能的全排列。你可以按任意顺序返回答案。
输入:nums = [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
思路:
回溯遍历,注意全排列,需要使用visisted来针对,所以需要每次从0开始
class Solution {
List<List<Integer>> list = new ArrayList<>();
List<Integer> level = new ArrayList<>();
int[] visited;
public List<List<Integer>> permute(int[] nums) {
visited = new int[nums.length];
dfs(nums);
return list;
}
public void dfs(int[] nums) {
if (level.size()==nums.length) list.add(new ArrayList(level));
for (int i = 0; i < nums.length; i++) {
if (visited[i] == 0) {
visited[i] = 1;
level.add(nums[i]);
dfs(nums);
level.remove(level.size() - 1);
visited[i] = 0;
}
}
}
}
47.全排列II
给定一个可包含重复数字的序列nums,按任意顺序返回所有不重复的全排列。
输入:nums = [1,1,2] 输出:[[1,1,2],[1,2,1],[2,1,1]]
思路:
回溯遍历,注意这个会回头,所以需要每次从0开始
class Solution {
List<List<Integer>> list = new ArrayList<>();
List<Integer> level = new ArrayList<>();
int[] visited;
public List<List<Integer>> permuteUnique(int[] nums) {
Arrays.sort(nums);
visited = new int[nums.length];
dfs(nums);
return list;
}
public void dfs(int[] nums) {
if (level.size() == nums.length) {
list.add(new ArrayList(level));
}
for (int i = 0; i < nums.length; i++) {
if (i > 0 && nums[i - 1] == nums[i]
&& visited[i - 1] == 0) {
continue;
}
if (visited[i] == 0) {
visited[i] = 1;
level.add(nums[i]);
dfs(nums);
level.remove(level.size() - 1);
visited[i] = 0;
}
}
}
}
4 N皇后
51.N皇后
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n皇后问题研究的是如何将n个皇后放置在n×n的棋盘上,并且使皇后彼此之间不能相互攻击。给你一个整数n,返回所有不同的n皇后问题的解决方案。每一种解法包含一个不同的n皇后问题的棋子放置方案,该方案中’Q’和’.'分别代表了皇后和空位。
输入:n = 4
输出:[[“.Q…”,“…Q”,“Q…”,“…Q.”],[“…Q.”,“Q…”,“…Q”,“.Q…”]]
思路:
注意维护行、列、上对角线、下对角线四个set去重即可
class Solution {
List<List<String>>list=new ArrayList<>();
HashSet<Integer> rowSet=new HashSet<>();
HashSet<Integer> colSet=new HashSet<>();
HashSet<Integer> shangDuiJiaoSet=new HashSet<>();
HashSet<Integer> xiaDuiJiaoSet=new HashSet<>();
public List<List<String>> solveNQueens(int n) {
char [][] arr=new char[n][n];
for(int i=0;i<n;i++){
Arrays.fill(arr[i],'.');
}
dfs(arr,0,n);
return list;
}
public void dfs(char [][] arr,int index,int n){
if(index==n){
List<String> level=new ArrayList<>();
for(int i=0;i<n;i++){
level.add(new String(arr[i]));
}
list.add(new ArrayList(level));
return;
}
for(int j=0;j<n;j++){
if(rowSet.contains(index)||colSet.contains(j)
||shangDuiJiaoSet.contains(index-j)||xiaDuiJiaoSet.contains(index+j)){
continue;
}
arr[index][j]='Q';
rowSet.add(index);
colSet.add(j);
shangDuiJiaoSet.add(index-j);
xiaDuiJiaoSet.add(index+j);
dfs(arr,index+1,n);
arr[index][j]='.';
rowSet.remove(index);
colSet.remove(j);
shangDuiJiaoSet.remove(index-j);
xiaDuiJiaoSet.remove(index+j);
}
}
}
52.N皇后II
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n皇后问题研究的是如何将n个皇后放置在n×n的棋盘上,并且使皇后彼此之间不能相互攻击。给你一个整数n,返回所有不同的n皇后问题的解决方案。每一种解法包含一个不同的n皇后问题的棋子放置方案,该方案中’Q’和’.'分别代表了皇后和空位。
输入:n = 4
输出:2
思路:
把51题的list填充变为全局count++即可。
36.有效的数独
请你判断一个9x9的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。
数字1-9在每一行只能出现一次;数字1-9在每一列只能出现一次;数字1-9在每一个以粗实线分隔的3x3宫内只能出现一次。
输入:board =
[[“5”,“3”,“.”,“.”,“7”,“.”,“.”,“.”,“.”]
,[“6”,“.”,“.”,“1”,“9”,“5”,“.”,“.”,“.”]
,[“.”,“9”,“8”,“.”,“.”,“.”,“.”,“6”,“.”]
,[“8”,“.”,“.”,“.”,“6”,“.”,“.”,“.”,“3”]
,[“4”,“.”,“.”,“8”,“.”,“3”,“.”,“.”,“1”]
,[“7”,“.”,“.”,“.”,“2”,“.”,“.”,“.”,“6”]
,[“.”,“6”,“.”,“.”,“.”,“.”,“2”,“8”,“.”]
,[“.”,“.”,“.”,“4”,“1”,“9”,“.”,“.”,“5”]
,[“.”,“.”,“.”,“.”,“8”,“.”,“.”,“7”,“9”]]
输出:true
思路:
横竖比较简单,困难的是33怎么进行区分,这个很考验数学,推导公式是第几个33矩阵是由(i/3)*3+(j/3)得来的。
class Solution {
HashSet<Character>[] rowSet = new HashSet[9];
HashSet<Character>[] columnSet = new HashSet[9];
HashSet<Character>[] threeSet = new HashSet[9];
public boolean isValidSudoku(char[][] board) {
for(int i=0;i<9;i++){
rowSet[i] = new HashSet<>();
columnSet[i] = new HashSet<>();
threeSet[i] = new HashSet<>();
}
for(int i=0;i<9;i++){
for(int j=0;j<9;j++){
if(board[i][j]=='.') continue;
char c=board[i][j];
if(rowSet[i].contains(c)) return false;
if(columnSet[j].contains(c)) return false;
if(threeSet[(i / 3) * 3 + (j / 3)].contains(c)) return false;
rowSet[i].add(c);
columnSet[j].add(c);
threeSet[(i / 3) * 3 + (j / 3)].add(c);
}
}
return true;
}
}
Tip:大小矩阵公式推导
如果要8×8的大矩阵划分为2×4×的小矩阵,我们可以使用以下公式来确定元素属于第几个小矩阵:
x = ⌊ i k ⌋ × ( N k ) + ⌊ i k ⌋ x=\left\lfloor \frac{i}{k} \right\rfloor \times \left( \frac{N}{k} \right)+\left\lfloor \frac{i}{k} \right\rfloor\ x=⌊ki⌋×(kN)+⌊ki⌋
i: 元素的行索引(从 0 开始)。
j: 元素的列索引(从 0 开始)。
k: 小矩阵的边长(例如 2 或 4)。
N: 大矩阵的边长(这里为 8)。
37.解数独
编写一个程序,通过填充空格来解决数独问题。只需要根据以下规则,验证已经填入的数字是否有效即可。数字1-9在每一行只能出现一次;数字1-9在每一列只能出现一次;数字1-9在每一个以粗实线分隔的3x3宫内只能出现一次。
输入:board =
[[“5”,“3”,“.”,“.”,“7”,“.”,“.”,“.”,“.”],
[“6”,“.”,“.”,“1”,“9”,“5”,“.”,“.”,“.”],
[“.”,“9”,“8”,“.”,“.”,“.”,“.”,“6”,“.”],
[“8”,“.”,“.”,“.”,“6”,“.”,“.”,“.”,“3”],
[“4”,“.”,“.”,“8”,“.”,“3”,“.”,“.”,“1”],
[“7”,“.”,“.”,“.”,“2”,“.”,“.”,“.”,“6”],
[“.”,“6”,“.”,“.”,“.”,“.”,“2”,“8”,“.”],
[“.”,“.”,“.”,“4”,“1”,“9”,“.”,“.”,“5”],
[“.”,“.”,“.”,“.”,“8”,“.”,“.”,“7”,“9”]]
输出:
[[“5”,“3”,“4”,“6”,“7”,“8”,“9”,“1”,“2”],
[“6”,“7”,“2”,“1”,“9”,“5”,“3”,“4”,“8”],
[“1”,“9”,“8”,“3”,“4”,“2”,“5”,“6”,“7”],
[“8”,“5”,“9”,“7”,“6”,“1”,“4”,“2”,“3”],
[“4”,“2”,“6”,“8”,“5”,“3”,“7”,“9”,“1”],
[“7”,“1”,“3”,“9”,“2”,“4”,“8”,“5”,“6”],
[“9”,“6”,“1”,“5”,“3”,“7”,“2”,“8”,“4”],
[“2”,“8”,“7”,“4”,“1”,“9”,“6”,“3”,“5”],
[“3”,“4”,“5”,“2”,“8”,“6”,“1”,“7”,“9”]]
思路:
对三个set回溯操作,遍历i和j,将1到9的数字全部试完。
先分别创建横竖和3*3
,并且把已经存在的数字先放进去,然后开始深搜,i表示行,j表示列。如果i==9
,说明整个数独都看完了,都符合,返回true即可;如果j==9
,说明这一行没问题,就要进行下一行,从下一行的第一个开始,也就是(i+1, 0)这个位置。
如果移动的时候碰见已经填进去的数字自然跳过,只看空着的从1到9挨个尝试,看看横竖和3*3,也就是!rowSet[i].contains(c) && !columnSet[j].contains(c) && !threeSet[(i / 3) * 3 + (j / 3)].contains(c)
。
如果能放进去,就放进去,然后递归的尝试同一行的下一个元素,也就是(i, j+1)
,如果一直递归的下一个都是ok的那就也返回ok。如果哪一步不行返回false了,我们就要回溯该路径的所有,也就是退格
class Solution {
HashSet<Character>[] rowSet = new HashSet[9];
HashSet<Character>[] columnSet = new HashSet[9];
HashSet<Character>[] threeSet = new HashSet[9];
public void solveSudoku(char[][] board) {
for(int i=0;i<9;i++){
rowSet[i] = new HashSet<>();
columnSet[i] = new HashSet<>();
threeSet[i] = new HashSet<>();
}
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (board[i][j] != '.') {
rowSet[i].add(board[i][j]);
columnSet[j].add(board[i][j]);
threeSet[(i / 3) * 3 + (j / 3)].add(board[i][j]);
}
}
}
dfs(board,0,0);
}
public boolean dfs(char[][] board,int i,int j){
if(i==9) return true;
if(j==9) return dfs(board,i+1,0);
if(board[i][j]!='.') return dfs(board,i,j+1);
for(int k=1;k<=9;k++){
char c=(char)('0'+k);
if(!rowSet[i].contains(c)&&!columnSet[j].contains(c)
&&!threeSet[(i / 3) * 3 + (j / 3)].contains(c)){
board[i][j]=c;
rowSet[i].add(c);
columnSet[j].add(c);
threeSet[(i / 3) * 3 + (j / 3)].add(c);
if(dfs(board,i,j+1)) return true;
board[i][j]='.';
rowSet[i].remove(c);
columnSet[j].remove(c);
threeSet[(i / 3) * 3 + (j / 3)].remove(c);
}
}
return false;
}
}