所用代码 java
重新安排行程 LeetCode 332
题目链接:重新安排行程 LeetCode 332 - 困难
思路
无。题都看不懂
class Solution {
Deque<String> res;
// 存放 起点机场 -> 终点机场,航班次数
Map<String, Map<String, Integer>> map;
public List<String> findItinerary(List<List<String>> tickets) {
res = new LinkedList<>();
map = new HashMap<>();
for (List<String> t : tickets){
Map<String, Integer> temp;
// 如果航班的出发点已存在
if (map.containsKey(t.get(0))){
// 先把该机场的终点赋值为temp
temp = map.get(t.get(0));
// 把另一航班赋值给temp
// 没有就把value赋值为 1,有就加 1,记录多少趟航班,以免出现重复
temp.put(t.get(1), temp.getOrDefault(t.get(1), 0) + 1);
}else {
// 没有该目的地的信息,就新建一个treemap来存目的地,treemap的key自动按升序排序
temp = new TreeMap<>();
temp.put(t.get(1), 1);
}
// 最后把起点和终点保存到一个map里面去
map.put(t.get(0), temp);
}
// 此时map里面就存了所有机场的起点=>终点的信息,以及航班的趟数
res.add("JFK"); // 起点机场一是 JFK
backtracking(tickets.size());
return new ArrayList<>(res);
}
// 只需要传入多少趟行程,因为返回的结果正好等于行程数+1,就可覆盖完所有航程
// 即n条航线有n+1个点
public boolean backtracking(int ticketNum){
// 终止条件
if (res.size() == ticketNum + 1){
return true;
}
// 每次都是从末尾开始一个一个添加
String last = res.getLast();
// 第一个从 JFK 开始出发
if (map.containsKey(last)){
for (Map.Entry<String, Integer> target : map.get(last).entrySet()){
// 记录到该机场的躺数还有没有
int count = target.getValue();
if (count > 0){
// 有就加进去
res.add(target.getKey());
target.setValue(count-1);
boolean result = backtracking(ticketNum);
// 如果每次都添加进去了,当满足终止条件后就返回的true,并一直往上返回true
if (result) return true;
// 回溯操作
res.removeLast();
// 前面并没有修改count的值,所以回溯是不能count+1
target.setValue(count);
}
}
}
// 最后返回false是该机场没有下一目的地,而我们的航程还没遍历完
return false;
}
}
总结
太难了~~
N皇后 LeetCode 51
题目链接:N皇后 LeetCode 51 - 困难
思路
无。
class Solution {
List<List<String>> res = new ArrayList<>();
public List<List<String>> solveNQueens(int n) {
char[][] chessboard = new char[n][n];
for (char[] c : chessboard) {
Arrays.fill(c, '.');
}
backtracking(chessboard, 0, n);
return res;
}
// row 表示行(递归宽度),n表示列(递归深度) 也表示棋盘的大小
public void backtracking(char[][] chessboard, int row, int n){
if (n == row){
res.add(getList(chessboard));
return;
}
for (int col = 0; col < n; col++) {
// col 表示每层遍历的列的位置
// n 在这里表示棋盘的大小
if (isValid(chessboard, row, col, n)){
chessboard[row][col] = 'Q';
// 每层递归从第二行开始 row+1
backtracking(chessboard, row+1, n);
chessboard[row][col] = '.'; // 回溯
}
}
return;
}
// 只用判断 上方,左斜45°,右斜45°
// 同一行由上边for循环控制
public boolean isValid(char[][] chessboard, int row, int col, int n){
// 检查上方是否有皇后
for (int i = 0; i < row; i++) {
// 第 i 行,col列存在,返回false
if (chessboard[i][col] == 'Q'){
return false;
}
}
// 检查左斜上45°是否有皇后
// 从row行,col列的 左上第一个位置开始判断
for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
if (chessboard[i][j] == 'Q'){
return false;
}
}
// 检查右斜上45°是否有皇后
// 从row行,col列的 右上第一个位置开始判断
for (int i = row - 1, j = col + 1 ; i >= 0 && j < n; i--, j++) {
if (chessboard[i][j] == 'Q'){
return false;
}
}
// 可以放皇后
return true;
}
public List<String> getList(char[][] chessboard){
List<String> list = new ArrayList<>();
for (char[] c : chessboard) {
list.add(String.copyValueOf(c));
}
return list;
}
}
总结
本题为经典回溯题,难度有点大,若学会了便可对回溯有更深层的理解。
解数独 LeetCode 37
题目链接:解数独 LeetCode 37 - 困难
思路
无。
n皇后是一个for循环遍历一行,一个递归遍历一列,且每一行只需要填一个数。
本题是需要填一行的所有数,所以需要两个for循环,一个遍历行,一个遍历列,最后递归进行判断放某个数字可否行
class Solution {
public void solveSudoku(char[][] board) {
backtracking(board);
}
// board : 9 x 9
public boolean backtracking(char[][] board){
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
// 在board[i][j]的情况下判断是否可以放下数字
if (board[i][j] == '.'){
for (char k = '1'; k <= '9'; k++) {
// 棋盘在i行j列的位置可以放下k
if (isValid(board, i, j, k)){
board[i][j] = k;
boolean result = backtracking(board);
// 如果每次放的数都合理就一直往下放,然后把棋盘放满
// 放满棋盘之后,下面就这步就一直向上返回true,直到最后回到第一步
if (result) return true;
board[i][j] = '.';
}
}
// 棋盘该位置所有数字都没法放,就返回false
return false;
}
}
}
// 如果所有棋盘都填满了,就返回ture
return true;
}
// 判断棋盘board在i行j列能否放数字k
// 3x3的棋盘是否都出现过所有数字
public boolean isValid(char[][] board, int i, int j, char k){
// 判断第 i 行是否合理 -- i行的每一列
for (int col = 0; col < board.length; col++) {
if (board[i][col] == k) return false;
}
// j 列是否合理 -- j列的每一个行
for (int row = 0; row < board[j].length; row++) {
if (board[row][j] == k) return false;
}
// 判断3x3棋盘是否合理
// 把行和列放在每3x3小格的第一个数
int startRow = (i / 3) * 3;
int startCol = (j / 3) * 3;
// 每次只需要判断从这个数字开始往后往下数3行就行了
for (int m = startRow; m < startRow + 3; m++) {
for (int n = startCol; n < startCol + 3; n++) {
if (board[m][n] == k) return false;
}
}
return true;
}
}
总结
本题返回值之所以是布尔类型,原因是本题我们只需要返回一个结果就行,搜索到正确的结果就直接返回。而之前的题返回值是void,是因为我们要搜索所有的结果,并保存每一个结果。