<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          ?LeetCode刷題實戰(zhàn)127:單詞接龍

          共 4750字,需瀏覽 10分鐘

           ·

          2020-12-21 15:13

          算法的重要性,我就不多說了吧,想去大廠,就必須要經(jīng)過基礎(chǔ)知識和業(yè)務(wù)邏輯面試+算法面試。所以,為了提高大家的算法能力,這個公眾號后續(xù)每天帶大家做一道算法題,題目就從LeetCode上面選 !

          今天和大家聊的問題叫做?單詞接龍,我們先來看題面:
          https://leetcode-cn.com/problems/word-ladder/

          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:


          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.

          題意


          給定兩個單詞(beginWord 和 endWord)和一個字典,找到從 beginWord 到 endWord 的最短轉(zhuǎn)換序列的長度。轉(zhuǎn)換需遵循如下規(guī)則:

          • 每次轉(zhuǎn)換只能改變一個字母。

          • 轉(zhuǎn)換過程中的中間單詞必須是字典中的單詞。



          說明:

          • 如果不存在這樣的轉(zhuǎn)換序列,返回 0。

          • 所有單詞具有相同的長度。

          • 所有單詞只由小寫字母組成。

          • 字典中不存在重復(fù)的單詞。

          • 你可以假設(shè) beginWord 和 endWord 是非空的,且二者不相同。


          樣例

          示例?1:

          輸入:
          beginWord = "hit",
          endWord = "cog",
          wordList = ["hot","dot","dog","lot","log","cog"]

          輸出: 5

          解釋: 一個最短轉(zhuǎn)換序列是 "hit"?-> "hot"?-> "dot"?-> "dog"?-> "cog",
          ?????返回它的長度 5

          示例 2:

          輸入:
          beginWord = "hit"
          endWord = "cog"
          wordList = ["hot","dot","dog","lot","log"]

          輸出:?0

          解釋:?endWord "cog"?不在字典中,所以無法進行轉(zhuǎn)換。


          解題

          本文來源:
          https://leetcode-cn.com/problems/word-ladder/solution/yan-du-you-xian-bian-li-shuang-xiang-yan-du-you-2/

          分析題意即:兩個單詞對應(yīng)位置只有一個字符不同,例如 "hit" 與 "hot",這種轉(zhuǎn)換是可以逆向的,因此,根據(jù)題目給出的單詞列表,可以構(gòu)建出一個無向(無權(quán))圖;

          如果一開始就構(gòu)建圖,每一個單詞都需要和除它以外的另外的單詞進行比較,復(fù)雜度是 O(N wordLen),這里 N 是單詞列表的長度;
          為此,我們在遍歷一開始,把所有的單詞列表放進一個哈希表中,然后在遍歷的時候構(gòu)建圖,每一次得到在單詞列表里可以轉(zhuǎn)換的單詞,復(fù)雜度是 O(26×wordLen),借助哈希表,找到鄰居與 N無關(guān);
          使用 BFS 進行遍歷,需要的輔助數(shù)據(jù)結(jié)構(gòu)是:
          • 隊列;

          • visited 集合。說明:可以直接在 wordSet (由 wordList 放進集合中得到)里做刪除。但更好的做法是新開一個哈希表,遍歷過的字符串放進哈希表里。這種做法具有普遍意義。絕大多數(shù)在線測評系統(tǒng)和應(yīng)用場景都不會在意空間開銷。


          方法一:廣度優(yōu)先遍歷


          public?class?Solution?{

          ????public?int?ladderLength(String beginWord, String endWord, List wordList)?{
          ????????// 第 1 步:先將 wordList 放到哈希表里,便于判斷某個單詞是否在 wordList 里
          ????????Set wordSet = new?HashSet<>(wordList);
          ????????if?(wordSet.size() == 0?|| !wordSet.contains(endWord)) {
          ????????????return?0;
          ????????}
          ????????wordSet.remove(beginWord);
          ????????
          ????????// 第 2 步:圖的廣度優(yōu)先遍歷,必須使用隊列和表示是否訪問過的 visited 哈希表
          ????????Queue queue?= new?LinkedList<>();
          ????????queue.offer(beginWord);
          ????????Set visited = new?HashSet<>();
          ????????visited.add(beginWord);
          ????????
          ????????// 第 3 步:開始廣度優(yōu)先遍歷,包含起點,因此初始化的時候步數(shù)為 1
          ????????int?step = 1;
          ????????while?(!queue.isEmpty()) {
          ????????????int?currentSize = queue.size();
          ????????????for?(int?i = 0; i < currentSize; i++) {
          ????????????????// 依次遍歷當(dāng)前隊列中的單詞
          ????????????????String currentWord = queue.poll();
          ????????????????// 如果 currentWord 能夠修改 1 個字符與 endWord 相同,則返回 step + 1
          ????????????????if?(changeWordEveryOneLetter(currentWord, endWord, queue, visited, wordSet)) {
          ????????????????????return?step + 1;
          ????????????????}
          ????????????}
          ????????????step++;
          ????????}
          ????????return?0;
          ????}

          ????/**
          ?????* 嘗試對 currentWord 修改每一個字符,看看是不是能與 endWord 匹配
          ?????*
          ?????* @param currentWord
          ?????* @param endWord
          ?????* @param queue
          ?????* @param visited
          ?????* @param wordSet
          ?????* @return
          ?????*/

          ????private?boolean changeWordEveryOneLetter(String currentWord, String endWord,
          ?????????????????????????????????????????????Queue queue, Set visited, Set wordSet)
          ?
          {
          ????????char[] charArray = currentWord.toCharArray();
          ????????for?(int?i = 0; i < endWord.length(); i++) {
          ????????????// 先保存,然后恢復(fù)
          ????????????char?originChar = charArray[i];
          ????????????for?(char?k = 'a'; k <= 'z'; k++) {
          ????????????????if?(k == originChar) {
          ????????????????????continue;
          ????????????????}
          ????????????????charArray[i] = k;
          ????????????????String nextWord = String.valueOf(charArray);
          ????????????????if?(wordSet.contains(nextWord)) {
          ????????????????????if?(nextWord.equals(endWord)) {
          ????????????????????????return?true;
          ????????????????????}
          ????????????????????if?(!visited.contains(nextWord)) {
          ????????????????????????queue.add(nextWord);
          ????????????????????????// 注意:添加到隊列以后,必須馬上標(biāo)記為已經(jīng)訪問
          ????????????????????????visited.add(nextWord);
          ????????????????????}
          ????????????????}
          ????????????}
          ????????????// 恢復(fù)
          ????????????charArray[i] = originChar;
          ????????}
          ????????return?false;
          ????}
          }


          方法二:雙向廣度優(yōu)先遍歷

          已知目標(biāo)頂點的情況下,可以分別從起點和目標(biāo)頂點(終點)執(zhí)行廣度優(yōu)先遍歷,直到遍歷的部分有交集。這種方式搜索的單詞數(shù)量會更小一些;
          更合理的做法是,每次從單詞數(shù)量小的集合開始擴散;
          這里 beginVisited 和 endVisited 交替使用,等價于單向 BFS 里使用隊列,每次擴散都要加到總的 visited 里。

          public class?Solution?{

          ????public int ladderLength(String?beginWord, String?endWord, List<String> wordList) {
          ????????// 第 1 步:先將 wordList 放到哈希表里,便于判斷某個單詞是否在 wordList 里
          ????????Set<String> wordSet = new?HashSet<>(wordList);
          ????????if?(wordSet.size() == 0?|| !wordSet.contains(endWord)) {
          ????????????return?0;
          ????????}

          ????????// 第 2 步:已經(jīng)訪問過的 word 添加到 visited 哈希表里
          ????????Set<String> visited = new?HashSet<>();
          ????????// 分別用左邊和右邊擴散的哈希表代替單向 BFS 里的隊列,它們在雙向 BFS 的過程中交替使用
          ????????Set<String> beginVisited = new?HashSet<>();
          ????????beginVisited.add(beginWord);
          ????????Set<String> endVisited = new?HashSet<>();
          ????????endVisited.add(endWord);

          ????????// 第 3 步:執(zhí)行雙向 BFS,左右交替擴散的步數(shù)之和為所求
          ????????int step = 1;
          ????????while?(!beginVisited.isEmpty() && !endVisited.isEmpty()) {
          ????????????// 優(yōu)先選擇小的哈希表進行擴散,考慮到的情況更少
          ????????????if?(beginVisited.size() > endVisited.size()) {
          ????????????????Set<String> temp = beginVisited;
          ????????????????beginVisited = endVisited;
          ????????????????endVisited = temp;
          ????????????}

          ????????????// 邏輯到這里,保證 beginVisited 是相對較小的集合,nextLevelVisited 在擴散完成以后,會成為新的 beginVisited
          ????????????Set<String> nextLevelVisited = new?HashSet<>();
          ????????????for?(String?word : beginVisited) {
          ????????????????if?(changeWordEveryOneLetter(word, endVisited, visited, wordSet, nextLevelVisited)) {
          ????????????????????return?step + 1;
          ????????????????}
          ????????????}

          ????????????// 原來的 beginVisited 廢棄,從 nextLevelVisited 開始新的雙向 BFS
          ????????????beginVisited = nextLevelVisited;
          ????????????step++;
          ????????}
          ????????return?0;
          ????}


          ????/**
          ?????* 嘗試對 word 修改每一個字符,看看是不是能落在 endVisited 中,擴展得到的新的 word 添加到 nextLevelVisited 里
          ?????*
          ?????* @param word
          ?????* @param endVisited
          ?????* @param visited
          ?????* @param wordSet
          ?????* @param nextLevelVisited
          ?????* @return
          ?????*/

          ????private boolean changeWordEveryOneLetter(String?word, Set<String> endVisited,
          ?????????????????????????????????????????????Set<String> visited,
          ?????????????????????????????????????????????Set<String> wordSet,
          ?????????????????????????????????????????????Set<String> nextLevelVisited) {
          ????????char[] charArray = word.toCharArray();
          ????????for?(int i = 0; i < word.length(); i++) {
          ????????????char originChar = charArray[i];
          ????????????for?(char c = 'a'; c <= 'z'; c++) {
          ????????????????if?(originChar == c) {
          ????????????????????continue;
          ????????????????}
          ????????????????charArray[i] = c;
          ????????????????String?nextWord = String.valueOf(charArray);
          ????????????????if?(wordSet.contains(nextWord)) {
          ????????????????????if?(endVisited.contains(nextWord)) {
          ????????????????????????return?true;
          ????????????????????}
          ????????????????????if?(!visited.contains(nextWord)) {
          ????????????????????????nextLevelVisited.add(nextWord);
          ????????????????????????visited.add(nextWord);
          ????????????????????}
          ????????????????}
          ????????????}
          ????????????// 恢復(fù),下次再用
          ????????????charArray[i] = originChar;
          ????????}
          ????????return?false;
          ????}
          }


          好了,今天的文章就到這里,如果覺得有所收獲,請順手點個在看或者轉(zhuǎn)發(fā)吧,你們的支持是我最大的動力。

          上期推文:

          LeetCode1-120題匯總,希望對你有點幫助!
          LeetCode刷題實戰(zhàn)121:買賣股票的最佳時機
          LeetCode刷題實戰(zhàn)122:買賣股票的最佳時機 II
          LeetCode刷題實戰(zhàn)123:買賣股票的最佳時機 III
          LeetCode刷題實戰(zhàn)124:二叉樹中的最大路徑和
          LeetCode刷題實戰(zhàn)125:驗證回文串
          LeetCode刷題實戰(zhàn)126:單詞接龍 II


          瀏覽 72
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产精品亚洲专区在线播放麻豆 | 黑人巨粗进入疼哭A片 | 免费看A片秘 免费 | 日韩在线免费 | 大香蕉伊人m |