leetcode269 - Alien Dictionary - hard

Posted jasminemzy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode269 - Alien Dictionary - hard相关的知识,希望对你有一定的参考价值。

There is a new alien language which uses the latin alphabet. However, the order among letters are unknown to you. You receive a list of non-empty words from the dictionary, where words are sorted lexicographically by the rules of this new language. Derive the order of letters in this language.
Example 1:
Input:[ "wrt","wrf","er","ett","rftt"]
Output: “wertf"
Example 2:
Input:["z","x"]
Output: "zx"
Example 3:
Input:["z", "x", "z"]
Output: ""
Explanation: The order is invalid, so return "".

 

拓扑排序。
general流程:
1. 统计所有点的入度,并初始化拓扑序列为空。
2. 将所有入度为 0 的点,也就是那些没有任何依赖的点,放到宽度优先搜索的队列中
3. 将队列中的点一个一个的释放出来,放到拓扑序列中,每次释放出某个点 A 的时候,就访问 A 的相邻点(所有A指向的点),并把这些点的入度减去 1。
4. 如果发现某个点的入度被减去 1 之后变成了 0,则放入队列中。
5. 直到队列为空时,算法结束,
(如果关系是先a后b,那图也就是map里要让a指向b,入度里要让b的增加)

 

本题流程:
1.初始化图和入度,让所有字符都有所记录,哪怕空荡荡的默认值。
2.详细计算图和入度。注意最多只对比前后字符串共享部分。
3.开始BFS,每从q里拿到一个字符就推入sb。
4.检查sb的length()是不是和所有出现过的字符数一样,从而知道有没有出现循环依赖导致拓扑排序不合格。
5.返回sb.toString()。

 

细节:
1.把对图的初始化和详细计算分开。因为详细计算内部只遍历前后两个字符串里短的那部分,实际上两个字符串后面长出来没有对比到的那部分,里面可能还有部分indegree == 0的字符。这些字符也要让他们的入度为0从而排到最前面,而且也要让他们的map取得到而不是null,从而避免之后BFS时for(char next : map.get(这些c))时,导致iterator 一个null对象了编译报错。
2.小心重复,也就是a->b出现多次的情况。如果map里用的是set数据结构来记录依赖关系,那a->b在这里只会记录一次的,如果你入度是每次看到a->b都加一次,那肯定就不一致出错了,之后BFS时无法正确地让indegree降到0,解决方法是只在第一次看到a->b的时候加入度。另外一个解决方案是map里用list数据结构记录依赖关系,看到所有a->b都增加list也增加indegree,缺陷是这种方式有冗余,时间复杂度会稍高。
3.注意本题每相邻两行只可以得到一个信息:第一个不相等的char之间的依赖关系。那么你在看到两个字符不等,处理好map和indegree的更新后,应该立刻break,不能继续比后面的字符,因为那些是无效信息了。
4.最后记得确认有没有循环依赖。

 

我的实现:

class Solution {
    public String alienOrder(String[] words) {
        
        Map<Character, List<Character>> map = new HashMap<>();
        Map<Character, Integer> indegree = new HashMap<>();
        
        for (String word : words) {
            for (char c : word.toCharArray()) {
                // P1: 解决下面循环里,两个字符串后面长出来没有对比到的那部分,这里面可能还有部分indegree == 0的字符没加进indegree。而且map不给的话,之后BFS拿这些入度为0而且从来没给依赖关系的点,会出现空指针情况。
                indegree.putIfAbsent(c, 0);
                map.putIfAbsent(c, new ArrayList<Character>());
            }
        }
        
        for (int i = 0; i < words.length - 1; i++) {
            for (int j = 0; j < Math.min(words[i].length(), words[i + 1].length()); j++) {
                if (words[i].charAt(j) != words[i + 1].charAt(j)) {
                    // P1: indegree不能和graph一起做,因为graph只遍历两个字符串里短的那部分,
                    char c1 = words[i].charAt(j);
                    char c2 = words[i + 1].charAt(j);
                    // P2: 小心a->b出现多次的情况,如果map内部嵌套的是set,不能出现第二次a->b的时候也无脑加入度,一定要让只有第一次出现a->b的时候加一次有效入度。因为你set没能再加一次,入度多加了的话,之后BFS的时候你的入度就减不到0了。另一个解决方案是map内部嵌套list,不过有冗余,时间复杂度高一点。
                    if (map.get(c1).add(c2)) {
                        indegree.put(c2, indegree.get(c2) + 1);
                    }
                    // map.get(c1).add(c2);
                    // indegree.put(c2, indegree.get(c2) + 1);
                    
                    //P3: 注意对比完一个就结束了!后续不能接着比的!两个字符串只得一个有效信息。
                    break;
                }
            }
            
        }
        
        Queue<Character> q = new LinkedList<>();
        for (char c : indegree.keySet()) {
            if (indegree.get(c) == 0) {
                q.offer(c);
            }
        }
        
        StringBuilder sb = new StringBuilder();
        while (!q.isEmpty()) {
            char crt = q.poll();
            sb.append(crt);
            for (char next : map.get(crt)) {
                indegree.put(next, indegree.get(next) - 1);
                if (indegree.get(next) == 0) {
                    q.offer(next);
                }
            }
        }
        
        // P4:要确认一下有没有出现循环依赖。
        if (sb.length() != indegree.size()) {
            return "";
        } else {
            return sb.toString();
        }
        
    }
}

 

