日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

算法细节系列(20):Word Ladder系列

發(fā)布時間:2023/12/8 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 算法细节系列(20):Word Ladder系列 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

算法細節(jié)系列(20):Word Ladder系列

詳細代碼可以fork下Github上leetcode項目,不定期更新。

題目摘自leetcode:
1. Leetcode 127: Word Ladder
2. Leetcode 126: Word Ladder II

Leetcode 127: Word Ladder

Problem:

Given two words (beginWord and endWord), and a dictionary’s word list, find the length of shortest transformation sequence from beginWord to endWord, such that:

  • Only one letter can be changed at a time.
  • Each transformed word must exist in the word list. Note that beginWord is not a transformed word.

Example:

Given:
beginWord = “hit”
endWord = “cog”
wordList = [“hot”,”dot”,”dog”,”lot”,”log”,”cog”]
As one shortest transformation is “hit” -> “hot” -> “dot” -> “dog” -> “cog”,
return its length 5.

Note:

  • Return 0 if there is no such transformation sequence.
  • All words have the same length.
  • All words contain only lowercase alphabetic characters.
  • You may assume no duplicates in the word list.
  • You may assume beginWord and endWord are non-empty and are not the same.

這道題其實不難,但要想到這種解法卻要費一番周折,如果對最短路徑搜索熟悉的話,相信你一眼就能看出答案了,并且我們要論證一點,為什么最短路徑算法對這道題來說是正確解法。

我的思路:
DFS,把所有編輯距離為1的單詞連接在一塊,構建一個MAP(鄰接矩陣)。這樣之后,我們就可以從beginWord開始DFS搜索了,中間需要狀態(tài)記錄。代碼如下:

public int ladderLength(String beginWord, String endWord, List<String> wordList) {Map<String, List<String>> map = new HashMap<>();map.put(beginWord, new ArrayList<>());for (String word : wordList){map.put(word, new ArrayList<>());}for (String key : map.keySet()){List<String> container = map.get(key);for (String word : wordList){if (oneDiff(key, word)){container.add(word);}}map.put(key, container);}int ans = helper(map, beginWord, endWord, new HashSet<>());return ans >= 1 << 30 ? 0 : ans;}private int helper(Map<String, List<String>> map,String beginWord, String endWord, Set<String> visited){if (visited.contains(beginWord)) return 1<<30;visited.add(beginWord);for (String find : map.get(beginWord)){if (find.equals(endWord)){visited.remove(beginWord);return 2;}}int min = Integer.MAX_VALUE;for (String find : map.get(beginWord)){int x = 1 + helper(map, find, endWord, visited);min = Math.min(min, x);}visited.remove(beginWord);return min;}private boolean oneDiff(String a, String b){if (a.equals(b)) return false;char[] aa = a.toCharArray();char[] bb = b.toCharArray();int oneDiff = 0;for (int i = 0; i < aa.length; i++){if (aa[i] != bb[i]){oneDiff ++;if (oneDiff >= 2) return false;}}return true;}

代碼沒有多大問題,典型的DFS+狀態(tài)回溯,遍歷搜索每一條到達endWord的路徑,找尋最短路徑。但很可惜TLE了,直觀上來看是因為為了拿到到endWord的最短路徑,我們需要遍歷每一條到endWord的路徑,這是遞歸求解的一個特點。但實際情況,我們可以省去某些點的遍歷。

就那這個問題來說,如從beginWord開始搜索,如

beginWord = "hit" endWord = "cog" wordList = ["hot","dot","dog","lot","log","cog"] wordList中編輯距離為1的單詞有: a. hot 此時BFS搜索與"hot"最近距離的單詞,有: a. dot b. lot 再BFS搜索"dot"時,有: a. cog 所以我們只需要BFS三次就能得到正確答案,而DFS中,需要DFS至少三次。

上述過程就是經典的Dijkstra算法,代碼如下:

