leetcode之回溯刷题总结2
1-解数独
题目链接:题目链接戳这里!!!
思路:递归+回溯
首先预处理board数组,如果是数字,则使用row[i][digit]标记数字digit+1在第i行出现过,column[j][digit]表示数字digit+1在第j列出现过,block[i/3][j/3][digit]数字digit+1在3*3的方格内出现过,后面递归依次尝试所有数字,如果满足条件则放入,否则回溯到最初位置继续尝试。
class Solution {
List<int[]> list = new ArrayList<>() ;
boolean [][] row = new boolean [9][9] ;
boolean [][] column = new boolean [9][9] ;
boolean [][][] block = new boolean [3][3][9] ;
boolean valid = false ;
public void solveSudoku(char[][] board) {
for(int i=0; i<9; i++){
for(int j=0; j<9; j++){
if(board[i][j]=='.'){
list.add(new int []{i,j}) ;
}else{
int digit = board[i][j] - '0' - 1;
row[i][digit] = column[j][digit] = block[i/3][j/3][digit] = true ;
}
}
}
dfs(0,board) ;
}
public void dfs(int k, char [][] board){
if(k==list.size()){
valid = true ;
return ;
}
int [] space = list.get(k) ;
int i = space[0], j = space[1] ;
for(int digit=0; digit<9 && !valid; digit++){
if(!row[i][digit] && !column[j][digit] && !block[i/3][j/3][digit]){
row[i][digit] = column[j][digit] = block[i/3][j/3][digit] = true ;
board[i][j] = (char)(digit + '0' + 1) ;
dfs(k+1, board) ;
row[i][digit] = column[j][digit] = block[i/3][j/3][digit] = false ;
}
}
}
}
2-N皇后
题目链接:题目链接戳这里!!!
思路:递归+回溯法
皇后的走法是:可以横直斜走,格数不限。因此要求皇后彼此之间不能相互攻击,等价于要求任何两个皇后都不能在同一行、同一列以及同一条斜线上。
回溯的具体做法是:使用一个数组记录每行放置的皇后的列下标,依次在每一行放置一个皇后。每次新放置的皇后都不能和已经放置的皇后之间有攻击:即新放置的皇后不能和任何一个已经放置的皇后在同一列以及同一条斜线上,并更新数组中的当前行的皇后列下标。当 N 个皇后都放置完毕,则找到一个可能的解。当找到一个可能的解之后,将数组转换成表示棋盘状态的列表,并将该棋盘状态的列表加入返回列表。
class Solution {
List<List<String>> ans = new ArrayList<>() ;
public List<List<String>> solveNQueens(int n) {
Set<Integer> columns = new HashSet<>() ;
Set<Integer> diagonals1 = new HashSet<>() ;
Set<Integer> diagonals2 = new HashSet<>() ;
int [] queues = new int [n] ;
Arrays.fill(queues,-1) ;
dfs(0,n,columns,diagonals1,diagonals2, queues) ;
return ans ;
}
public void dfs(int row, int n, Set<Integer> columns, Set<Integer> diagonals1, Set<Integer> diagonals2, int [] queues){
if(row==n){
List<String> board = f(queues,n) ;
ans.add(new ArrayList<>(board)) ;
return ;
}
for(int i=0; i<n; i++){
if(columns.contains(i)){
continue ;
}
int diagonal1 = row + i ;
if(diagonals1.contains(diagonal1)){
continue ;
}
int diagonal2 = row - i ;
if(diagonals2.contains(diagonal2)){
continue ;
}
queues[row] = i ;
columns.add(i) ;
diagonals1.add(diagonal1) ;
diagonals2.add(diagonal2) ;
dfs(row+1, n, columns, diagonals1, diagonals2,queues) ;
queues[row] = -1 ;
columns.remove(i) ;
diagonals1.remove(diagonal1) ;
diagonals2.remove(diagonal2) ;
}
}
public List<String> f(int [] queues, int n){
List<String> list = new ArrayList<>() ;
for(int i=0; i<n; i++){
char [] a = new char [n] ;
Arrays.fill(a,'.') ;
a[queues[i]] = 'Q' ;
list.add(new String(a)) ;
}
return list ;
}
}
3-N皇后II
题目链接:题目链接戳这里!!!
思路:递归+回溯
这题和上一题思路一样,每一次只需要返回满足条件的种类数量即可。
class Solution {
public int totalNQueens(int n) {
Set<Integer> columns = new HashSet<>() ;
Set<Integer> diagonals1 = new HashSet<>() ;
Set<Integer> diagonals2 = new HashSet<>() ;
return dfs(0,n,columns,diagonals1,diagonals2) ;
}
public int dfs(int row, int n, Set<Integer> columns, Set<Integer> diagonals1, Set<Integer> diagonals2){
if(row==n){
return 1 ;
}else{
int cnt = 0 ;
for(int i=0; i<n; i++){
if(columns.contains(i)){
continue ;
}
int diagonal1 = row + i ;
if(diagonals1.contains(diagonal1)){
continue ;
}
int diagonal2 = row - i ;
if(diagonals2.contains(diagonal2)){
continue ;
}
columns.add(i) ;
diagonals1.add(diagonal1) ;
diagonals2.add(diagonal2) ;
cnt += dfs(row+1,n,columns,diagonals1,diagonals2) ;
columns.remove(i) ;
diagonals1.remove(diagonal1) ;
diagonals2.remove(diagonal2) ;
}
return cnt ;
}
}
}
4-字母大小写全排列
题目链接:题目链接戳这里!!!
思路1:如果下一个字符 c 是字母,将当前已遍历过的字符串全排列复制两份,在第一份的每个字符串末尾添加 lowercase©,在第二份的每个字符串末尾添加 uppercase©。
class Solution {
public List<String> letterCasePermutation(String s) {
List<StringBuilder> res = new ArrayList<>() ;
List<String> list = new ArrayList<>() ;
res.add(new StringBuilder()) ;
for(char c : s.toCharArray()){
int n = res.size();
if(Character.isLetter(c)){
for(int i=0; i<n; i++){
res.add(new StringBuilder(res.get(i))) ;
res.get(i).append(Character.toUpperCase(c)) ;
res.get(n+i).append(Character.toLowerCase(c)) ;
}
}else{
for(int i=0; i<n; i++){
res.get(i).append(c) ;
}
}
}
for(StringBuilder ans : res){
list.add(ans.toString()) ;
}
return list ;
}
}
思路2:搜索+回溯
递归搜索,如果是数字,直接添加,字母则转换之后添加,同时,每一轮搜索完成,需要回溯到原始状态。
class Solution {
List<String> res = new ArrayList<>() ;
char [] c ;
public List<String> letterCasePermutation(String s) {
c = s.toCharArray() ;
dfs(0,new StringBuilder()) ;
return res ;
}
public void dfs(int idx, StringBuilder str){
if(idx>=c.length){
res.add(str.toString()) ;
return ;
}
dfs(idx+1,str.append(c[idx])) ;
str.deleteCharAt(idx) ;
if(c[idx]>='a'){
dfs(idx+1,str.append((char)(c[idx]-32))) ;
str.deleteCharAt(idx) ;
}else if(c[idx]>='A'){
dfs(idx+1,str.append((char)(c[idx]+32))) ;
str.deleteCharAt(idx) ;
}
}
}
思路3:递归法
class Solution {
List<String> res = new ArrayList<>() ;
public List<String> letterCasePermutation(String s) {
char []c = s.toCharArray() ;
dfs(0,c) ;
return res ;
}
public void dfs(int idx, char [] c){
if(idx>=c.length){
res.add(new String(c)) ;
return ;
}
char ch = c[idx] ;
if(Character.isLetter(ch)){
c[idx] = Character.toLowerCase(ch) ;
dfs(idx+1,c) ;
c[idx] = Character.toUpperCase(ch) ;
dfs(idx+1,c) ;
}else{
dfs(idx+1,c) ;
}
}
}
5-单词搜索
题目链接:题目链接戳这里!!!
思路:搜索+回溯
压着四个方向搜索,每一次搜索标记,未被访问且没有越界,则可以继续搜索,每轮搜索完成需要标记数组需要回溯到原始状态。
class Solution {
int [] dx = {-1,1,0,0} ;
int [] dy = {0,0,-1,1} ;
boolean flags = false ;
public boolean exist(char[][] board, String word) {
boolean [][] vis = new boolean[board.length][board[0].length] ;
for(int i=0;i<board.length; i++){
for(int j=0; j<board[0].length; j++){
boolean flag = dfs(board,i,j,vis,word,0) ;
if(flag){
return true ;
}
}
}
return false ;
}
public boolean dfs(char [][]board, int x, int y, boolean [][] vis, String word,int begin){
if(board[x][y]!=word.charAt(begin)){
return false ;
}
if(begin==word.length()-1){
return true ;
}
vis[x][y] = true ;
for(int i=0; i<4; i++){
int tx = x + dx[i] ;
int ty = y + dy[i] ;
if(tx<0 || ty<0 || tx>board.length-1 || ty>board[0].length-1){
continue ;
}
if(!vis[tx][ty]){
flags = dfs(board,tx,ty,vis,word,begin+1) ;
if(flags){
return true ;
}
}
}
vis[x][y] = false ;
return flags ;
}
}
6-黄金矿工
题目链接:题目链接戳这里!!!
思路:搜索+回溯
沿着四个方向搜索,当前值大于0则继续搜索,每次搜索要求和,同时标记为0,同时求出最大值,搜索完后后,要回溯到原始状态。
class Solution {
int res = 0;
int [] dx = {-1,1,0,0} ;
int [] dy = {0,0,-1,1} ;
public int getMaximumGold(int[][] grid) {
for(int i=0; i<grid.length; i++){
for(int j=0; j<grid[0].length; j++){
if(grid[i][j]>0){
dfs(grid,i,j,0) ;
}
}
}
return res ;
}
public void dfs(int [][] grid, int x, int y, int sum){
if(x<0 || y<0 || x>grid.length-1 || y>grid[0].length-1){
return ;
}
int temp = grid[x][y] ;
sum += grid[x][y] ;
grid[x][y] = 0 ;
res = Math.max(res,sum) ;
for(int i=0; i<4; i++){
int tx = x + dx[i] ;
int ty = y + dy[i] ;
if(tx<0 || ty<0 || tx>grid.length-1 || ty>grid[0].length-1){
continue ;
}
if(grid[tx][ty]>0){
dfs(grid,tx,ty,sum) ;
}
}
sum -= grid[x][y] ;
grid[x][y] = temp ;
}
}
7-活字印刷
题目链接:题目链接戳这里!!!
思路:递归+回溯+set集合去重
class Solution {
public int numTilePossibilities(String tiles) {
Set<String> res = new TreeSet<>() ;
int n = tiles.length() ;
boolean [] vis = new boolean [n] ;
dfs(res,tiles,vis,new StringBuilder()) ;
return res.size()-1 ;
}
public void dfs(Set<String> res, String tiles, boolean [] vis, StringBuilder temp){
res.add(temp.toString()) ;
for(int i=0; i<tiles.length(); i++){
if(!vis[i]){
vis[i] = true ;
temp.append(tiles.charAt(i)) ;
dfs(res,tiles,vis,temp) ;
temp.deleteCharAt(temp.length()-1) ;
vis[i] = false ;
}
}
}
}
这样写效率高一些。
class Solution {
int cnt = 0 ;
public int numTilePossibilities(String tiles) {
char [] c = tiles.toCharArray() ;
Arrays.sort(c) ;
boolean [] vis = new boolean[c.length] ;
for(int i=1; i<=tiles.length(); i++){
dfs(c,vis,0,i) ;
}
return cnt ;
}
public void dfs(char [] c, boolean [] vis, int cur, int target){
if(cur == target){
cnt ++ ;
return ;
}
for(int i=0; i<c.length; i++){
if(vis[i]){
continue ;
}
if(i>0 && c[i-1]==c[i] && !vis[i-1]){
continue ;
}
vis[i] = true ;
cur += 1 ;
dfs(c,vis,cur,target) ;
cur -= 1 ;
vis[i] = false ;
}
}
}