九章实现:

/**
* 本参考程序来自九章算法,由 @令狐冲 提供。版权所有,转发请注明出处。
* - 九章算法致力于帮助更多中国人找到好的工作,教师团队均来自硅谷和国内的一线大公司在职工程师。
* - 现有的面试培训课程包括:九章算法班,系统设计班,算法强化班,Java入门与基础算法班,android 项目实战班,
* - Big Data 项目实战班,算法面试高频题班, 动态规划专题班
* - 更多详情请见官方网站:http://www.jiuzhang.com/?source=code
*/ 

class Solution {
    public String alienOrder(String[] words) {
        Map<Character, Set<Character>> graph = constructGraph(words);
        return topologicalSorting(graph);
    }
    
        
    private Map<Character, Set<Character>> constructGraph(String[] words) {
        Map<Character, Set<Character>> graph = new HashMap<>();
        
        // create nodes
        for (int i = 0; i < words.length; i++) {
            for (int j = 0; j < words[i].length(); j++) {
                char c = words[i].charAt(j);
                if (!graph.containsKey(c)) {
                    graph.put(c, new HashSet<Character>());
                }
            }
        }
        
        // create edges
        for (int i = 0; i <  words.length - 1; i++) {
            int index = 0;
            while (index < words[i].length() && index < words[i + 1].length()) {
                if (words[i].charAt(index) != words[i + 1].charAt(index)) {
                    graph.get(words[i].charAt(index)).add(words[i + 1].charAt(index));
                    break;
                }
                index++;
            }
        }

        return graph;
    }
    
    private Map<Character, Integer> getIndegree(Map<Character, Set<Character>> graph) {
        Map<Character, Integer> indegree = new HashMap<>();
        for (Character u : graph.keySet()) {
            indegree.put(u, 0);
        }
        
        for (Character u : graph.keySet()) {
            for (Character v : graph.get(u)) {
                indegree.put(v, indegree.get(v) + 1);
            }
        }     
        
        return indegree;
    }

    private String topologicalSorting(Map<Character, Set<Character>> graph) {
        Map<Character, Integer> indegree = getIndegree(graph);
        // as we should return the topo order with lexicographical order
        // we should use PriorityQueue instead of a FIFO Queue
        Queue<Character> queue = new PriorityQueue<>();
        
        for (Character u : indegree.keySet()) {
            if (indegree.get(u) == 0) {
                queue.offer(u);
            }
        }
        
        StringBuilder sb = new StringBuilder();
        while (!queue.isEmpty()) {
            Character head = queue.poll();
            sb.append(head);
            for (Character neighbor : graph.get(head)) {
                indegree.put(neighbor, indegree.get(neighbor) - 1);
                if (indegree.get(neighbor) == 0) {
                    queue.offer(neighbor);
                }
            }
        }
        
        if (sb.length() != indegree.size()) {
            return "";
        }
        return sb.toString();
    }
}

 




以上是关于leetcode269 - Alien Dictionary - hard的主要内容,如果未能解决你的问题,请参考以下文章

[LeetCode] 269. Alien Dictionary 外文字典

leetcode269 - Alien Dictionary - hard

[LeetCode] 269. Alien Dictionary

269. Alien Dictionary

269. Alien Dictionary

269. Alien Dictionary