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);
}
}
}
}