由于字符是不同的,并且每个字符在输出中只能出现在其他两个字符旁边,因此每个字符最多只能出现在两个规则中。如果我们将规则视为graph 中的边,那么它们会形成一组不相交的路径。
最早的按字母顺序排列的解决方案可以通过查找所有不在任何路径中或为路径端点的字符,然后按字母顺序将它们插入到输出中来形成。当插入路径端点时,我们必须在继续之前按顺序插入该路径的其余部分。
为了方便地找到具有零条或一条边的节点并沿路径迭代,我们首先将edge list data structure 转换为adjacency list data structure。
import java.util.*;
public class Solution {
public static char[] solve(char[] letters, char[][] rules) {
// convert to adjacency list
Map<Character, List<Character>> neighbours = new HashMap<>();
for(char[] edge : rules) {
char a = edge[0], b = edge[1];
neighbours.computeIfAbsent(a, ArrayList::new).add(b);
neighbours.computeIfAbsent(b, ArrayList::new).add(a);
}
// find nodes with 0 or 1 edges, in order
List<Character> endpoints = new ArrayList<>();
for(char a : letters) {
if(!neighbours.containsKey(a) || neighbours.get(a).size() <= 1) {
endpoints.add(a);
}
}
Collections.sort(endpoints);
// build output
char[] out = new char[letters.length];
Set<Character> used = new HashSet<>();
int i = 0;
for(char a : endpoints) {
if(used.contains(a)) { continue; }
out[i++] = a;
used.add(a);
// if it's a path, iterate along path
while(neighbours.containsKey(a) && !neighbours.get(a).isEmpty()) {
char b = neighbours.get(a).get(0);
out[i++] = b;
used.add(b);
// remove previous neighbour so next one guaranteed at index 0
neighbours.get(b).remove((Character) a); // don't convert to int
a = b;
}
}
return out;
}
}
例子:
>>> Solution.solve(
... new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' },
... new char[][] { {'A', 'B'} , {'D', 'B'}, {'G', 'E'}, {'H', 'A'} }
... )
...
char[8] { 'C', 'D', 'B', 'A', 'H', 'E', 'G', 'F' }