生活随笔
收集整理的這篇文章主要介紹了
数据结构与算法--图论最短路径算法应用-词阶求解
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
最短路徑案例
- 詞梯應(yīng)用,在一個(gè)詞梯中,每個(gè)單詞均由前一個(gè)單詞改變一個(gè)字母而得到。例如,我們通過一系列單字母替換而得到zero轉(zhuǎn)換為five,如下:five:zero,hero,here,hire,fire,five
- 我們可以看成是一個(gè)無權(quán)最短路徑問題,其中每一個(gè)單詞都是一個(gè)頂點(diǎn),如果兩個(gè)單詞可以通過單字母替換而互相轉(zhuǎn)換,那么他們之間就有邊
- 假設(shè)我們有一個(gè)詞典,由于n個(gè)不同的單詞組成,大部分單詞在6~11個(gè)字母之間,我們?cè)紨?shù)據(jù)將這些單詞存儲(chǔ)在一個(gè)字符串?dāng)?shù)組中。
算法分析
- 首先我們需要一個(gè)比較方法,用來對(duì)比兩個(gè)單詞直接是否只有一個(gè)字符的不同,在這我們同時(shí)考慮增加一個(gè)字符,刪除一個(gè)字符的情況,如下代碼:
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;}
- 首先我們需要對(duì)數(shù)據(jù)進(jìn)行處理,得到一個(gè)Map,其中key是單詞,value是該key單詞通過1個(gè)字母替換能夠從關(guān)鍵字變換成的一系列單詞。我們用兩個(gè)for循環(huán)來對(duì)數(shù)據(jù)進(jìn)行一次遍歷,得到一個(gè)單詞的key對(duì)應(yīng)的數(shù)組:
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
;}
- 以上算法可以得到我們需要的數(shù)據(jù)結(jié)構(gòu),基礎(chǔ)詞典轉(zhuǎn)換成我們需要的鄰接表,其中key相當(dāng)于我們的所有節(jié)點(diǎn),key對(duì)應(yīng)的value值是鄰接節(jié)點(diǎn),這樣我們就可以通過這個(gè)鄰接表來基礎(chǔ)上用最短路徑算法得到我們需要的詞梯信息
- 算法的缺點(diǎn)在于速度太慢,詞語(yǔ)基數(shù)過大時(shí)候,費(fèi)時(shí)比較多,時(shí)間復(fù)雜度O(N^2),我們可以對(duì)以上算法進(jìn)行優(yōu)化,
- 預(yù)處理詞典信息,將轉(zhuǎn)換為Map字典,key值是關(guān)鍵字的長(zhǎng)度,value是該長(zhǎng)度的詞語(yǔ)數(shù)組,也就是我們先通過字符串長(zhǎng)度對(duì)他進(jìn)行歸類,然后繼續(xù)用之前的做法將他轉(zhuǎn)成我們需要的鄰接表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
;}
-
以上算法我們先按長(zhǎng)度分組后對(duì)每個(gè)組進(jìn)行運(yùn)算。
-
我們將這個(gè)Map代表一個(gè)圖的鄰接表表示方法,在此基礎(chǔ)上我們只需要編寫一個(gè)案例用來運(yùn)行單源最短路徑算法,然后在輸出單詞序列
我們通過findChain方法利用上面Map鄰接表信息和兩個(gè)要被鏈接的單詞,同時(shí)返回一個(gè)Map,改Map中,關(guān)鍵字是單詞,而相應(yīng)的值是從first開始的最短詞梯上的關(guān)鍵字簽名的那個(gè)單詞
-
如上面舉例中的那個(gè),如果開始單詞是zero,關(guān)鍵字five的值是fire, 關(guān)鍵字fire的值是hire, 關(guān)鍵字hire的值是here,等等,只要我們得到這樣一個(gè)Map鏈的結(jié)構(gòu),我們就能從后向前回溯出我們需要的詞梯信息,實(shí)現(xiàn)方法如下:
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
;}
- 如上實(shí)現(xiàn)中findChain假設(shè)first是合法的單詞,通過鄰接表中的信息得到對(duì)應(yīng)的鄰接單詞,將鄰接單詞都構(gòu)造成指向first的一個(gè)Map,并且意見處理過的鄰接單詞不會(huì)再次處理,Map相同key存儲(chǔ)一次。
- getChainFromPreviousMap使用prev Map和 second,他是Map中的一個(gè)關(guān)鍵字并返回用于形成詞梯的那些單詞,通過prev向后遍歷map,使用linkedList插表頭的形式得到正確的排序詞梯。
- 如上得到最終的答案,關(guān)鍵步驟是我們?cè)陬A(yù)處理的階段,我們利用圖論的最短路徑算法解決問題首先需要建立一個(gè)鄰接表模型,才能完成后面的Dijkstra算法求解最短路徑問題
上一篇:數(shù)據(jù)結(jié)構(gòu)與算法–圖論,最短路算法,拓?fù)渑判蛩惴?br /> 下一篇:數(shù)據(jù)結(jié)構(gòu)與算法–圖論-深度優(yōu)先搜索及其應(yīng)用
總結(jié)
以上是生活随笔為你收集整理的数据结构与算法--图论最短路径算法应用-词阶求解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。