【Leetcode】126. Word Ladder II(代码)

195 阅读2分钟

题目地址:

leetcode.com/problems/wo…

给定两个单词作为起点和终点,每一步允许从单词中改变某个位置的字母,变为另一个单词。再给定一个单词的列表,问从起点到终点的所有最短路径,要求路径中所有单词都要在列表里(起点不必在,终点必须在)。题目保证所有单词都由小写字母组成。

法1:BFS记录距离 + DFS(这是BFS找路径的标准做法)。如果列表里没有终点那直接返回空列表。接着先从起点单词开始BFS,搜的过程中用哈希表记录路径上每个单词与起点的距离。一旦搜到终点就停止搜索。设从起点到终点的最少步数是k,那么此时哈希表里已经存了所有距离起点k−1步以内的单词,以及一部分距离k步的单词(距离k步的单词我们只关心终点,虽然别的单词也可能会加进去)。接着我们从终点向起点DFS,一路永远找步数比当前位置少1的单词,直到搜到起点(之所以从终点搜,是因为如果从起点搜的话,即使每次搜的都是比自己步数多1的单词,但是有可能搜到第k步的时候搜到了别的单词。从终点开始搜的话能保证搜到第k步一定是起点)。代码如下:

import java.util.*;

public class Solution {
    public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
        List<List<String>> res = new ArrayList<>();
        Set<String> set = new HashSet<>(wordList);
        
        // 特判一下,如果终点不在列表里则直接返回空
        if (!set.contains(endWord)) {
            return res;
        }
        
        // dist记录每个单词与起点的距离
        Map<String, Integer> dist = new HashMap<>();
        dist.put(beginWord, 0);
        Queue<String> queue = new LinkedList<>();
        queue.offer(beginWord);
        
        // 为了搜到终点就退出整个while循环,我们在while循环上加个label
        OUT:
        while (!queue.isEmpty()) {
            String cur = queue.poll();
            char[] chs = cur.toCharArray();
            for (int i = 0; i < chs.length; i++) {
                char c = chs[i];
                for (char ch = 'a'; ch <= 'z'; ch++) {
                    if (ch != c) {
                        chs[i] = ch;
                        String next = new String(chs);
                        // 注意,这里每个单词只允许加入dist一次,第一次加入的时候才是最小步数
                        if (set.contains(next) && !dist.containsKey(next)) {
                            dist.put(next, dist.get(cur) + 1);
                            queue.offer(next);
                        }
                        
                        // 搜到终点了就退出整个while循环
                        if (next.equals(endWord)) {
                            break OUT;
                        }
                        
                    }
                }
                chs[i] = c;
            }
        }
        
        dfs(endWord, beginWord, dist, new ArrayList<>(), res);
        return res;
    }
    
    private void dfs(String cur, String begin, Map<String, Integer> dist, List<String> list, List<List<String>> res) {
        list.add(cur);
        // 如果搜到起点了,就记录答案
        if (cur.equals(begin)) {
        	// 由于是从终点开始搜的,加入答案的时候要反个序
            Collections.reverse(list);
            res.add(new ArrayList<>(list));
            // 加入答案之后再翻回来
            Collections.reverse(list);
            // 回溯之前恢复现场
            list.remove(list.size() - 1);
            return;
        }
        
        char[] chs = cur.toCharArray();
        for (int i = 0; i < chs.length; i++) {
            char c = chs[i];
            for (char ch = 'a'; ch <= 'z'; ch++) {
                if (ch != c) {
                    chs[i] = ch;
                    String next = new String(chs);
                    // 这里要写dist.containsKey(next),因为起点不一定在set里
                    if (dist.containsKey(next) && dist.get(next).equals(dist.get(cur) - 1)) {
                        dfs(next, begin, set, dist, list, res);
                    }
                }
            }
            chs[i] = c;
        }
        
        list.remove(list.size() - 1);
    }
}

有关链接:

  1. (7条消息) LC DFS、BFS与图论_edWard的博客-CSDN博客

  2. happygirlzt