LeetCode269 Alien DIctionary

given you a list of instance, find the pattern and return it.
example of input and output:
Input:
[
“wrt”,
“wrf”,
“er”,
“ett”,
“rftt”
]

Output: “wertf”

if I’m not told this problem can be solved in graph idea, i will not know, but now since I’m been told that, so how does this problem have anything to do with graph?
when thinking of “order”, we have natrual orders, like 1 < 2, but we also have our own rules about some things, and that comes to topological order.
and if we want to order sth, it doesn’t matter for the ways we traverse the whole graph, so dfs or bfs are both good.

let’s start with bfs:

class Solution {
    public String alienOrder(String[] words) {
    
    // Step 0: Create data structures and find all unique letters.
    Map<Character, List<Character>> adjList = new HashMap<>(); //use hashmap to represent the adjancent matrix, wen need to fill that and try to do topological sort about it.
    Map<Character, Integer> counts = new HashMap<>();
    for (String word : words) {
        for (char c : word.toCharArray()) {
            counts.put(c, 0);
            adjList.put(c, new ArrayList<>());
        }
    }
    
    // Step 1: Find all edges. due to we can only find edge from two adjancent words, because it is the only way we can sure the order of two chars
    for (int i = 0; i < words.length - 1; i++) {
        String word1 = words[i];
        String word2 = words[i + 1];
        // Check that word2 is not a prefix of word1.
        if (word1.length() > word2.length() && word1.startsWith(word2)) {
            return ""; //why as long as there is one pair of words is relationship of prefix?
        }
        // Find the first non match and insert the corresponding relation.
        for (int j = 0; j < Math.min(word1.length(), word2.length()); j++) {
            if (word1.charAt(j) != word2.charAt(j)) {
                adjList.get(word1.charAt(j)).add(word2.charAt(j)); //this is exactly the topological order, from front one and end with it's point ones.
                counts.put(word2.charAt(j), counts.get(word2.charAt(j)) + 1); //count put the latter words as the key, and count its over all appearance times
                break; //each time we can only find at most one edge
            }
        }
    }
    
    // Step 2: Breadth-first search.
    StringBuilder sb = new StringBuilder();
    Queue<Character> queue = new LinkedList<>(); //use queue in bfs
    for (Character c : counts.keySet()) { //for every key in counts hashmap
        if (counts.get(c).equals(0)) { //if the number of times that current char c uses as the "later chars", which means it is a starting node
            queue.add(c); //then we add into the queue
        }//until we add all the starting node
    }
    while (!queue.isEmpty()) { 
        Character c = queue.remove();
        sb.append(c); //because for unsured order, we just return any way we want, in this case, we returns according to the order of char into queue
        for (Character next : adjList.get(c)) {
            counts.put(next, counts.get(next) - 1); //update counts
            if (counts.get(next).equals(0)) { //if this "next" node in count is 0, then it means every node that point to this "next" node is already renaversed
                queue.add(next); //and in this case will we add this node to queue
            } //and when there is still other node point to this neighbor that's not been tranversed, we juat update count, but didn;t put it in the queue as the next level
        }
    }
    
    if (sb.length() < counts.size()) { // if we are not tranvesed every node, then their must be。。?but how can sb not append everything? is it because their exist sth that is not logical? may be circle appears? if so, then this defintely not legal?
        return "";
    }
    return sb.toString();
}
}

so the general idea of this problem is: construct two hashmaps, one uses for adjacent matrix for storing the graph. the other uses for calcualte the number of times current node be pointed.(which means it is like that count hashmap uses for calculate the in degree of everynode)
and next, uses a queue and a stringbuilder, queue is for bfs and the stringBuilder is the final results. pay attention, when we do bfs, only if the neigbor has in degree is 0 will we put it in the queue as the next level.

the next solution is dfs:

//the dfs solution:
class Solution {
    
    private Map<Character, List<Character>> reverseAdjList = new HashMap<>();//the key is some fixed node, and the value is its prequsite nodes
    private Map<Character, Boolean> seen = new HashMap<>();
    private StringBuilder output = new StringBuilder();
    
    public String alienOrder(String[] words) {
        
        // Step 0: Put all unique letters into reverseAdjList as keys. which means we put all the keys in reverseAdjList first
        for (String word : words) {
            for (char c : word.toCharArray()) {
                reverseAdjList.putIfAbsent(c, new ArrayList<>());
            }
        }
        
        // Step 1: Find all edges and add reverse edges to reverseAdjList.
        for (int i = 0; i < words.length - 1; i++) {
            String word1 = words[i];
            String word2 = words[i + 1];
            // Check that word2 is not a prefix of word1.
            if (word1.length() > word2.length() && word1.startsWith(word2)) {
                return "";
            }
            // Find the first non match and insert the corresponding relation.
            for (int j = 0; j < Math.min(word1.length(), word2.length()); j++) {
                if (word1.charAt(j) != word2.charAt(j)) {
                    reverseAdjList.get(word2.charAt(j)).add(word1.charAt(j));
                    break; //a pair of wors[i] and words[i+1] only conrains one edge at most, so after you find it, just break
                }
            }
        }
        
        // Step 2: DFS to build up the output list.
        for (Character c : reverseAdjList.keySet()) {
            boolean result = dfs(c); //for every node in graph, dfs starting from that node
            if (!result) return "";
        }
        
        
        if (output.length() < reverseAdjList.size()) {
            return "";
        }
        return output.toString();
    }
    
    // Return true iff no cycles detected.
    //use dfs to detect cycle is very easy, use hashmap seen to keep track of the node. if we meet a ndoe, we mark it as false, and if on the same path, we meet this node again, then there must be a circle. however, dfs needs to backtrackig when their is not way ahead, so we have to backtrack node to mark it as true
    private boolean dfs(Character c) {
        if (seen.containsKey(c)) {
            return seen.get(c); // If this node was grey (false), a cycle was detected.
        }
        seen.put(c, false);
        for (Character next : reverseAdjList.get(c)) {
            boolean result = dfs(next);
            if (!result) return false;
        }
        seen.put(c, true); //dfs backtracking, this is the reason why we use a hashmap as seen, however, we can use 0-1-2 in array to represent the status of curent node
        output.append(c); //this is the post order tranverse, and that's why we have reversedAdjancentList
        return true;
    }    
}

so the gernal idea of this dfs is: two hashmaps, the first one is called reverseAdjancentList, because unlike bfs, it stores the node and its corresponding prerequsitre nodes. and the second one is called seen, which uses as the check for cycle(means if we meet node we have encountered before in a single dfs path, then it defintely have cycle).

the first step is to get all the character as keys in reverseadjacentList
and next step is to find all the edges and add them to reverseAdjacentedList, and we only need to compare the pair between words[i] and words[i+1]
and then, for every key in reverseAdjList, we dfs it(remeber that keys in reverseadjcentList is all not starting nodes), and we dfs, we use seen this hashmap as the cycle detector. if anyrhing is wrong, return false. and we append every c in dfs path usring the dfs part as the part of final results, so final results should be a global variable

posted @ 2020-05-08 04:10  EvanMeetTheWorld  阅读(19)  评论(0)    收藏  举报