文章目录
一、重新安排行程
java
class Solution {
private LinkedList<String> result;
// Map<出发机场, map<到达机场, 航班次数>> targets
private Map<String, Map<String, Integer>> targets;
public List<String> findItinerary(List<List<String>> tickets) {
targets = new HashMap<String, Map<String, Integer>>();
result = new LinkedList<>();
// 把tickets的所有航班信息转换为:Map<出发机场, map<到达机场, 航班次数>> 的map集合
for(List<String> t : tickets){
Map<String, Integer> temp;
if(targets.containsKey(t.get(0))){
temp = targets.get(t.get(0));
temp.put(t.get(1), temp.getOrDefault(t.get(1), 0) + 1);
}else{
// 升序Map,底层红黑树,保证了根据出发机场获取的map<到达机场, 航班次数>是有序的
temp = new TreeMap<>();
temp.put(t.get(1), 1);
}
targets.put(t.get(0), temp);
}
result.add("JFK"); // 起始机场
backtracking(tickets.size()); // 传入机票的张数,用于终止条件判断
return new ArrayList<>(result);
}
// 由于根据出发机场获取的map<到达机场, 航班次数>是有序的,所以找到的第一个路径就是需要的字典排序最小的行程组合。
// 所以设置布尔类型返回值,找到结果停止遍历所有树,立马返回答案。
private boolean backtracking(int ticketNum){
if(result.size() == ticketNum + 1){
return true;
}
// 获取起始位置
String last = result.getLast();
// 根据起始位置 确定能找到出发地为起始位置的机票
if(targets.containsKey(last)){
// 遍历该起始位置能到达的entry键值对<到达机场, 航班次数>
for(Map.Entry<String, Integer> target : targets.get(last).entrySet()){
// count为 该<起始位置, 到达机场> 的机票张数
int count = target.getValue();
// count>0(有票)的情况
if(count > 0){
result.add(target.getKey()); // 该目的地存入result中
target.setValue(count - 1); // 票数 - 1
if(backtracking(ticketNum)) {
return true;
}
target.setValue(count); // 回溯
result.removeLast(); // 回溯
}
}
}
return false;
}
}
二、N皇后
注:
row.append(".".repeat(Math.max(0, n))); 中 String.repeat是jdk11的方法
java
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
public class Solution {
private int n;
/**
* 记录某一列是否放置了皇后
*/
private boolean[] col;
/**
* 记录主对角线上的单元格是否放置了皇后
*/
private boolean[] main;
/**
* 记录了副对角线上的单元格是否放置了皇后
*/
private boolean[] sub;
private List<List<String>> res;
public List<List<String>> solveNQueens(int n) {
res = new ArrayList<>();
if (n == 0) {
return res;
}
// 设置成员变量,减少参数传递,具体作为方法参数还是作为成员变量,请参考团队开发规范
this.n = n;
this.col = new boolean[n];
//这里的 2 * n - 1 刚好是 主对角线或者副对角线的数量
this.main = new boolean[2 * n - 1];
this.sub = new boolean[2 * n - 1];
Deque<Integer> path = new ArrayDeque<>();
dfs(0, path);
return res;
}
private void dfs(int row, Deque<Integer> path) {
if (row == n) {
// 深度优先遍历到下标为 n,表示 [0.. n - 1] 已经填完,得到了一个结果
List<String> board = convert2board(path);
res.add(board);
return;
}
// 针对下标为 row 的每一列,尝试是否可以放置
for (int j = 0; j < n; j++) {
//对于当前行的每一列,判断是否可以放置皇后。判断的条件是当前列未被占据,并且当前位置所在的主对角线和副对角线上都没有皇后
//主对角线特点 左上 -> 右下:横坐标 - 纵坐标的值固定,为了保证值非负
//计算逻辑为 row - j + n - 1
//副对角线特点 左下 -> 右上:横纵坐标之和固定
if (!col[j] && !main[row - j + n - 1] && !sub[row + j]) {
// 如果符合条件,将当前列号保存,并将对应的列,主对角线,副对角线设置为true
path.addLast(j);
col[j] = true;
main[row - j + n - 1] = true;
sub[row + j] = true;
//进入下一层递归,也是下一行递归;
dfs(row + 1, path);
//返回递归的时候移除递归的影响
sub[row + j] = false;
main[row - j + n - 1] = false;
col[j] = false;
path.removeLast();
}
}
}
private List<String> convert2board(Deque<Integer> path) {
List<String> board = new ArrayList<>();
for (Integer num : path) {
StringBuilder row = new StringBuilder();
row.append(".".repeat(Math.max(0, n)));
//左闭右开,包含左边,但是不包含右边
row.replace(num, num + 1, "Q");
board.add(row.toString());
}
return board;
}
}
三、解数独
java
class Solution {
public void solveSudoku(char[][] board) {
solveSudokuHelper(board);
}
private boolean solveSudokuHelper(char[][] board){
//「一个for循环遍历棋盘的行,一个for循环遍历棋盘的列,
// 一行一列确定下来之后,递归遍历这个位置放9个数字的可能性!」
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++){ // (i, j) 这个位置放k是否合适
if (isValidSudoku(i, j, k, board)){
board[i][j] = k;
if (solveSudokuHelper(board)){ // 如果找到合适一组立刻返回
return true;
}
board[i][j] = '.';
}
}
// 9个数都试完了,都不行,那么就返回false
return false;
// 因为如果一行一列确定下来了,这里尝试了9个数都不行,说明这个棋盘找不到解决数独问题的解!
// 那么会直接返回, 「这也就是为什么没有终止条件也不会永远填不满棋盘而无限递归下去!」
}
}
// 遍历完没有返回false,说明找到了合适棋盘位置了
return true;
}
/**
* 判断棋盘是否合法有如下三个维度:
* 同行是否重复
* 同列是否重复
* 9宫格里是否重复
*/
private boolean isValidSudoku(int row, int col, char val, char[][] board){
// 同行是否重复
for (int i = 0; i < 9; i++){
if (board[row][i] == val){
return false;
}
}
// 同列是否重复
for (int j = 0; j < 9; j++){
if (board[j][col] == val){
return false;
}
}
// 9宫格里是否重复
int startRow = (row / 3) * 3;
int startCol = (col / 3) * 3;
for (int i = startRow; i < startRow + 3; i++){
for (int j = startCol; j < startCol + 3; j++){
if (board[i][j] == val){
return false;
}
}
}
return true;
}
}