算法训练营Day27

#Java #回溯 #Hard

开源学习资料

Feeling and experiences:

以下题目都比较困难,第一遍刷的时候,先看懂即可。

重新安排行程:力扣题目链接

给你一份航线列表 tickets ,其中 tickets[i] = [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。

所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。如果存在多种有效的行程,请你按字典排序返回最小的行程组合。

  • 例如,行程 ["JFK", "LGA"]["JFK", "LGB"] 相比就更小,排序更靠前。

假定所有机票至少存在一种合理的行程。且所有的机票 必须都用一次 且 只能用一次。

class Solution {
    Map> map = new HashMap>();
    List itinerary = new LinkedList();

    public List findItinerary(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);
    }
}

1. 问题理解:
• 目标是根据给定的航班列表,找到一条从”JFK”出发,覆盖所有航班至少一次的行程。
• 如果存在多种可能的行程,选择字典序最小的那一条。


2. 关键概念:
• 涉及到类似于欧拉路径的概念,即通过图中的所有边恰好一次的路径。


3. 数据结构选择:
• 使用哈希表(Map> map)来存储从每个机场出发的航班。键是出发机场,值是一个优先级队列,包含按字典序排列的目的地机场。
• 使用链表(List itinerary)来存储最终的行程。


4. 算法流程:
• 构建图:遍历每张机票,构建出发地到目的地的映射,存入哈希表。
• 深度优先搜索(DFS):从”JFK”开始进行DFS,按字典序探索每个机场的所有目的地。
• 递归终止条件:当一个机场没有更多可飞往的目的地时,将其加入行程。
• 行程构建:由于DFS是先达到最深层再回溯,所以最终的行程是逆序的,需要反转链表得到正确顺序。


5. 特殊情况处理:
• 当存在多条路径时,优先级队列确保按字典序选择路径。
• 由于题目中的图可能不是传统意义上的欧拉图(可能不包含所有边),算法的目标是覆盖所有给定的边。

N皇后:力扣题目链接

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q''.' 分别代表了皇后和空位。

class Solution {
    public List> solveNQueens(int n) {
        List> solutions = new ArrayList>();
        int[] queens = new int[n];
        Arrays.fill(queens, -1);
        Set columns = new HashSet();
        Set diagonals1 = new HashSet();
        Set diagonals2 = new HashSet();
        backtrack(solutions, queens, n, 0, columns, diagonals1, diagonals2);
        return solutions;
    }

    public void backtrack(List> solutions, int[] queens, int n, int row, Set columns, Set diagonals1, Set diagonals2) {
        if (row == n) {
            List 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 generateBoard(int[] queens, int n) {
        List board = new ArrayList();
        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 columns, diagonals1, diagonals2:分别记录已经放置皇后的列和两组斜线,以检测冲突。


3. 算法流程:
• 初始化:创建解集solutions,初始化queens数组和三个用于检测冲突的集合。
• 回溯搜索 (backtrack 方法):
• 如果已经处理完所有行,生成棋盘并添加到解集中。
• 在当前行,遍历所有列:
• 检查当前列和两个斜线方向是否已经有皇后(冲突检测)。
• 如果无冲突,放置皇后并更新冲突检测集合。
• 递归处理下一行。
• 回溯:撤销当前行皇后的放置,恢复冲突检测集合,尝试下一个位置。


4. 生成棋盘 (generateBoard 方法):
• 根据每行皇后的位置,生成代表棋盘的字符串列表。

以上代码均为力扣答案~

谁家今夜扁舟子?

何处相思明月楼?

Fighting!
 

你可能感兴趣的:(java)