学习目标:
60天训练营打卡计划!
学习内容:
332.重新安排行程
- 这这这,着实是上强度了。。。
- 对HashMap的API不熟悉,然后遇到了双层map,很自闭。
- 难点一:如何把List
tickets和回溯的树形结构画上联系?
设计了HashMap> map的数据结构,其中第一个String存的是出发地;Map是一个树形映射,按照升序构造,Map中的String存的是目的地,Integer存的是到目的地的次数。在主程序构造出这样的结构。
- 难点二:
递归的结束条件:res.size() == size + 1,由题干可知。
主要是数据结构太复杂,导致操作很复杂,实现也很臃肿,但确实让我大开眼界。
class Solution {
private Deque<String> res;
private HashMap<String, Map<String, Integer>> map;
private boolean backtracking(int size){
if(res.size() == size + 1){
return true;
}
String last = res.getLast();
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);
if(backtracking(size)) return true;
res.removeLast();
target.setValue(count);
}
}
}
return false;
}
public List<String> findItinerary(List<List<String>> tickets) {
map = new HashMap<String, Map<String, Integer>>();
res = new LinkedList<>();
Map<String, Integer> temp = null;
for(List<String> t :tickets){
if(map.containsKey(t.get(0))){
temp = map.get(t.get(0));
temp.put(t.get(1), temp.getOrDefault(t.get(1), 0) + 1);
}
else{
temp = new TreeMap<>();
temp.put(t.get(1), 1);
}
map.put(t.get(0),temp);
}
res.add("JFK");
backtracking(tickets.size());
return new ArrayList<>(res);
}
}
51. N皇后
- 为了使用 List cheeseBoard = new ArrayList<>();,更符合题目最后的返回值,所以用了StringBuilder类,使实现的时间和空间都比较差。
- 难点一:isValid()函数的实现
因为皇后是逐行放入的,每行有且仅有一个,故只需要判断该列,该45度线和135度线是否已经有皇后?若无则满足题意。
- 难点二:List中对String的修改操作
使用StringBuilder类先把位置处的元素删除,再使用insert方法插入需要的元素。deleteCharAt(rank); insert(rank, c);
但是确实有长进了,看到String不再是束手无策,脑子里会出现StringBuilder 类,虽然用的还不是很熟练。
- 难点三:回溯的实现
最小的回溯条件很像切割IP的判断
先假设某个放了皇后,如果可以放才会真的放置一个皇后,并进入回溯过程。
class Solution {
List<List<String>> res = new ArrayList<>();
List<String> cheeseBoard = new ArrayList<>();
private boolean isValid(int row, int rank, List<String> cheeseBoard){
for(int j = 0; j < row; j++){
if(cheeseBoard.get(j).charAt(rank) == 'Q') return false;
}
for(int i = row-1, j = rank-1; i >= 0 && j >= 0; i--,j--){
if(cheeseBoard.get(i).charAt(j) == 'Q') return false;
}
for(int i = row-1, j = rank+1; j < cheeseBoard.size() && i >= 0; j++,i--){
if(cheeseBoard.get(i).charAt(j) == 'Q') return false;
}
return true;
}
private void backtracking(List<String> cheeseBoard, int n, int row){
if(row >= n){
res.add(new ArrayList<>(cheeseBoard));
return;
}
for(int i = 0; i < n; i++){
if(isValid(row, i, cheeseBoard)){
cheeseBoard.set(row, createStr(i, cheeseBoard.get(row), 'Q'));
backtracking(cheeseBoard, n, row+1);
cheeseBoard.set(row, createStr(i, cheeseBoard.get(row), '.'));
}
}
return;
}
private String createStr(int rank, String s, char c){
StringBuilder sb = new StringBuilder(s);
sb.deleteCharAt(rank);
sb.insert(rank, c);
return sb.toString();
}
public List<List<String>> solveNQueens(int n) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) {
sb.append('.');
}
for (int i = 0; i < n; i++) {
cheeseBoard.add(sb.toString());
}
backtracking(cheeseBoard, n, 0);
return res;
}
}
37. 解数独
- 和普通的回溯是不一样的,因为他没有明确的结束条件,所以第一次看一定会很懵。
- 和Q女皇相似,本题也需要对最后的结果有一个合理性地判断函数:
- 同行不重复
- 同列不重复
- 9宫格不重复 — 固定的9 * 9大小的棋盘,可以被分为9个3 *3的小棋盘,所以首先要判断当前的节点落在哪个小棋盘?小棋盘的行列都乘3就是当前小棋盘的初始的位置。
- 考虑回溯的策略:
玩法就是对空格填入1-9的数字,所以先对大棋盘进行巡查,找到为空的位置,再使用isValid()判断当前值是否合理?合理则真正放入元素,并进行递归。
class Solution {
private boolean isValid(int row, int col, char val, char[][] board){
for(int i = 0; i < 9; i++)
if(board[i][col] == val) return false;
for(int i = 0; i < 9; i++)
if(board[row][i] == val) return false;
int r_start = (row / 3) * 3;
int c_start = (col / 3) * 3;
for(int i = r_start; i < r_start + 3; i++)
for(int j = c_start; j < c_start + 3; j++)
if(board[i][j] == val) return false;
return true;
}
private boolean backtracking(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(isValid(i, j, (char)(k + '0'), board)){
board[i][j] = (char)(k + '0');
if(backtracking(board)) return true;
board[i][j] = '.';
}
}
return false;
}
}
}
return true;
}
public void solveSudoku(char[][] board) {
backtracking(board);
}
}
学习时间: