一、排列问题
排列是有序的,即{1,2}与{2,1}是两个排序。
1. 46【全排列】
- 题目: 给定一个不含重复数字的数组 nums ,返回其所有可能的全排列。你可以按任意顺序返回答案。
- 代码:
class Solution {
List<List<Integer>> ansList = new ArrayList<>();
List<Integer> path = new LinkedList<>();
public List<List<Integer>> permute(int[] nums) {
//排列是有序的,因此每一层用过的元素,在下一层还可以使用
//但是需要记录在同一递归循环中是否使用过该元素
//终止条件是path中的元素个数==nums.length
boolean[] used = new boolean[nums.length];
backtrack(nums,used);
return ansList;
}
public void backtrack(int[] nums,boolean[] used){
if(path.size() == nums.length) {
ansList.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < nums.length; i++) {
if(used[i]) continue;
path.add(nums[i]);
used[i] = true;
backtrack(nums,used);
path.removeLast();
used[i] = false;
}
}
}
2. 47【全排列 II】
- 题目: 给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
- 代码:
class Solution {
public List<List<Integer>> ansList = new ArrayList<>();
public List<Integer> path = new LinkedList<>();
public List<List<Integer>> permuteUnique(int[] nums) {
//本题与上一题的区别是本题含有重复数据
//因此在每一递归层需要判断前面是否出现过相同元素
//此外输入数组不一定是有序的,因此需要对数组进行排序
Arrays.sort(nums);
boolean[] used = new boolean[nums.length];
backtrack(nums,used);
return ansList;
}
public void backtrack(int[] nums,boolean[] used){
if(path.size() == nums.length){
ansList.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < nums.length; i++) {
if(i>0 && nums[i]==nums[i-1]) {
//如果上一个与当前元素相等的数被使用过,则代表两个数位于同一递归分支
//我们需要对同一递归层中相同的元素进行剪枝,也就是说上一个元素不能被使用
if(!used[i-1]) continue;
}
if(!used[i]){
path.add(nums[i]);
used[i] = true;
backtrack(nums,used);
path.removeLast();
used[i] = false;
}
}
}
}
二、棋盘问题
1. 51【N皇后】
- 题目: 按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
- 代码:
class Solution {
List<List<String>> ansList = new ArrayList<>();
public List<List<String>> solveNQueens(int n) {
//N皇后问题:皇后的位置不能同行,同列,对角
//棋盘是个n×n的数组,因此可以将第i行看成树的第i层
//第j列看成树的第j个分支
char[][] chessboard = new char[n][n];
for(char[] cArr: chessboard){
Arrays.fill(cArr,'.');
}
backtrack(n,chessboard,0);
return ansList;
}
public void backtrack(int n,char[][] chessboard,int row){
if(row == n){
List<String> sList = new ArrayList<>();
for (char[] cArr:chessboard) {
sList.add(String.valueOf(cArr));
}
ansList.add(sList);
}
for (int col = 0; col <n ; col++) {
if(isValid(n,chessboard,row,col)){
chessboard[row][col] = 'Q';
backtrack(n,chessboard,row+1);
chessboard[row][col] ='.';
}
}
}
public boolean isValid(int n,char[][] chessboard,int row,int col){
//因为一行只能有1个皇后,所以不需要检查第row行是否合法
for (int i = 0; i < row; i++) {
if(chessboard[i][col] == 'Q'){
return false;
}
}
//检查左上角
for (int i=row-1,j=col-1; i>=0 && j>=0; i--,j--) {
if(chessboard[i][j] == 'Q'){
return false;
}
}
//检查右上角
for (int i=row-1,j=col+1; i>=0 && j<n; i--,j++) {
if(chessboard[i][j] == 'Q'){
return false;
}
}
return true;
}
}
2. 52【N皇后Ⅱ】
- 题目: n皇后问题 研究的是如何将 n 个皇后放置在 n × n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回 n 皇后问题 不同的解决方案的数量。 - 代码:
class Solution {
int num = 0;
public int totalNQueens(int n) {
char[][] board = new char[n][n];
for(char[] c:board){
Arrays.fill(c,'.');
}
backtrack(n,board,0);
return num;
}
public void backtrack(int n,char[][] board,int row){
if(row == n){
num++;
return;
}
for (int col = 0; col < n; col++) {
if(isValid(row,col,board,n)){
board[row][col] = 'Q';
backtrack(n,board,row+1);
board[row][col] = '.';
}
}
}
public boolean isValid(int row,int col,char[][] board,int n){
for (int i = 0; i < row; i++) {
if(board[i][col] == 'Q'){
return false;
}
}
for(int i=row-1,j=col-1;i>=0 && j>=0;i--,j--){
if(board[i][j] == 'Q'){
return false;
}
}
for(int i=row-1,j=col+1;i>=0 && j<n;i--,j++){
if(board[i][j] == 'Q'){
return false;
}
}
return true;
}
}
3. 37【解数独】
- 题目: 编写一个程序,通过填充空格来解决数独问题。数独部分空格内已填入了数字,空白格用 ‘.’ 表示。
数独的解法需 遵循如下规则:- 数字 1-9 在每一行只能出现一次。
- 数字 1-9 在每一列只能出现一次。
- 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
- 代码:
class Solution {
public void solveSudoku(char[][] board) {
//数独问题与N皇后问题不同,N皇后每次递归对行进行一次操作
//而数独需要多次对同一行进行操作
//所以使用两个for循环来进行递归
//每次for循环找到符合条件的值就不在继续进行,因此回溯需要有boolean返回值
backtrack(board);
}
public boolean backtrack(char[][] board){
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if(board[i][j] != '.') continue;
for (char k = '1'; k <= '9' ; k++) {
if(isValid(i,j,k,board)){
board[i][j] = k;
if(backtrack(board)) return true;
board[i][j] = '.';
}
}
return false;
}
}
return true;
}
public boolean isValid(int row,int col,char k,char[][] board){
//判断行
for (int i = 0; i < 9; i++) {
if(board[i][col] == k){
return false;
}
}
//判断列
for (int i = 0; i < 9; i++) {
if(board[row][i] == k){
return false;
}
}
for (int i = (row/3)*3; i < (row/3)*3+3; i++) {
for (int j = (col/3)*3; j < (col/3)*3+3; j++) {
if(board[i][j] == k){
return false;
}
}
}
return true;
}
}