leetcode
leetcode 2951 ~ 3000
火星词典

火星词典

难度:

标签:

题目描述

English description is not available for the problem. Please switch to Chinese.

代码结果

运行时间: 28 ms, 内存: 16.0 MB


/*
思路:
1. 使用流处理构建图和入度数组。
2. 使用流进行拓扑排序。
*/
import java.util.*;
import java.util.stream.*;

public class AlienDictionaryStream {
    public String alienOrder(String[] words) {
        Map<Character, Set<Character>> graph = new HashMap<>();
        Map<Character, Integer> inDegree = new HashMap<>();
        Arrays.stream(words).forEach(word -> {
            word.chars().forEach(c -> {
                graph.putIfAbsent((char) c, new HashSet<>());
                inDegree.putIfAbsent((char) c, 0);
            });
        });
        IntStream.range(0, words.length - 1).forEach(i -> {
            String word1 = words[i];
            String word2 = words[i + 1];
            if (word1.length() > word2.length() && word1.startsWith(word2)) {
                return;
            }
            IntStream.range(0, Math.min(word1.length(), word2.length())).filter(j -> word1.charAt(j) != word2.charAt(j)).findFirst().ifPresent(j -> {
                char parent = word1.charAt(j);
                char child = word2.charAt(j);
                if (graph.get(parent).add(child)) {
                    inDegree.put(child, inDegree.get(child) + 1);
                }
            });
        });
        Queue<Character> queue = inDegree.keySet().stream().filter(c -> inDegree.get(c) == 0).collect(Collectors.toCollection(LinkedList::new));
        StringBuilder order = new StringBuilder();
        while (!queue.isEmpty()) {
            char current = queue.poll();
            order.append(current);
            graph.get(current).forEach(neighbor -> {
                inDegree.put(neighbor, inDegree.get(neighbor) - 1);
                if (inDegree.get(neighbor) == 0) {
                    queue.offer(neighbor);
                }
            });
        }
        return order.length() == inDegree.size() ? order.toString() : "";
    }
}

解释

方法:

这道题解的思路基于图论中的拓扑排序。首先,题解尝试通过给定的单词列表构建一个有向图,其中节点代表字母,有向边代表字母间的顺序关系。使用边和入度数组来建立这个图的结构。接下来,题解遍历单词列表,比较相邻的单词,找到第一个不同的字母对,以此来确定字母间的顺序关系,即在图中添加边。特别的,如果发现短单词排在长单词后面,那么立即返回空字符串,因为这是不合法的。最后,题解使用拓扑排序算法来得到一个有效的字母顺序。如果排序过程中某个时刻没有入度为0的字母而图中仍有字母未处理,则说明存在环,也返回空字符串。

时间复杂度:

O(n + V + E)

空间复杂度:

O(26) 或 O(1)

代码细节讲解

🦆
在构建有向图时,如果两个单词相同,比如`aa`和`aa`,这种情况在代码中是如何处理的?是否会影响图的构建或是最终的结果?
在构建有向图时,如果遇到两个单词完全相同,例如`aa`和`aa`,代码中通过`zip(pre, now)`比较相邻单词中的每个字母。由于两个单词相同,`zip`函数会比较每个位置的字母,但都是相同的,因此不会进入`if sa != sb`的逻辑块内,也就不会在图中添加任何新的边。这种情况不会影响图的构建,因为没有新的顺序关系被发现,也不会影响最终的结果,因为字母的相对顺序并没有改变。
🦆
题解中提到,如果较短的单词排在较长的单词之后,则返回空字符串。能否详细解释为什么这种情况下必然不存在合法的字母顺序?
如果较短的单词排在较长的单词之后,例如`abc`之后是`ab`,这在字母顺序的逻辑上是不合法的。因为按照字典顺序的规则,如果一个单词的前缀与另一个单词相同,那么较短的单词应该排在前面。在这种情况下,`ab`不能排在`abc`之后,因为这意味着`c`需要在`b`之前出现,同时又在`b`之后出现,这自然形成了一个逻辑上的矛盾。因此,这种情况下一定不存在合法的字母顺序,所以返回空字符串。
🦆
在处理字母入度数组时,为什么只增加不同字母对中后一个字母的入度而不是两个字母都增加?这样的处理对拓扑排序的结果有何影响?
在构建图和处理入度数组时,我们只增加不同字母对中后一个字母的入度,因为这反映了前一个字母应该在后一个字母之前出现的顺序关系。如果我们增加两个字母的入度,这将意味着两个字母都依赖于对方,形成了一个循环依赖,这在拓扑排序中是没有意义的。正确的做法是只表达一种单向依赖关系。这样的处理确保了拓扑排序能够正确地表示字母之间的顺序,如果存在一种合法的顺序。如果处理错误,可能会导致无法解析出一个有效的顺序,或者错误地认为存在依赖环。

相关问题