public int ladderLength(String beginWord, String endWord, List<String> wordList) {List<String> reached = new ArrayList<>();reached.add(beginWord);Set<String> wordSet = new HashSet<>(wordList);if(!wordSet.contains(endWord)) return 0;wordSet.add(endWord);int distance = 1;while (!reached.contains(endWord)){ //到達該目的地List<String> toAdd = new ArrayList<>();for (String each : reached){for (int i = 0; i < each.length(); i++){char[] chars = each.toCharArray();for (char c = 'a'; c <= 'z'; c++){chars[i] = c;String wd = new String(chars);if (wordSet.contains(wd)){toAdd.add(wd);wordSet.remove(wd); //記錄訪問狀態(tài)}}}}distance ++;if (toAdd.size() == 0) return 0; //沒有編輯距離為1的單詞reached = toAdd;}return distance;}

Leetcode 126: Word Ladder II

Problem:

Given two words (beginWord and endWord), and a dictionary’s word list, find all shortest transformation sequence(s) from beginWord to endWord, such that:

  • Only one letter can be changed at a time
  • Each transformed word must exist in the word list. Note that beginWord is not a transformed word.

Example:

Given:
beginWord = “hit”
endWord = “cog”
wordList = [“hot”,”dot”,”dog”,”lot”,”log”,”cog”]
Return
[
[“hit”,”hot”,”dot”,”dog”,”cog”],
[“hit”,”hot”,”lot”,”log”,”cog”]
]

Note:

  • Return an empty list if there is no such transformation sequence.
  • All words have the same length.
  • All words contain only lowercase alphabetic characters.
  • You may assume no duplicates in the word list.
  • You may assume beginWord and endWord are non-empty and are not the same.

這道題的思路讓我對DFS和BFS有了一些基本理解,但還不夠深刻,咋說呢,我沒想到BFS和DFS還可以分工合作,BFS用來快速求出最小distance,而DFS則用來遍歷所有路徑,兩種遍歷方法各有長處,綜合起來就能解決該問題了,所以我寫了一個版本,代碼如下:

public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {Map<String, List<String>> map = new HashMap<>();map.put(beginWord, new ArrayList<>());for (String word : wordList){map.put(word, new ArrayList<>());}for (String key : map.keySet()){List<String> container = map.get(key);for (String word : wordList){if (oneDiff(key, word)){container.add(word);}}map.put(key, container);}int distance = bfs(beginWord, endWord, wordList);List<List<String>> ans = new ArrayList<>();dfs(map, beginWord, endWord, ans, new ArrayList<>(), distance);return ans;}private void dfs(Map<String, List<String>> map,String beginWord, String endWord, List<List<String>> ans, List<String> path, int distance){path.add(beginWord);if (distance == 0){path.remove(path.size()-1); return;}if (beginWord.equals(endWord)){ans.add(new ArrayList<>(path));path.remove(path.size()-1);return;}for (String find : map.get(beginWord)){dfs(map, find, endWord, ans, path, distance-1);}path.remove(path.size()-1);}private int bfs(String beginWord, String endWord, List<String> wordList) {List<String> reached = new ArrayList<>();reached.add(beginWord);Set<String> wordSet = new HashSet<>(wordList);if(!wordSet.contains(endWord)) return 0;wordSet.add(endWord);int distance = 1;while (!reached.contains(endWord)){ //達到該目的地List<String> toAdd = new ArrayList<>();for (String each : reached){for (int i = 0; i < each.length(); i++){char[] chars = each.toCharArray();for (char c = 'a'; c <= 'z'; c++){chars[i] = c;String wd = new String(chars);if (wordSet.contains(wd)){toAdd.add(wd);wordSet.remove(wd);}}}}distance ++;if (toAdd.size() == 0) return 0;reached = toAdd;}return distance;}private boolean oneDiff(String a, String b){if (a.equals(b)) return false;char[] aa = a.toCharArray();char[] bb = b.toCharArray();int oneDiff = 0;for (int i = 0; i < aa.length; i++){if (aa[i] != bb[i]){oneDiff ++;if (oneDiff >= 2) return false;}}return true;}

思路相當清楚了,以為能夠AC,結果發(fā)現(xiàn)TLE了,說明該題對時間的要求很高,從上述代碼我們也能發(fā)現(xiàn)一些基本問題,如BFS遍歷時可以構建MAP,而不用單獨構建MAP,非常耗時。其次,最關鍵的問題在于DFS,此版本的DFS沒有進行剪枝處理,剪枝能省去很多時間,所以我還需要對BFS進行改進。

思路:
首先,我們來看看上述代碼構建圖的一個模型,如下圖所示:

很明顯,如果我們對BFS沒有做任何限制,我們拿到的鄰接表一定是上述探頭斯,而此時如果用DFS進行搜索時,如從“hot”開始,它會搜索:

一條可能的搜索路徑: hot ---> dot ---> dog ---> cog 但與此同時DFS還會搜索路徑: hot ---> dot ---> tot ---> hot 上述路徑很明顯不需要DFS,但因為邊的相連,使得這種沒必要的搜索也將繼續(xù)。

所以一個優(yōu)化點就在于,好馬不吃回頭草,存在環(huán)路的回頭草絕對不是達到endWord的最短路徑。很遺憾,鄰接表無法表示這種非環(huán)的圖,所以想法就是用一個Map<String,Integer>來記錄到達每個單詞的最短路徑,一旦map中有該單詞,就不再更新最短路徑(避免環(huán)路搜索)

所以BFS代碼如下:

private int bfs(String beginWord, String endWord, Set<String> wordDict, Map<String, Integer> distanceMap,Map<String, List<String>> map) {if (!wordDict.contains(endWord))return 0;map.put(beginWord, new ArrayList<>());for (String word : wordDict) {map.put(word, new ArrayList<>());}Queue<String> queue = new LinkedList<>();queue.offer(beginWord);distanceMap.put(beginWord, 1);while (!queue.isEmpty()) {int count = queue.size();boolean foundEnd = false;// 這種循環(huán)遍歷很有意思,看作一個整體for (int i = 0; i < count; i++) {String cur = queue.poll();int curDistance = distanceMap.get(cur);List<String> neighbors = getNeighbors(cur, wordDict);if (neighbors.size() == 0)return 0;for (String neighbor : neighbors) {map.get(cur).add(neighbor);//存在環(huán)的情況,不去更新最短路徑if (!distanceMap.containsKey(neighbor)) {distanceMap.put(neighbor, curDistance + 1);if (endWord.equals(neighbor)) {foundEnd = true;} else {queue.offer(neighbor);}}}}//一旦抵到了endWord,我們就放棄建立后續(xù)的圖if (foundEnd)break;}return distanceMap.get(endWord);}

上述代碼在BFS時,與endWord無關的那些結點都丟棄掉了,且解決了有環(huán)路的情況。圖結構如下所示:

這樣在DFS構建路徑時,它的速度就比原先要快得多。在BFS中還需要注意一個函數(shù)【getNeighbors()】,剛開始我寫的這版程序也超時了,苦思許久都找不到原因,后來才發(fā)現(xiàn)是getNeighbors的玄機,它在建立鄰接表時,一定要使用【HashSet】的搜索方法,而不要用原生的【List】的搜索方法。

所以完整代碼如下:

public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {Map<String, List<String>> map = new HashMap<>();Map<String, Integer> distanceMap = new HashMap<>();Set<String> wordDict = new HashSet<>(wordList);wordDict.add(beginWord);int distance = bfs(beginWord, endWord, wordDict, distanceMap, map);List<List<String>> ans = new ArrayList<>();if (distance == 0)return ans;dfs(map, beginWord, endWord, ans, new ArrayList<>(), distance, distanceMap);return ans;}private void dfs(Map<String, List<String>> map, String beginWord, String endWord, List<List<String>> ans,List<String> path, int distance, Map<String, Integer> distanceMap) {path.add(beginWord);if (distance == 0) {path.remove(path.size() - 1);return;}if (beginWord.equals(endWord)) {ans.add(new ArrayList<>(path));path.remove(path.size() - 1);return;}for (String find : map.get(beginWord)) {if (!distanceMap.containsKey(find))continue;if (distanceMap.get(beginWord) + 1 == distanceMap.get(find))dfs(map, find, endWord, ans, path, distance - 1, distanceMap);}path.remove(path.size() - 1);}private int bfs(String beginWord, String endWord, Set<String> wordDict, Map<String, Integer> distanceMap,Map<String, List<String>> map) {if (!wordDict.contains(endWord))return 0;map.put(beginWord, new ArrayList<>());for (String word : wordDict) {map.put(word, new ArrayList<>());}Queue<String> queue = new LinkedList<>();queue.offer(beginWord);distanceMap.put(beginWord, 1);while (!queue.isEmpty()) {int count = queue.size();boolean foundEnd = false;for (int i = 0; i < count; i++) {String cur = queue.poll();int curDistance = distanceMap.get(cur);List<String> neighbors = getNeighbors(cur, wordDict);if (neighbors.size() == 0)return 0;for (String neighbor : neighbors) {map.get(cur).add(neighbor);if (!distanceMap.containsKey(neighbor)) {distanceMap.put(neighbor, curDistance + 1);if (endWord.equals(neighbor)) {foundEnd = true;} else {queue.offer(neighbor);}}}}if (foundEnd)break;}return distanceMap.get(endWord);}private List<String> getNeighbors(String word, Set<String> wordList) {List<String> ans = new ArrayList<>();for (int i = 0; i < word.length(); i++) {char[] cc = word.toCharArray();for (char c = 'a'; c <= 'z'; c++) {cc[i] = c;String newWord = new String(cc);if (wordList.contains(newWord)) {if (newWord.equals(word))continue;ans.add(newWord);}}}return ans;}

DFS是一個典型的回溯+剪枝的遞歸方法,凡是函數(shù)返回的地方,我們都需要進行狀態(tài)還原,注意再注意。

總結

以上是生活随笔為你收集整理的算法细节系列(20):Word Ladder系列的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 香蕉亚洲| 久久久av电影 | 激情小说在线 | 伊人色在线 | 河北彩花av在线播放 | 污网站免费观看 | 蜜臀国产AV天堂久久无码蜜臀 | 日韩免费影视 | 少妇裸体视频 | 日本无遮羞调教打屁股网站 | 18国产免费视频 | 天天射网站| 欧美另类一区二区 | 99久热| 不卡的在线视频 | 亚洲国内自拍 | 人人人妻人人澡人人爽欧美一区 | 免费日韩av | 欧美在线v | 中文字幕在线视频一区 | www.国产黄色 | 亚洲一区二区三区麻豆 | 最新版天堂资源在线 | 亚洲av综合色区无码一区 | 亚洲av永久无码精品三区在线 | 国产一区二区三区麻豆 | 91黄色入口 | 香蕉视频首页 | 日韩欧美久久久 | 欧美大片在线免费观看 | 日韩不卡一区二区 | 91秦先生在线播放 | 99re这里都是精品 | 中文天堂在线播放 | av在线浏览 | 777精品 | 成年人在线播放视频 | 在线观看成人小视频 | 亚洲国产影视 | 中文字幕在线播出 | 精品国产午夜福利在线观看 | 少妇人妻综合久久中文字幕 | 国产亚洲精品久久久久四川人 | 91狠狠综合 | 手机看片国产精品 | 日韩欧美国产一区二区 | 热久久精品| 高潮毛片无遮挡免费看 | 欧美大片大全 | 依依av| 澳门超碰 | 嫩草国产精品 | 国产亚洲精品久久久久婷婷瑜伽 | 日韩一区二区三区四区五区 | 亚洲黄色在线观看视频 | 亚洲黄页| 特黄网站 | 欧洲成人综合网 | 国产精品theporn88 | 在线视频播放大全 | 黄色一级片国产 | 亚洲国产精品激情在线观看 | 成人免费毛片入口 | 久久久精品中文字幕麻豆发布 | 黄色三级国产 | 日本另类视频 | 黑丝美女一区二区 | 欧美黄色免费视频 | 国产成人精品自拍 | www精品国产 | 久久久久人妻一区精品色 | 中文字幕有码在线播放 | 精品日韩av | 欧美性生活精品 | 91福利在线免费观看 | 亚洲AV无码一区二区伊人久久 | 国产4区| 午夜不卡福利视频 | 特大黑人巨人吊xxxx | 日韩jizz | 天天操天天干天天 | 在线免费三级 | 丁香婷婷在线 | 女人高潮潮呻吟喷水 | 久久久77 | 少妇被躁爽到高潮 | 国产日韩视频在线观看 | 动漫裸体无遮挡 | 靠逼动漫| 五月婷婷啪啪 | 美腿丝袜亚洲色图 | 欧洲美熟女乱又伦 | 爱福利视频一区二区 | 国产精品亚洲一区二区三区在线观看 | 免费在线观看的黄色网址 | 国产做爰全过程免费视频 | 亚洲va韩国va欧美va | 日韩小视频在线 | 欧美在线一级片 |