[LeetCode-JAVA] Word Ladder II

题目:

Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, such that:

  1. Only one letter can be changed at a time
  2. Each intermediate word must exist in the dictionary

For example,

Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]

Return

  [
    ["hit","hot","dot","dog","cog"],
    ["hit","hot","lot","log","cog"]
  ]

思路:先建立dict的邻接链表,例子中的结果如下:

hit : [hot]
cog : [dog, log]
hot : [hit, lot, dot]
lot : [hot, log, dot]
dog : [cog, log, dot]
log : [cog, lot, dog]
dot : [lot, hot, dog]

在Word LadderI的基础上,由于要重新构建结果集,因此要自己创建一个Node类,保存每一个节点的上一个节点,这样在找到最短结果的时候,以此找到该节点的上一个节点,加入链表即可。

需要注意的几点:

(1)构建邻接链表前,需要把start和end加入,这样才能找到结果。

(2)构建邻接链表的时候,对于每一个单词,一定要在每一次替换char后,再将其恢复,以便进行下一次判断。

(3)每一层的queue用一个num来记录此层的Node个数,只要是找到一个,就证明这一层即为最短路径,设标志位为true,将该层循环之后,结束循环。

(4)循环的过程中,需要判断节点是否已经被判断过,创建一个Set用于记录所有判断过的节点。

代码:

public class Solution {
    private class Node{
        public Node pre;
        public String val;
        public Node(Node p, String s){
            pre = p;
            val = s;
        }
    }
    public List<List<String>> findLadders(String start, String end, Set<String> dict) {
        dict.add(start);
        dict.add(end); //注意(1)
        Map<String, Set<String>> neighbours = calNeighbours(dict);
        
        List<List<String>> req = new ArrayList<List<String>>();
        
        LinkedList<Node> queue = new LinkedList<Node>();
        queue.offer(new Node(null, start)); //开始节点的前一个为null
        
        Set<String> visited = new HashSet<String>(); //注意(4)
        boolean flag = false; //注意(3)
        while(!queue.isEmpty() || visited.size() == dict.size()){
            int num = queue.size();
            for(int i = 0 ; i < num ; i++){
                Node n = queue.poll();
                if(end.equals(n.val)){
                    findPath(n, req);
                    flag = true; //注意(3)
                }else{
                    Set<String> temp = neighbours.get(n.val);
                    if(temp == null || temp.size() == 0)
                        continue;
                    else{
                        for(String s : temp){
                            if(!visited.contains(s)){ //注意(4)
                                queue.offer(new Node(n, s));
                            }
                        }
                    }
                }
                visited.add(n.val);//注意(4)
            }
            if(flag)
                break;
        }
        if(!flag) // 如果flag为false,证明无解
            return new ArrayList<List<String>>();
        return req;
    }
    
    private void findPath(Node n, List<List<String>> req) {
        // TODO Auto-generated method stub
        List<String> temp = new ArrayList<String>();
        while(n != null){
            temp.add(0, n.val);
            n = n.pre;
        }
        req.add(temp);
    }
    public Map<String, Set<String>> calNeighbours(Set<String> dict) {
        Map<String, Set<String>> neighbours = new HashMap<String, Set<String>>();
        
        for(String str : dict){
            int len = str.length();
            char[] chars = str.toCharArray();
            
            for(int i = 0 ; i < len ; i++){
                char old = chars[i]; //注意(2)
                for(char c = 'a' ; c <= 'z' ; c++){
                    if(c == chars[i])
                        continue;
                    chars[i] = c;
                    String newstr = new String(chars);
                    
                    if(dict.contains(newstr)) {
                        Set<String> set = neighbours.get(str);
                        if(set != null)
                            set.add(newstr);
                        else{
                            Set<String> newset = new HashSet<String>();
                            newset.add(newstr);
                            neighbours.put(str, newset);
                        }
                    }
                    chars[i] = old ; //注意(2)引用型变量 每一次用完要还原 
                } // end fot c
            } // end for len
        } // end for str

        return neighbours;
    }
}

AC时间为 1120ms左右,因此还有很大的改进空间,继续新的思路。

改进1:

在判断去重的时候,不仅要去掉已经走过的String,还要去掉同层的其他String,同层的路径一定回比下一层的要短,因此要在上一次循环的时候去掉所有新添加的,因此,每一层新建立一个remove的集合,存储该层需要去掉的节点。

修改后代码(只添加了三行 24, 39, 45):

public class Solution {
    private class Node{
        public Node pre;
        public String val;
        public Node(Node p, String s){
            pre = p;
            val = s;
        }
    }
    public List<List<String>> findLadders(String start, String end, Set<String> dict) {
        dict.add(start);
        dict.add(end); //注意(1)
        Map<String, Set<String>> neighbours = calNeighbours(dict);
        
        List<List<String>> req = new ArrayList<List<String>>();
        
        LinkedList<Node> queue = new LinkedList<Node>();
        queue.offer(new Node(null, start)); //开始节点的前一个为null
        
        Set<String> visited = new HashSet<String>(); //注意(4)
        boolean flag = false; //注意(3)
        while(!queue.isEmpty() || visited.size() == dict.size()){
            int num = queue.size();
            Set<String> remove = new HashSet<String>(); // 新加判断 每一层需要删除的集合
            for(int i = 0 ; i < num ; i++){
                Node n = queue.poll();
                if(end.equals(n.val)){
                    findPath(n, req);
                    flag = true; //注意(3)
                }else{
                    Set<String> temp = neighbours.get(n.val);
                    if(temp == null || temp.size() == 0)
                        continue;
                    else{
                        for(String s : temp){
                            if(!visited.contains(s)){ //注意(4)
                                queue.offer(new Node(n, s));
                            }
                            remove.add(s);
                        }
                    }
                }
                //visited.add(n.val);  // 可以去掉此处
            }
            visited.addAll(remove);
            if(flag)
                break;
        }
        if(!flag) // 如果flag为false,证明无解
            return new ArrayList<List<String>>();
        return req;
    }
    
    private void findPath(Node n, List<List<String>> req) {
        // TODO Auto-generated method stub
        List<String> temp = new ArrayList<String>();
        while(n != null){
            temp.add(0, n.val); 
            n = n.pre;
        }
        req.add(temp);
    }
    public Map<String, Set<String>> calNeighbours(Set<String> dict) {
        Map<String, Set<String>> neighbours = new HashMap<String, Set<String>>();
        
        for(String str : dict){
            int len = str.length();
            char[] chars = str.toCharArray();
            
            for(int i = 0 ; i < len ; i++){
                char old = chars[i]; //注意(2)
                for(char c = 'a' ; c <= 'z' ; c++){
                    if(c == chars[i])
                        continue;
                    chars[i] = c;
                    String newstr = new String(chars);
                    
                    if(dict.contains(newstr)) {
                        Set<String> set = neighbours.get(str);
                        if(set != null)
                            set.add(newstr);
                        else{
                            Set<String> newset = new HashSet<String>();
                            newset.add(newstr);
                            neighbours.put(str, newset);
                        }
                    }
                    chars[i] = old ; //注意(2)引用型变量 每一次用完要还原 
                } // end fot c
            } // end for len
        } // end for str

        return neighbours;
    }
}

 改完之后 AC的时间大概是 820ms 应该还有继续修改的空间,持续更新.....

参考链接:

http://www.cnblogs.com/springfor/p/3893529.html

http://www.cnblogs.com/shawnhue/archive/2013/06/05/leetcode_126.html

原文地址:https://www.cnblogs.com/TinyBobo/p/4575009.html