代码随想录刷题随记26-回溯last
3个hard。。。
332. 重新安排行程
leetcode链接
一个行程中,如果航班处理不好容易变成一个圈,成为死循环
解决方法就是一张票只能用一次。
如何按字典排序返回最小的行程组合。首先这说明返回一组就可以,所以需要及时的终止循环里面的递归。因此会使用判断是否要跳出for循环
if (backTracking(tickets, used)) {
return true;
}
字母序:
Collections.sort(tickets, (a, b) -> a.get(1).compareTo(b.get(1)));
其中每个列表代表一张票。Lambda表达式(a, b) -> a.get(1).compareTo(b.get(1))用作排序的比较器。
注意不能用==而是要用equals比较对象
class Solution {
List<String> path=new ArrayList<>();
//List<String> ret;
boolean backtrace(List<List<String>> tickets,boolean[]used){
if(path.size()==tickets.size()+1){
return true;
}
for(int i=0;i<tickets.size();i++){
if(used[i]||!tickets.get(i).get(0).equals(path.getLast()))
continue;
used[i]=true;
path.add(tickets.get(i).get(1));
if(backtrace(tickets, used)){
return true;
}
used[i]=false;
path.removeLast();
}
return false;
}
public List<String> findItinerary(List<List<String>> tickets) {
Collections.sort(tickets,(a,b)->a.get(1).compareTo(b.get(1)));
boolean [ ] used=new boolean[tickets.size()];
path.add("JFK");
backtrace(tickets, used);
return path;
}
}
这个解法会超时有一个测试案例无法通过
不超时解法:
感觉只是优化了map的查找?为啥就不超时了
class Solution {
private Deque<String> res;
private Map<String, Map<String, Integer>> map;
private boolean backTracking(int ticketNum){
if(res.size() == ticketNum + 1){
return true;
}
String last = res.getLast();
if(map.containsKey(last)){//防止出现null
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(ticketNum)) 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<>();
for(List<String> t : tickets){
Map<String, Integer> temp;
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<>();//升序Map
temp.put(t.get(1), 1);
}
map.put(t.get(0), temp);
}
res.add("JFK");
backTracking(tickets.size());
return new ArrayList<>(res);
}
}
51. N皇后
leetcode链接
由于回溯到每一层的时候只需要上面一层的record数据,所以record数组是可以不回溯的
用一维的record来记载的思路十分巧妙。,因为一位数组的索引天然就包含了第几行的信息,所以只需要记载列。
class Solution {
List<List<String>> ret=new ArrayList<>();
List<String> path=new LinkedList<>();
boolean isvalid(int [] record,int i,int row){
for(int j=0;j<row;j++){
if(record[j]==i||(Math.abs(record[j]-i)==(Math.abs(j-row))))
return false;
}
return true;
}
void backtrace(int row,int[]record,int n){
if(row==n){
ret.add(new ArrayList <>(path));
return;
}
for(int i=0;i<n;i++){
if(isvalid(record,i,row)){
String s="";
for(int k=0;k<i;k++)
s+='.';
s+="Q";
for(int k=i+1;k<n;k++)
s+='.';
path.add(s);
record[row]=i;
backtrace(row+1, record, n);
path.removeLast();
}
}
}
public List<List<String>> solveNQueens(int n) {
int []record=new int[n];
backtrace(0, record, n);
return ret;
}
}
37. 解数独
leetcode链接
整体思路和N皇后很像
判断棋盘是否合法有如下三个维度:
- 同行是否重复
- 同列是否重复
- 9宫格里是否重复
解题代码:
class Solution {
boolean isvalid(char[][] board,int row,int col,char val){
//检查行
for(int i=0;i<9;i++){
if(i==row)
continue;
if(board[i][col]==val)
return false;
}
//检查列
for(int i=0;i<9;i++){
if(i==col)
continue;
if(board[row][i]==val)
return false;
}
//检查九宫格
//确定边界
int tmp=row/3;
int start=tmp*3;
tmp=col/3;
int startcol=tmp*3;
for(int i=start;i<start+3;i++){
for(int j=startcol;j<startcol+3;j++){
if(board[i][j]==val)
return false;
}
}
return true;
}
boolean backtrace(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(board, i, j,k)){
board[i][j]=k;
if(backtrace(board))
return true;
board[i][j]='.';
}
}
return false;
}
}
return true;
}
public void solveSudoku(char[][] board) {
backtrace(board);
}
}