文章目录
- [1. 优美的排列(LC526)](#1. 优美的排列(LC526))
- [2. N皇后(LC51)](#2. N皇后(LC51))
- [3. 有效的数独(LC36)](#3. 有效的数独(LC36))
- [4. 解数独(LC37)](#4. 解数独(LC37))
- [5. 单词搜索(LC79)](#5. 单词搜索(LC79))
- [6. 黄金矿工(LC1219)](#6. 黄金矿工(LC1219))
- [7. 不同路径III(LC980)](#7. 不同路径III(LC980))
1. 优美的排列(LC526)
题目描述

解题思路
类似全排列,排列同时要检查是否符合题意,使用check数组标记已使用的元素。
代码实现
java
class Solution {
boolean[] check ;
int ret;
public int countArrangement(int n) {
check = new boolean[n+1];
dfs(1,n);
return ret;
}
void dfs(int i ,int n){
if(i == n+1){
ret++;
return;
}
for(int j = 1;j<=n;j++){
if(!check[j] && (i%j==0 || j%i==0)){
check[j] = true;
dfs(i+1,n);
check[j] = false;
}
}
}
}
2. N皇后(LC51)
题目描述

解题思路

遍历行时利用三个数组(简化哈希表)剪枝:
col[],在遍历行时同时记录所在的列,如果当前位置所在列被标记,就剪枝;dig1[],记录主对角线。转换为一次函数,y-x是定值b,为了防止b越界,整体向上平移n个单位。dig1[],记录副对角线。y+x是定值b

代码实现
java
boolean[] col;
boolean[] dig1;
boolean[] dig2;
char[][] path;
List<List<String>> ret = new ArrayList<>();
StringBuilder str = new StringBuilder();
public List<List<String>> solveNQueens(int n) {
col = new boolean[n];
dig1 = new boolean[2*n];
dig2 = new boolean[2*n];
path = new char[n][n];
for(int i = 0;i<n;i++){
for(int j = 0;j<n;j++){
path[i][j] = '.';
}
}
dfs(0,n);
return ret;
}
void dfs(int i,int n){
if(i == n){
//此时遍历结束,把每一行转为list<String>
List<String> tmp = new ArrayList<>();
for(int k = 0;k<n;k++)
tmp.add(new String(path[k]));
ret.add(new ArrayList<>(tmp));
return;
}
// i是纵坐标,j是横坐标
for(int j = 0;j <n;j++){
if(!(col[j] || dig1[i - j + n ] || dig2[j+i])){
path[i][j] = 'Q';
col[j] = true;
dig1[i - j + n] = true;
dig2[i + j] = true;
dfs(i + 1,n);
//恢复现场
col[j] = false;
dig1[i - j + n] = false;
dig2[i + j] = false;
path[i][j] = '.';
}
}
}
3. 有效的数独(LC36)
题目描述

解题思路

定义三个数组分别标记当前行,当前列和当前九宫格中1-9是否被使用过
row[][]和col[][]第一个元素表示横坐标或纵坐标,第二个元素表示1-9是否被使用grid[][][]前两个元素表示横纵坐标(计算方式:题目中的二维数组坐标分别/3,得到grid数组对应的坐标)第三个元素表示1-9是否被占用
代码实现
java
class Solution {
boolean[][] row = new boolean[9][10];
boolean[][] col = new boolean[9][10];
boolean[][][] grid = new boolean[3][3][10];
public boolean isValidSudoku(char[][] board) {
for(int i = 0;i<9;i++){
for(int j = 0;j<9;j++){
if(board[i][j] != '.'){
int num = board[i][j] - '0';
if(row[i][num]||col[j][num]||grid[i/3][j/3][num])
return false;
else{
row[i][num] = true;
col[j][num] = true;
grid[i/3][j/3][num] = true;
}
}
}
}
return true;
}
}
4. 解数独(LC37)
题目描述

解题思路
与上一题的思路类似,利用三个数组帮助记录数字是否被使用过,使用过可以直接剪枝。

如图所示,可能存在第一个空填1,后面没有正确答案的情况。因此这个函数需要一个返回值,没有正确答案返回false。
代码实现
java
class Solution {
boolean[][] row = new boolean[9][10];
boolean[][] col = new boolean[9][10];
boolean[][][] grid = new boolean[3][3][10];
public void solveSudoku(char[][] board) {
//初始化
for(int i =0;i<9;i++){
for(int j = 0;j<9;j++){
if(board[i][j]!='.'){
int num = board[i][j]-'0';
row[i][num] = col[j][num] =grid[i/3][j/3][num] = true;
}
}
}
dfs(board);
}
boolean dfs(char[][] board){
for(int i = 0;i<9;i++){
for(int j = 0;j<9;j++){
//剪枝 是数字则直接跳过,是空则进入
if(board[i][j]=='.'){
for(int k =1;k<=9;k++){
//填空
if(!(row[i][k]||col[j][k]||grid[i/3][j/3][k])){
board[i][j] =(char)('0' + k);
row[i][k] = col[j][k] = grid[i/3][j/3][k] = true;
if(dfs(board))
return true;
//填错了 恢复现场
board[i][j] = '.';
row[i][k] = col[j][k] = grid[i/3][j/3][k] = false;
}
}
//1-9没有可以填入的,说明失败
return false;
}
}
}
//是数字,所以没有进入for循环
return true;
}
}
5. 单词搜索(LC79)
题目描述

解题思路
类似于"走迷宫",入口是单词首字母,出口是单词尾字母。因此首先要找到首字母。以字母为中心向四周搜索。
函数:boolean dfs( int i, int j ,int pos),传入字母在数组的位置,下一个字母在字符串的位置。返回值说明当前路径是否能找到单词。二维数组和单词转换为全局变量
注意: 在二维数组中走迷宫要警惕"走重路"的情况。可以新建一个二维数组记录是否经过;也可以直接修改原数组(风险比较大,尽量避免)。
代码实现
java
class Solution {
boolean[][] check;
char[][] board;
String word;
int m;
int n;
public boolean exist(char[][] _board, String _word) {
m = _board.length;
n = _board[0].length;
check = new boolean[m][n];
board = _board;
word = _word;
//找入口
char tar = word.charAt(0);
for(int i = 0;i<m;i++){
for(int j = 0;j<n;j++){
if(board[i][j]==tar){
check[i][j] = true;
if(dfs(i,j,1))
return true;
check[i][j] = false;
}
}
}
return false;
}
boolean dfs(int i, int j, int pos){
if(pos==word.length())
return true;
char tar = word.charAt(pos);
if(i>0 && !check[i-1][j] && board[i-1][j] == tar){
check[i-1][j] = true;
if(dfs(i-1,j,pos+1))
return true;
check[i-1][j] = false;
}
if(i<m-1 && !check[i+1][j] && board[i+1][j] == tar){
check[i+1][j] = true;
if(dfs(i+1,j,pos+1))
return true;
check[i+1][j] = false;
}
if(j>0 && !check[i][j-1] && board[i][j-1] == tar){
check[i][j-1] = true;
if(dfs(i,j-1,pos+1))
return true;
check[i][j-1] = false;
}
if(j<n-1 && !check[i][j+1] && board[i][j+1] == tar){
check[i][j+1] = true;
if(dfs(i,j+1,pos+1))
return true;
check[i][j+1] = false;
}
return false;
}
}
- 优化:利用向量数组搞定四个方向
java
int[] dx = {1,-1,0,0};
int[] dy = {0,0,1,-1};
for(int k = 0;k<4;k++){
int x = i+dx[k];
int y = j+dy[k];
if(x>=0 && x<m && y>=0 && y<n && !check[x][y] && board[x][y] == tar){
check[x][y] = true;
if(dfs(x,y,pos+1))
return true;
check[x][y] = false;
}
}
6. 黄金矿工(LC1219)
题目描述

解题思路
与上一题相似,因为需要遍历所有情况,递归函数不需要提前返回,不需要设置返回值。
代码实现
java
class Solution {
int[][] grid;
int m ;
int n;
boolean[][] check;
int ret = 0;
public int getMaximumGold(int[][] _grid) {
m = _grid.length;
n = _grid[0].length;
grid = _grid;
check = new boolean[m][n];
for(int i = 0;i<m;i++){
for(int j=0;j<n;j++){
if(grid[i][j]!=0){
check[i][j] = true;
dfs(i,j,0);
//恢复现场
check[i][j] = false;
}
}
}
return ret;
}
void dfs(int i ,int j,int sum){
sum += grid[i][j];
if(sum > ret)
ret = sum;
int[] dx = {1,-1,0,0};
int[] dy = {0,0,1,-1};
for(int k = 0;k<4;k++){
int x = dx[k]+i;
int y = dy[k]+j;
if(x>=0 && x < m && y >= 0 && y < n && !check[x][y] && grid[x][y]!=0){
check[x][y] = true;
dfs(x,y,sum);
//恢复现场
check[x][y] = false;
}
}
}
}
7. 不同路径III(LC980)
题目描述

解题思路
与前两个题类似。需要注意的是:要提前统计0的个数,保证最终不漏下格子。
代码实现
java
class Solution {
int[][] grid;
int m = 0;
int n = 0;
int step = 0;
boolean[][] check;
int ret = 0;
public int uniquePathsIII(int[][] _grid) {
grid = _grid;
m = _grid.length;
n = _grid[0].length;
check = new boolean [m][n];
//统计总共要走的步数和起点
int bx = 0;
int by = 0;
for(int i = 0;i<m;i++){
for(int j = 0;j<n;j++){
if(grid[i][j] == 1){
bx = i;
by = j;
}else if(grid[i][j] == 0)
step++;
}
}
check[bx][by] = true;
step += 2;
dfs(bx,by,1);
return ret;
}
void dfs(int i ,int j,int walked){
if(grid[i][j] == 2 ){
if(step == walked)
ret++;
return;
}
int[] dx = {0,0,1,-1};
int[] dy = {1,-1,0,0};
for(int k = 0;k<4;k++){
int x = i+dx[k];
int y = j+dy[k];
if(x>=0 && y>=0 && x<m && y<n && !check[x][y] && grid[x][y] != -1){
check[x][y] = true;
dfs(x,y,walked+1);
check[x][y] = false;
}
}
}
}