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