diff --git a/Leecode/src/main/java/com/markilue/leecode/backtrace/T13_0_FindItinerary.java b/Leecode/src/main/java/com/markilue/leecode/backtrace/T13_0_FindItinerary.java new file mode 100644 index 0000000..acdf94c --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/backtrace/T13_0_FindItinerary.java @@ -0,0 +1,207 @@ +package com.markilue.leecode.backtrace; + +import org.junit.Test; + +import java.util.*; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.backtrace + *@Author: dingjiawen + *@CreateTime: 2023-02-03 10:21 + *@Description: + * TODO 力扣332 重新安排行程: + * 给你一份航线列表 tickets ,其中 tickets[i] = [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。 + * 所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。如果存在多种有效的行程,请你按字典排序返回最小的行程组合。 + * 例如,行程 ["JFK", "LGA"] 与 ["JFK", "LGB"] 相比就更小,排序更靠前。 + * 假定所有机票至少存在一种合理的行程。且所有的机票 必须都用一次 且 只能用一次。 + *@Version: 1.0 + */ +public class T13_0_FindItinerary { + + @Test + public void test() { + List> tickets = new ArrayList<>(); + tickets.add(Arrays.asList("MUC", "LHR")); + tickets.add(Arrays.asList("JFK", "MUC")); + tickets.add(Arrays.asList("SFO", "SJC")); + tickets.add(Arrays.asList("LHR", "SFO")); + findItinerary(tickets); + System.out.println(result); + } + + @Test + public void test1() { + List> tickets = new ArrayList<>(); + tickets.add(Arrays.asList("JFK", "SFO")); + tickets.add(Arrays.asList("JFK", "ATL")); + tickets.add(Arrays.asList("SFO", "ATL")); + tickets.add(Arrays.asList("ATL", "JFK")); + tickets.add(Arrays.asList("ATL", "SFO")); + findItinerary1(tickets); + System.out.println(result); + } + + + @Test + public void test2() { + List> tickets = new ArrayList<>(); + tickets.add(Arrays.asList("JFK", "KUL")); + tickets.add(Arrays.asList("JFK", "NRT")); + tickets.add(Arrays.asList("NRT", "JFK")); +// tickets.add(Arrays.asList("ATL", "JFK")); +// tickets.add(Arrays.asList("ATL", "SFO")); + findItinerary1(tickets); + System.out.println(result); + } + + + List cur = new ArrayList<>(); + List result = new ArrayList<>(); + List> result1 = new ArrayList<>(); + + public List findItinerary(List> tickets) { + Collections.sort(tickets, new Comparator>() { + @Override + public int compare(List o1, List o2) { + return o1.get(1).compareTo(o2.get(0)); + } + }); + backtracking(tickets, 0, "", new boolean[tickets.size()]); + return result; + } + + public void backtracking(List> tickets, int level, String last, boolean[] used) { + if (level == tickets.size()) { + //所有机票用完了 + result = new ArrayList<>(cur); + return; + } + + for (int i = 0; i < tickets.size(); i++) { + if (level == 0) { + if (!"JFK".equals(tickets.get(i).get(0))) { + //起始地一定要是JFK + continue; + } + used[i] = true; + List lines = tickets.get(i); + cur.add(lines.get(0)); + cur.add(lines.get(1)); + backtracking(tickets, level + 1, lines.get(1), used); + if (result != null) { + return; + } + used[i] = false; + cur.clear(); + } else { + if (used[i] || !last.equals(tickets.get(i).get(0))) { + continue; + } + used[i] = true; + cur.add(tickets.get(i).get(1)); + backtracking(tickets, level + 1, tickets.get(i).get(1), used); + if (result != null) { + return; + } + used[i] = false; + cur.remove(cur.size() - 1); + } + } + } + + + /** + * 代码优化 + * 速度击败26.88%,内存击败43.82% 15ms + * @param tickets + * @return + */ + public List findItinerary1(List> tickets) { + Collections.sort(tickets, (a, b) -> a.get(1).compareTo(b.get(1))); + cur.add("JFK"); + backtracking1(tickets, 0, "JFK", new boolean[tickets.size()]); + return result; + } + + public void backtracking1(List> tickets, int level, String last, boolean[] used) { + if (level == tickets.size()) { + //所有机票用完了 + result = new ArrayList<>(cur); + return; + } + + for (int i = 0; i < tickets.size(); i++) { + if (used[i] || !last.equals(tickets.get(i).get(0))) { + continue; + } + used[i] = true; + cur.add(tickets.get(i).get(1)); + backtracking1(tickets, level + 1, tickets.get(i).get(1), used); + if (result != null && result.size() != 0) { + return; + } + used[i] = false; + cur.remove(cur.size() - 1); + } + + } + + + /** + * 官方解法:使用一个优先队列进行排序,好像默认了按字典序就一定能遍历完所有的票 + * 速度击败98.38%,内存击败5.4% 5ms + */ + Map> map = new HashMap>(); + List itinerary = new LinkedList(); + + public List findItinerary2(List> tickets) { + for (List ticket : tickets) { + String src = ticket.get(0), dst = ticket.get(1); + if (!map.containsKey(src)) { + map.put(src, new PriorityQueue()); + } + map.get(src).offer(dst); + } + dfs("JFK"); + Collections.reverse(itinerary); + return itinerary; + } + + public void dfs(String curr) { + while (map.containsKey(curr) && map.get(curr).size() > 0) { + String tmp = map.get(curr).poll(); + dfs(tmp); + } + itinerary.add(curr); + } + + + /** + * 官方最快 4ms,不用反转了,进一步优化 + */ + private List resList = new LinkedList<>(); + + public List findItinerary3(List> tickets) { + for (List ticket : tickets) { + String src = ticket.get(0); + String dst = ticket.get(1); + if (!map.containsKey(src)) { + PriorityQueue pq = new PriorityQueue<>(); + map.put(src, pq); + } + map.get(src).add(dst); + } + dfs1("JFK"); + return resList; + } + + private void dfs1(String src) { + PriorityQueue pq = map.get(src); + while (pq != null && !pq.isEmpty()){ + dfs1(pq.poll()); + } + + ((LinkedList) resList).addFirst(src); + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/backtrace/SolveNQueens.java b/Leecode/src/main/java/com/markilue/leecode/backtrace/T13_1_SolveNQueens.java similarity index 99% rename from Leecode/src/main/java/com/markilue/leecode/backtrace/SolveNQueens.java rename to Leecode/src/main/java/com/markilue/leecode/backtrace/T13_1_SolveNQueens.java index 1ca63be..3cdf38d 100644 --- a/Leecode/src/main/java/com/markilue/leecode/backtrace/SolveNQueens.java +++ b/Leecode/src/main/java/com/markilue/leecode/backtrace/T13_1_SolveNQueens.java @@ -17,7 +17,7 @@ import java.util.*; * 每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。 * @Version: 1.0 */ -public class SolveNQueens { +public class T13_1_SolveNQueens { @Test public void test() { diff --git a/Leecode/src/main/java/com/markilue/leecode/backtrace/second/T13_1_SolveNQueens.java b/Leecode/src/main/java/com/markilue/leecode/backtrace/second/T13_1_SolveNQueens.java new file mode 100644 index 0000000..164c42b --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/backtrace/second/T13_1_SolveNQueens.java @@ -0,0 +1,201 @@ +package com.markilue.leecode.backtrace.second; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.backtrace.second + *@Author: dingjiawen + *@CreateTime: 2023-02-03 12:43 + *@Description: + * TODO 力扣51题 N皇后: + * 按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 + * n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。 + * 给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。 + * 每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。 + *@Version: 1.0 + */ +public class T13_1_SolveNQueens { + + @Test + public void test() { + System.out.println(solveNQueens1(4)); + } + + List> result = new ArrayList<>(); + List cur = new ArrayList<>(); + + public List> solveNQueens(int n) { + backtracking(n, 0, new boolean[n][n]); + return result; + + } + + + /** + * 速度击败41.44%,内存击败78.99% 4ms + * @param n + * @param level + * @param used + */ + public void backtracking(int n, int level, boolean[][] used) { + if (level == n) { + for (int i = 0; i < n; i++) { + StringBuilder builder = new StringBuilder(); + for (int j = 0; j < n; j++) { + if(used[i][j]){ + builder.append("Q"); + }else { + builder.append("."); + } + } + cur.add(builder.toString()); + } + result.add(new ArrayList<>(cur)); + cur.clear(); + return; + } + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < n; i++) { + if (isFit(used, level, i)) { + used[level][i] = true; + backtracking(n, level + 1, used); + used[level][i] = false; + } + } + + } + + public boolean isFit(boolean[][] used, int row, int col) { + + //看看列 + for (int i = 0; i < row; i++) { + if (used[i][col]) { + return false; + } + } + + //看看行,行不用看,一定不会 + //看看斜边 + for (int i = row - 1, j = col - 1, k = col + 1; i >= 0; i--, j--, k++) { + if (j >= 0 && used[i][j]) { + return false; + } + if (k < used.length && used[i][k]) { + return false; + } + } + + return true; + + } + + + /** + * 代码随想录式,不用一个一个append速度提高 + * 速度击败92.55%,内存击败48.62% 2ms + */ + List> res = new ArrayList<>(); + + public List> solveNQueens1(int n) { + char[][] chessboard = new char[n][n]; + for (char[] c : chessboard) { + Arrays.fill(c, '.'); + } + backTrack(n, 0, chessboard); + return res; + } + + + public void backTrack(int n, int row, char[][] chessboard) { + if (row == n) { + res.add(Array2List(chessboard)); + return; + } + + for (int col = 0;col < n; ++col) { + if (isFitWithChar(chessboard,row, col)) { + chessboard[row][col] = 'Q'; + backTrack(n, row+1, chessboard); + chessboard[row][col] = '.'; + } + } + + } + + + public List Array2List(char[][] chessboard) { + List list = new ArrayList<>(); + + for (char[] c : chessboard) { + list.add(String.copyValueOf(c)); + } + return list; + } + + public boolean isFitWithChar(char[][] used, int row, int col) { + + //看看列 + for (int i = 0; i < row; i++) { + if (used[i][col]=='Q') { + return false; + } + } + + //看看行,行不用看,一定不会 + //看看斜边 + for (int i = row - 1, j = col - 1, k = col + 1; i >= 0; i--, j--, k++) { + if (j >= 0 && used[i][j]=='Q') { + return false; + } + if (k < used.length && used[i][k]=='Q') { + return false; + } + } + + return true; + + } + + + /** + * 官方最快 0ms + * 通过大量的位运算,和Arrays.fill加快速度 + * @param n + * @return + */ + public List> solveNQueens2(int n) { + List> ans=new ArrayList<>(); + int[] queen=new int[n]; + Arrays.fill(queen,-1); + backtrack(ans,n,queen,0,0,0,0); + return ans; + } + public void backtrack(List> ans,int n,int[] queen,int rows,int col,int diagonal1,int diagonal2){ + if(rows==n){ + List list=new ArrayList<>(); + for(int i=0;i>1); + queen[rows]=-1; + } + } + + } +}