332. 重新安排行程
给你一份航线列表 tickets ,其中 tickets[i] = [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。
所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。如果存在多种有效的行程,请你按字典排序返回最小的行程组合。
- 例如,行程 [“JFK”, “LGA”] 与 [“JFK”, “LGB”] 相比就更小,排序更靠前。


假定所有机票至少存在一种合理的行程。且所有的机票 必须都用一次 且 只能用一次。
示例 1:
输入:tickets = [[“MUC”,”LHR”],[“JFK”,”MUC”],[“SFO”,”SJC”],[“LHR”,”SFO”]] 输出:[“JFK”,”MUC”,”LHR”,”SFO”,”SJC”]
示例 2:
输入:tickets = [[“JFK”,”SFO”],[“JFK”,”ATL”],[“SFO”,”ATL”],[“ATL”,”JFK”],[“ATL”,”SFO”]] 输出:[“JFK”,”ATL”,”JFK”,”SFO”,”ATL”,”SFO”] 解释:另一种有效的行程是 [“JFK”,”SFO”,”ATL”,”JFK”,”ATL”,”SFO”] ,但是它字典排序更大更靠后。
提示:
- 1 <= tickets.length <= 300
- tickets[i].length == 2
- fromi.length == 3
- toi.length == 3
- fromi 和 toi 由大写英文字母组成
- fromi != toi
思路:
经典的欧拉通路问题,只是题目要求输出字典序最小的行程组合。
记录一笔画中的所有点
class Solution {Map<String, Queue<String>> map = new HashMap<>();List<String> res = new ArrayList<>();public List<String> findItinerary(List<List<String>> tickets) {Collections.sort(tickets, (o1, o2) -> {if (o1.get(0).equals(o2.get(0))) {return o1.get(1).compareTo(o2.get(1));} else {return o1.get(0).compareTo(o2.get(0));}});for (List<String> list : tickets) {String S = list.get(0), E = list.get(1);map.computeIfAbsent(S, key -> new LinkedList<>()).offer(E);}dfs("JFK");res.add("JFK");Collections.reverse(res);return res;}void dfs(String u) {Queue<String> q = map.get(u);if (q == null) return;while (!q.isEmpty()) {String ne = q.poll();dfs(ne);res.add(ne);}}}
class Solution {int[] res = new int[310];int cnt = 0;public List<String> findItinerary(List<List<String>> tickets) {TreeSet<String> set = new TreeSet<>();Map<String, Integer> map = new HashMap<>();for (var t : tickets) {for (var s : t)set.add(s);}int n = set.size();String[] unmap = new String[n];int idx = 0;for (String s : set) {unmap[idx] = s;map.put(s, idx++);}int[][] g = new int[n][n];for (var t : tickets) {String a = t.get(0), b = t.get(1);int x = map.get(a), y = map.get(b);g[x][y]++;}dfs(map.get("JFK"), g);List<String> ans = new ArrayList<>();for (int i = cnt; i > 0; i--)ans.add(unmap[res[i]]);return ans;}void dfs(int u, int[][] g) {for (int i = 0; i < g[u].length; i++) {if (g[u][i] > 0) {g[u][i] --;dfs(i, g);}}res[++cnt] = u;}}
5932. 合法重新排列数对
思路:
记录一笔画中的所有边
class Solution {Map<Integer, Queue<Integer>> map = new HashMap<>();List<int[]> res = new ArrayList<>();public int[][] validArrangement(int[][] pairs) {Map<Integer, Integer> din = new HashMap<>();Map<Integer, Integer> dout = new HashMap<>();int min = Integer.MAX_VALUE;for (int[] p : pairs) {int a = p[0], b = p[1];din.merge(b, 1, Integer::sum);dout.merge(a, 1, Integer::sum);min = Math.min(a, min);map.computeIfAbsent(a, key -> new LinkedList<>()).offer(b);}for (int key : map.keySet()) {if (din.getOrDefault(key, 0) + 1 == dout.getOrDefault(key, 0)) {min = key;break;}}dfs(min);Collections.reverse(res);return res.toArray(new int[pairs.length][]);}void dfs(int u) {var q = map.get(u);if (q == null || q.isEmpty()) return;while (!q.isEmpty()) {int v = q.poll();dfs(v);res.add(new int[]{u, v});}}}
753. 破解保险箱
思路:
将每个密码看作一条边,而图中的节点正好是密码的位数减一能组成的字符串
证明:图是连通的,且每个节点的入度等于出度
说明可以构成欧拉回路
class Solution {int k;Set<String> used = new HashSet<>();StringBuilder ans = new StringBuilder();public String crackSafe(int n, int k) {this.k = k;StringBuilder start = new StringBuilder();for (int i = 1; i < n; i++)start.append("0");dfs(start.toString());ans.append(start);return ans.toString();}void dfs(String s) {for (int i = 0; i < k; i++) {String v = s + i;if (!used.contains(v)) {used.add(v);dfs(v.substring(1, v.length()));ans.append(i);}}}}
