生活随笔
收集整理的這篇文章主要介紹了
数据结构与算法--图论最短路径算法应用-词阶求解
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
最短路徑案例
- 詞梯應用,在一個詞梯中,每個單詞均由前一個單詞改變一個字母而得到。例如,我們通過一系列單字母替換而得到zero轉換為five,如下:five:zero,hero,here,hire,fire,five
- 我們可以看成是一個無權最短路徑問題,其中每一個單詞都是一個頂點,如果兩個單詞可以通過單字母替換而互相轉換,那么他們之間就有邊
- 假設我們有一個詞典,由于n個不同的單詞組成,大部分單詞在6~11個字母之間,我們原始數據將這些單詞存儲在一個字符串數組中。
算法分析
- 首先我們需要一個比較方法,用來對比兩個單詞直接是否只有一個字符的不同,在這我們同時考慮增加一個字符,刪除一個字符的情況,如下代碼:
public static boolean oneCharOff(String word1
, String word2
) {if (word1
== null
|| word2
== null
|| word1
.length() != word2
.length()) {return false;}if (word1
== word2
) {return false;}for (int i
= 0; i
< word1
.length(); i
++) {if (word1
.charAt(i
) != word2
.charAt(i
)) {if (word1
.substring(i
+ 1, word1
.length()) != word2
.substring(i
+ 1, word2
.length())) {return false;}}}return true;}public static boolean oneCharAdd(String word1
, String word2
){if(word1
== null
|| word2
== null
|| Math
.abs(word1
.length() - word2
.length()) != 1){return false;}String largeWord
= word1
.length() > word2
.length() ? word2
: word1
;String shortWord
= word1
.length() > word2
.length() ? word1
: word2
;Integer count
= 0;Integer largePis
= 0;for (int i
= 0; i
< shortWord
.length(); i
++) {if(shortWord
.charAt(i
) != largeWord
.charAt(largePis
)){count
++;if(count
> 1){return false;}if(shortWord
.charAt(i
) != largeWord
.charAt(++largePis
)){return false;}}largePis
++;}return true;}
- 首先我們需要對數據進行處理,得到一個Map,其中key是單詞,value是該key單詞通過1個字母替換能夠從關鍵字變換成的一系列單詞。我們用兩個for循環來對數據進行一次遍歷,得到一個單詞的key對應的數組:
public static void update(Map
<String
, List
<String>> map
, String key
, String value
) {List
<String> list
= map
.get(key
);if (list
== null
) {list
= new ArrayList<>();map
.put(key
, list
);}list
.add(value
);}public static Map
<String
, List
<String>> computeAdjacentWords(List
<String> basicWords
) {Map
<String
, List
<String>> adjMap
= new HashMap<>();for (int i
= 0; i
< basicWords
.size(); i
++) {for (int j
= i
+ 1; j
< basicWords
.size(); j
++) {if (oneCharOff(basicWords
.get(i
), basicWords
.get(j
)) || oneCharAdd(basicWords
.get(i
), basicWords
.get(j
))) {update(adjMap
, basicWords
.get(i
), basicWords
.get(j
));update(adjMap
, basicWords
.get(j
), basicWords
.get(i
));}}}return adjMap
;}
- 以上算法可以得到我們需要的數據結構,基礎詞典轉換成我們需要的鄰接表,其中key相當于我們的所有節點,key對應的value值是鄰接節點,這樣我們就可以通過這個鄰接表來基礎上用最短路徑算法得到我們需要的詞梯信息
- 算法的缺點在于速度太慢,詞語基數過大時候,費時比較多,時間復雜度O(N^2),我們可以對以上算法進行優化,
- 預處理詞典信息,將轉換為Map字典,key值是關鍵字的長度,value是該長度的詞語數組,也就是我們先通過字符串長度對他進行歸類,然后繼續用之前的做法將他轉成我們需要的鄰接表Map,如下代碼:
public static Map
<String
, List
<String>> computeAdjacentWords_v1(List
<String> basicWords
) {Map
<String
, List
<String>> adjMap
= new HashMap<>();Map
<String
, List
<String>> lengthMap
= new HashMap<>();for (String basicWord
: basicWords
) {update(lengthMap
, String
.valueOf(basicWord
.length()), basicWord
);}for (List
<String> strings
: lengthMap
.values()) {for (int i
= 0; i
< strings
.size(); i
++) {for (int j
= i
+ 1; j
< strings
.size(); j
++) {if (oneCharOff(strings
.get(i
), strings
.get(j
)) || oneCharAdd(strings
.get(i
), strings
.get(j
))) {update(adjMap
, strings
.get(i
), strings
.get(j
));update(adjMap
, strings
.get(j
), strings
.get(i
));}}}}return adjMap
;}
-
以上算法我們先按長度分組后對每個組進行運算。
-
我們將這個Map代表一個圖的鄰接表表示方法,在此基礎上我們只需要編寫一個案例用來運行單源最短路徑算法,然后在輸出單詞序列
我們通過findChain方法利用上面Map鄰接表信息和兩個要被鏈接的單詞,同時返回一個Map,改Map中,關鍵字是單詞,而相應的值是從first開始的最短詞梯上的關鍵字簽名的那個單詞
-
如上面舉例中的那個,如果開始單詞是zero,關鍵字five的值是fire, 關鍵字fire的值是hire, 關鍵字hire的值是here,等等,只要我們得到這樣一個Map鏈的結構,我們就能從后向前回溯出我們需要的詞梯信息,實現方法如下:
public static List
<String> findChain(List
<String> baseWords
, String first
, String second
) {Map
<String
, List
<String>> adjacentWords
= computeAdjacentWords_v1(baseWords
);LinkedList
<String> q
= new LinkedList<>();Map
<String, String> previousWords
= new HashMap<>();q
.add(first
);while (!q
.isEmpty()) {String current
= q
.removeFirst();List
<String> currentArray
= adjacentWords
.get(current
);if (currentArray
!= null
&& currentArray
.size() > 0) {for (String adjWord
: currentArray
) {if(previousWords
.get(adjWord
) == null
){previousWords
.put(adjWord
, current
);q
.add(adjWord
);}}}}previousWords
.put(first
, null
);return getChainFromPreviousMap(previousWords
, first
, second
);}public static List
<String> getChainFromPreviousMap( Map
<String, String> previousWords
, String first
, String second
){LinkedList
<String> q
= null
;if(previousWords
.get(second
) != null
){q
= new LinkedList<>();for(String str
= second
; str
!= null
; str
= previousWords
.get(str
)){q
.addFirst(str
);}}return q
;}
- 如上實現中findChain假設first是合法的單詞,通過鄰接表中的信息得到對應的鄰接單詞,將鄰接單詞都構造成指向first的一個Map,并且意見處理過的鄰接單詞不會再次處理,Map相同key存儲一次。
- getChainFromPreviousMap使用prev Map和 second,他是Map中的一個關鍵字并返回用于形成詞梯的那些單詞,通過prev向后遍歷map,使用linkedList插表頭的形式得到正確的排序詞梯。
- 如上得到最終的答案,關鍵步驟是我們在預處理的階段,我們利用圖論的最短路徑算法解決問題首先需要建立一個鄰接表模型,才能完成后面的Dijkstra算法求解最短路徑問題
上一篇:數據結構與算法–圖論,最短路算法,拓撲排序算法
下一篇:數據結構與算法–圖論-深度優先搜索及其應用
總結
以上是生活随笔為你收集整理的数据结构与算法--图论最短路径算法应用-词阶求解的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。