#Java #回溯 #Hard
开源学习资料
Feeling and experiences:
以下题目都比较困难,第一遍刷的时候,先看懂即可。
重新安排行程:力扣题目链接
给你一份航线列表 tickets
,其中 tickets[i] = [fromi, toi]
表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。
所有这些机票都属于一个从 JFK
(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK
开始。如果存在多种有效的行程,请你按字典排序返回最小的行程组合。
- 例如,行程
["JFK", "LGA"]
与["JFK", "LGB"]
相比就更小,排序更靠前。
假定所有机票至少存在一种合理的行程。且所有的机票 必须都用一次 且 只能用一次。
class Solution {Map<String, PriorityQueue<String>> map = new HashMap<String, PriorityQueue<String>>();List<String> itinerary = new LinkedList<String>();public List<String> findItinerary(List<List<String>> tickets) {for (List<String> ticket : tickets) {String src = ticket.get(0), dst = ticket.get(1);if (!map.containsKey(src)) {map.put(src, new PriorityQueue<String>());}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);}
}
1. 问题理解:
• 目标是根据给定的航班列表,找到一条从”JFK”出发,覆盖所有航班至少一次的行程。
• 如果存在多种可能的行程,选择字典序最小的那一条。
2. 关键概念:
• 涉及到类似于欧拉路径的概念,即通过图中的所有边恰好一次的路径。
3. 数据结构选择:
• 使用哈希表(Map<String, PriorityQueue<String>> map)来存储从每个机场出发的航班。键是出发机场,值是一个优先级队列,包含按字典序排列的目的地机场。
• 使用链表(List<String> itinerary)来存储最终的行程。
4. 算法流程:
• 构建图:遍历每张机票,构建出发地到目的地的映射,存入哈希表。
• 深度优先搜索(DFS):从”JFK”开始进行DFS,按字典序探索每个机场的所有目的地。
• 递归终止条件:当一个机场没有更多可飞往的目的地时,将其加入行程。
• 行程构建:由于DFS是先达到最深层再回溯,所以最终的行程是逆序的,需要反转链表得到正确顺序。
5. 特殊情况处理:
• 当存在多条路径时,优先级队列确保按字典序选择路径。
• 由于题目中的图可能不是传统意义上的欧拉图(可能不包含所有边),算法的目标是覆盖所有给定的边。
N皇后:力扣题目链接
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n 皇后问题 研究的是如何将 n
个皇后放置在 n×n
的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n
,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q'
和 '.'
分别代表了皇后和空位。
class Solution {public List<List<String>> solveNQueens(int n) {List<List<String>> solutions = new ArrayList<List<String>>();int[] queens = new int[n];Arrays.fill(queens, -1);Set<Integer> columns = new HashSet<Integer>();Set<Integer> diagonals1 = new HashSet<Integer>();Set<Integer> diagonals2 = new HashSet<Integer>();backtrack(solutions, queens, n, 0, columns, diagonals1, diagonals2);return solutions;}public void backtrack(List<List<String>> solutions, int[] queens, int n, int row, Set<Integer> columns, Set<Integer> diagonals1, Set<Integer> diagonals2) {if (row == n) {List<String> board = generateBoard(queens, n);solutions.add(board);} else {for (int i = 0; i < n; i++) {if (columns.contains(i)) {continue;}int diagonal1 = row - i;if (diagonals1.contains(diagonal1)) {continue;}int diagonal2 = row + i;if (diagonals2.contains(diagonal2)) {continue;}queens[row] = i;columns.add(i);diagonals1.add(diagonal1);diagonals2.add(diagonal2);backtrack(solutions, queens, n, row + 1, columns, diagonals1, diagonals2);queens[row] = -1;columns.remove(i);diagonals1.remove(diagonal1);diagonals2.remove(diagonal2);}}}public List<String> generateBoard(int[] queens, int n) {List<String> board = new ArrayList<String>();for (int i = 0; i < n; i++) {char[] row = new char[n];Arrays.fill(row, '.');row[queens[i]] = 'Q';board.add(new String(row));}return board;}
}
1. 问题定义:
• 在一个N×N的棋盘上放置N个皇后,使得它们互不攻击。
2. 关键数据结构:
• int[] queens:存储每一行皇后的列位置。
• Set<Integer> columns, diagonals1, diagonals2:分别记录已经放置皇后的列和两组斜线,以检测冲突。
3. 算法流程:
• 初始化:创建解集solutions,初始化queens数组和三个用于检测冲突的集合。
• 回溯搜索 (backtrack 方法):
• 如果已经处理完所有行,生成棋盘并添加到解集中。
• 在当前行,遍历所有列:
• 检查当前列和两个斜线方向是否已经有皇后(冲突检测)。
• 如果无冲突,放置皇后并更新冲突检测集合。
• 递归处理下一行。
• 回溯:撤销当前行皇后的放置,恢复冲突检测集合,尝试下一个位置。
4. 生成棋盘 (generateBoard 方法):
• 根据每行皇后的位置,生成代表棋盘的字符串列表。
以上代码均为力扣答案~
谁家今夜扁舟子?
何处相思明月楼?
Fighting!