chinese直男口爆体育生外卖, 99久久er热在这里只有精品99, 又色又爽又黄18禁美女裸身无遮挡, gogogo高清免费观看日本电视,私密按摩师高清版在线,人妻视频毛茸茸,91论坛 兴趣闲谈,欧美 亚洲 精品 8区,国产精品久久久久精品免费

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

解決一類最難掌握的雙指針技巧:滑動(dòng)窗口技巧

算法與數(shù)據(jù)結(jié)構(gòu) ? 來源:labuladong ? 2020-06-03 17:42 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

關(guān)于雙指針的快慢指針和左右指針的用法,可以參見前文 雙指針技巧匯總,本文就解決一類最難掌握的雙指針技巧:滑動(dòng)窗口技巧,并總結(jié)出一套框架,可以保你閉著眼直接套出答案。

說起滑動(dòng)窗口算法,很多讀者都會(huì)頭疼。這個(gè)算法技巧的思路非常簡(jiǎn)單,就是維護(hù)一個(gè)窗口,不斷滑動(dòng),然后更新答案么。LeetCode 上有起碼 10 道運(yùn)用滑動(dòng)窗口算法的題目,難度都是中等和困難。該算法的大致邏輯如下:

intleft=0,right=0; while(right

這個(gè)算法技巧的時(shí)間復(fù)雜度是 O(N),比一般的字符串暴力算法要高效得多。

其實(shí)困擾大家的,不是算法的思路,而是各種細(xì)節(jié)問題。比如說如何向窗口中添加新元素,如何縮小窗口,在窗口滑動(dòng)的哪個(gè)階段更新結(jié)果。即便你明白了這些細(xì)節(jié),也容易出 bug,找 bug 還不知道怎么找,真的挺讓人心煩的。

所以今天我就寫一套滑動(dòng)窗口算法的代碼框架,我連在哪里做輸出 debug 都給你寫好了,以后遇到相關(guān)的問題,你就默寫出來如下框架然后改三個(gè)地方就行,還不會(huì)出邊界問題:

/*滑動(dòng)窗口算法框架*/ voidslidingWindow(strings,stringt){ unordered_mapneed,window; for(charc:t)need[c]++; intleft=0,right=0; intvalid=0; while(right

其中兩處...表示的更新窗口數(shù)據(jù)的地方,到時(shí)候你直接往里面填就行了。

而且,這兩個(gè)...處的操作分別是右移和左移窗口更新操作,等會(huì)你會(huì)發(fā)現(xiàn)它們操作是完全對(duì)稱的。

說句題外話,其實(shí)有很多人喜歡執(zhí)著于表象,不喜歡探求問題的本質(zhì)。比如說有很多人評(píng)論我這個(gè)框架,說什么散列表速度慢,不如用數(shù)組代替散列表;還有很多人喜歡把代碼寫得特別短小,說我這樣代碼太多余,影響編譯速度,LeetCode 上速度不夠快。

我也是服了,算法看的是時(shí)間復(fù)雜度,你能確保自己的時(shí)間復(fù)雜度最優(yōu)就行了。至于 LeetCode 所謂的運(yùn)行速度,那個(gè)都是玄學(xué),只要不是慢的離譜就沒啥問題,根本不值得你從編譯層面優(yōu)化,不要舍本逐末……

labuladong 公眾號(hào)的重點(diǎn)在于算法思想,你把框架思維了然于心套出解法,然后隨你再魔改代碼好吧,你高興就好。

言歸正傳,下面就直接上四道LeetCode 原題來套這個(gè)框架,其中第一道題會(huì)詳細(xì)說明其原理,后面四道就直接閉眼睛秒殺了。

本文代碼為 C++ 實(shí)現(xiàn),不會(huì)用到什么編程方面的奇技淫巧,但是還是簡(jiǎn)單介紹一下一些用到的數(shù)據(jù)結(jié)構(gòu),以免有的讀者因?yàn)檎Z言的細(xì)節(jié)問題阻礙對(duì)算法思想的理解:

unordered_map就是哈希表(字典),它的一個(gè)方法count(key)相當(dāng)于 Java 的containsKey(key)可以判斷鍵 key 是否存在。

可以使用方括號(hào)訪問鍵對(duì)應(yīng)的值map[key]。需要注意的是,如果該key不存在,C++ 會(huì)自動(dòng)創(chuàng)建這個(gè) key,并把map[key]賦值為 0。

所以代碼中多次出現(xiàn)的map[key]++相當(dāng)于 Java 的map.put(key, map.getOrDefault(key, 0) + 1)。

一、最小覆蓋子串

LeetCode 76 題,Minimum Window Substring,難度Hard,我?guī)Т蠹铱纯此降子卸郒ard:

就是說要在S(source) 中找到包含T(target) 中全部字母的一個(gè)子串,且這個(gè)子串一定是所有可能子串中最短的。

如果我們使用暴力解法,代碼大概是這樣的:

for(inti=0;i

思路很直接,但是顯然,這個(gè)算法的復(fù)雜度肯定大于 O(N^2) 了,不好。

滑動(dòng)窗口算法的思路是這樣:

1、我們?cè)谧址甋中使用雙指針中的左右指針技巧,初始化left = right = 0,把索引左閉右開區(qū)間[left, right)稱為一個(gè)「窗口」。

2、我們先不斷地增加right指針擴(kuò)大窗口[left, right),直到窗口中的字符串符合要求(包含了T中的所有字符)。

3、此時(shí),我們停止增加right,轉(zhuǎn)而不斷增加left指針縮小窗口[left, right),直到窗口中的字符串不再符合要求(不包含T中的所有字符了)。同時(shí),每次增加left,我們都要更新一輪結(jié)果。

4、重復(fù)第 2 和第 3 步,直到right到達(dá)字符串S的盡頭。

這個(gè)思路其實(shí)也不難,第 2 步相當(dāng)于在尋找一個(gè)「可行解」,然后第 3 步在優(yōu)化這個(gè)「可行解」,最終找到最優(yōu)解,也就是最短的覆蓋子串。左右指針輪流前進(jìn),窗口大小增增減減,窗口不斷向右滑動(dòng),這就是「滑動(dòng)窗口」這個(gè)名字的來歷。

下面畫圖理解一下,needs和window相當(dāng)于計(jì)數(shù)器,分別記錄T中字符出現(xiàn)次數(shù)和「窗口」中的相應(yīng)字符的出現(xiàn)次數(shù)。

初始狀態(tài):

增加right,直到窗口[left, right)包含了T中所有字符:

現(xiàn)在開始增加left,縮小窗口[left, right)。

直到窗口中的字符串不再符合要求,left不再繼續(xù)移動(dòng)。

之后重復(fù)上述過程,先移動(dòng)right,再移動(dòng)left…… 直到right指針到達(dá)字符串S的末端,算法結(jié)束。

如果你能夠理解上述過程,恭喜,你已經(jīng)完全掌握了滑動(dòng)窗口算法思想?,F(xiàn)在我們來看看這個(gè)滑動(dòng)窗口代碼框架怎么用:

首先,初始化window和need兩個(gè)哈希表,記錄窗口中的字符和需要湊齊的字符:

unordered_mapneed,window; for(charc:t)need[c]++;

然后,使用left和right變量初始化窗口的兩端,不要忘了,區(qū)間[left, right)是左閉右開的,所以初始情況下窗口沒有包含任何元素:

intleft=0,right=0; intvalid=0; while(right

其中valid變量表示窗口中滿足need條件的字符個(gè)數(shù),如果valid和need.size的大小相同,則說明窗口已滿足條件,已經(jīng)完全覆蓋了串T。

現(xiàn)在開始套模板,只需要思考以下四個(gè)問題:

1、當(dāng)移動(dòng)right擴(kuò)大窗口,即加入字符時(shí),應(yīng)該更新哪些數(shù)據(jù)?

2、什么條件下,窗口應(yīng)該暫停擴(kuò)大,開始移動(dòng)left縮小窗口?

3、當(dāng)移動(dòng)left縮小窗口,即移出字符時(shí),應(yīng)該更新哪些數(shù)據(jù)?

4、我們要的結(jié)果應(yīng)該在擴(kuò)大窗口時(shí)還是縮小窗口時(shí)進(jìn)行更新?

如果一個(gè)字符進(jìn)入窗口,應(yīng)該增加window計(jì)數(shù)器;如果一個(gè)字符將移出窗口的時(shí)候,應(yīng)該減少window計(jì)數(shù)器;當(dāng)valid滿足need時(shí)應(yīng)該收縮窗口;應(yīng)該在收縮窗口的時(shí)候更新最終結(jié)果。

下面是完整代碼:

stringminWindow(strings,stringt){ unordered_mapneed,window; for(charc:t)need[c]++; intleft=0,right=0; intvalid=0; //記錄最小覆蓋子串的起始索引及長(zhǎng)度 intstart=0,len=INT_MAX; while(right

需要注意的是,當(dāng)我們發(fā)現(xiàn)某個(gè)字符在window的數(shù)量滿足了need的需要,就要更新valid,表示有一個(gè)字符已經(jīng)滿足要求。而且,你能發(fā)現(xiàn),兩次對(duì)窗口內(nèi)數(shù)據(jù)的更新操作是完全對(duì)稱的。

當(dāng)valid == need.size()時(shí),說明T中所有字符已經(jīng)被覆蓋,已經(jīng)得到一個(gè)可行的覆蓋子串,現(xiàn)在應(yīng)該開始收縮窗口了,以便得到「最小覆蓋子串」。

移動(dòng)left收縮窗口時(shí),窗口內(nèi)的字符都是可行解,所以應(yīng)該在收縮窗口的階段進(jìn)行最小覆蓋子串的更新,以便從可行解中找到長(zhǎng)度最短的最終結(jié)果。

至此,應(yīng)該可以完全理解這套框架了,滑動(dòng)窗口算法又不難,就是細(xì)節(jié)問題讓人煩得很。以后遇到滑動(dòng)窗口算法,你就按照這框架寫代碼,保準(zhǔn)沒有 bug,還省事兒。

下面就直接利用這套框架秒殺幾道題吧,你基本上一眼就能看出思路了。

二、字符串排列

LeetCode 567 題,Permutation in String,難度 Medium:

注意哦,輸入的s1是可以包含重復(fù)字符的,所以這個(gè)題難度不小。

這種題目,是明顯的滑動(dòng)窗口算法,相當(dāng)給你一個(gè)S和一個(gè)T,請(qǐng)問你S中是否存在一個(gè)子串,包含T中所有字符且不包含其他字符?

首先,先復(fù)制粘貼之前的算法框架代碼,然后明確剛才提出的 4 個(gè)問題,即可寫出這道題的答案:

//判斷s中是否存在t的排列 boolcheckInclusion(stringt,strings){ unordered_mapneed,window; for(charc:t)need[c]++; intleft=0,right=0; intvalid=0; while(right=t.size()){ //在這里判斷是否找到了合法的子串 if(valid==need.size()) returntrue; chard=s[left]; left++; //進(jìn)行窗口內(nèi)數(shù)據(jù)的一系列更新 if(need.count(d)){ if(window[d]==need[d]) valid--; window[d]--; } } } //未找到符合條件的子串 returnfalse; }

對(duì)于這道題的解法代碼,基本上和最小覆蓋子串一模一樣,只需要改變兩個(gè)地方:

1、本題移動(dòng)left縮小窗口的時(shí)機(jī)是窗口大小大于t.size()時(shí),因?yàn)榕帕新铮@然長(zhǎng)度應(yīng)該是一樣的。

2、當(dāng)發(fā)現(xiàn)valid == need.size()時(shí),就說明窗口中就是一個(gè)合法的排列,所以立即返回true。

至于如何處理窗口的擴(kuò)大和縮小,和最小覆蓋子串完全相同。

三、找所有字母異位詞

這是 LeetCode 第 438 題,F(xiàn)ind All Anagrams in a String,難度 Medium:

呵呵,這個(gè)所謂的字母異位詞,不就是排列嗎,搞個(gè)高端的說法就能糊弄人了嗎?相當(dāng)于,輸入一個(gè)串S,一個(gè)串T,找到S中所有T的排列,返回它們的起始索引。

直接默寫一下框架,明確剛才講的 4 個(gè)問題,即可秒殺這道題:

vectorfindAnagrams(strings,stringt){ unordered_mapneed,window; for(charc:t)need[c]++; intleft=0,right=0; intvalid=0; vectorres;//記錄結(jié)果 while(right=t.size()){ //當(dāng)窗口符合條件時(shí),把起始索引加入res if(valid==need.size()) res.push_back(left); chard=s[left]; left++; //進(jìn)行窗口內(nèi)數(shù)據(jù)的一系列更新 if(need.count(d)){ if(window[d]==need[d]) valid--; window[d]--; } } } returnres; }

跟尋找字符串的排列一樣,只是找到一個(gè)合法異位詞(排列)之后將起始索引加入res即可。

四、最長(zhǎng)無重復(fù)子串

這是 LeetCode 第 3 題,Longest Substring Without Repeating Characters,難度 Medium:

這個(gè)題終于有了點(diǎn)新意,不是一套框架就出答案,不過反而更簡(jiǎn)單了,稍微改一改框架就行了:

intlengthOfLongestSubstring(strings){ unordered_mapwindow; intleft=0,right=0; intres=0;//記錄結(jié)果 while(right1){ chard=s[left]; left++; //進(jìn)行窗口內(nèi)數(shù)據(jù)的一系列更新 window[d]--; } //在這里更新答案 res=max(res,right-left); } returnres; }

這就是變簡(jiǎn)單了,連need和valid都不需要,而且更新窗口內(nèi)數(shù)據(jù)也只需要簡(jiǎn)單的更新計(jì)數(shù)器window即可。

當(dāng)window[c]值大于 1 時(shí),說明窗口中存在重復(fù)字符,不符合條件,就該移動(dòng)left縮小窗口了嘛。

唯一需要注意的是,在哪里更新結(jié)果res呢?我們要的是最長(zhǎng)無重復(fù)子串,哪一個(gè)階段可以保證窗口中的字符串是沒有重復(fù)的呢?

這里和之前不一樣,要在收縮窗口完成后更新res,因?yàn)榇翱谑湛s的 while 條件是存在重復(fù)元素,換句話說收縮完成后一定保證窗口中沒有重復(fù)嘛。

五、最后總結(jié)

建議背誦并默寫這套框架,順便背誦一下文章開頭的那首詩。以后就再也不怕子串、子數(shù)組問題了。

我覺得吧,能夠看到這的都是高手,要么就是在成為高手的路上。有了框架,任他窗口怎么滑,東哥這波車開得依然穩(wěn)如老狗,「在看」安排一下。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 算法
    +關(guān)注

    關(guān)注

    23

    文章

    4710

    瀏覽量

    95405

原文標(biāo)題:我寫了套框架,把滑動(dòng)窗口算法變成了默寫題

文章出處:【微信號(hào):TheAlgorithm,微信公眾號(hào):算法與數(shù)據(jù)結(jié)構(gòu)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    harmony-utils之WindowUtil,窗口相關(guān)工具

    harmony-utils之WindowUtil,窗口相關(guān)工具 harmony-utils 簡(jiǎn)介與說明 [harmony-utils] 款功能豐富且極易上手的HarmonyOS工具庫,借助眾多
    的頭像 發(fā)表于 06-30 17:33 ?122次閱讀

    在matlab中如何計(jì)算含有第一類修正的貝塞爾函數(shù)的積分算不出的問題?

    問題摘要: 在matlab中計(jì)算含有第一類修正的貝塞爾函數(shù)的積分時(shí),出現(xiàn)了在部分值能計(jì)算出結(jié)果,部分值無法計(jì)算出結(jié)果的問題。 、變量定義: syms x y=1.0+1i*x; %積分
    發(fā)表于 05-19 16:53

    請(qǐng)問ADS1118 Delta-sigma一類的ADC輸入端的RC濾波器參數(shù)如何計(jì)算?

    最近在使用ADS1118,想要在ADC的輸入端加個(gè)跟隨器以減小輸入阻抗,再在二者之間加入個(gè)RC濾波器,濾波器的參數(shù)不知道如何計(jì)算?請(qǐng)問有相關(guān)資料嗎?(SAR一類的ADC到是很多)。本來想用Tina仿真,結(jié)果沒有spice模
    發(fā)表于 01-14 08:02

    滑動(dòng)變阻器的接線方法 滑動(dòng)變阻器的常見故障與維修

    滑動(dòng)變阻器的接線方法 串聯(lián)接線法 : 將滑動(dòng)變阻器的端接到電路的正極,另端接到電路的負(fù)載(如燈泡)。 負(fù)載的另端接到電路的負(fù)極。
    的頭像 發(fā)表于 11-25 15:03 ?3137次閱讀

    卷積神經(jīng)網(wǎng)絡(luò)的基本原理與算法

    卷積神經(jīng)網(wǎng)絡(luò)(Convolutional Neural Networks,CNN)是一類包含卷積計(jì)算且具有深度結(jié)構(gòu)的前饋神經(jīng)網(wǎng)絡(luò)(Feedforward Neural Networks,F(xiàn)NN
    的頭像 發(fā)表于 11-15 14:47 ?1785次閱讀

    請(qǐng)問TLV320ADC3101是哪一類功放?A還是D?

    TLV320ADC3101是哪一類功放?A還是D?在數(shù)據(jù)手冊(cè)中的哪頁有提到呀?
    發(fā)表于 11-08 07:26

    滑動(dòng)變阻器的作用是什么?

    滑動(dòng)變阻器是種電子元件,主要用于調(diào)整電路中的電阻值,從而實(shí)現(xiàn)對(duì)電流、電壓等電學(xué)參數(shù)的控制。它在電路中起到非常重要的作用,廣泛應(yīng)用于各種電子設(shè)備和實(shí)驗(yàn)裝置中。 滑動(dòng)變阻器可以用于調(diào)整電路的電流,在
    發(fā)表于 10-08 16:39

    如何正確連接和使用滑動(dòng)變阻器?

    滑動(dòng)變阻器是可以改變電阻值的電子元件,廣泛應(yīng)用于各種電子設(shè)備和電路中。正確連接和使用滑動(dòng)變阻器對(duì)于保證電路的正常工作和延長(zhǎng)設(shè)備的使用壽命至關(guān)重要。以下是關(guān)于如何正確連接和使用滑動(dòng)變阻器的
    發(fā)表于 09-27 15:23

    請(qǐng)問INA38一類的電流檢測(cè)芯片被測(cè)電流與芯片可以不共地嗎?

    如題,因?yàn)镸CU是3.3V供電的,而電機(jī)是24V的,單片機(jī)與電機(jī)用的是隔離的DCDC模塊,兩者的參考地是隔離的。現(xiàn)在想通過MCU讀出電機(jī)的電流。不知是否可以用INA138一類的芯片。如果不能,求推薦。謝謝
    發(fā)表于 08-23 08:02

    AMC23C15-Q1具有可調(diào)節(jié)閾值的汽車路、快速響應(yīng)、增強(qiáng)型隔離式窗口比較器數(shù)據(jù)表

    電子發(fā)燒友網(wǎng)站提供《AMC23C15-Q1具有可調(diào)節(jié)閾值的汽車、路、快速響應(yīng)、增強(qiáng)型隔離式窗口比較器數(shù)據(jù)表.pdf》資料免費(fèi)下載
    發(fā)表于 08-16 11:19 ?0次下載
    AMC23C15-Q1具有可調(diào)節(jié)閾值的汽車<b class='flag-5'>類</b>、<b class='flag-5'>雙</b>路、快速響應(yīng)、增強(qiáng)型隔離式<b class='flag-5'>窗口</b>比較器數(shù)據(jù)表

    面試???1:函數(shù)指針指針函數(shù)、數(shù)組指針指針數(shù)組

    在嵌入式開發(fā)領(lǐng)域,函數(shù)指針、指針函數(shù)、數(shù)組指針指針數(shù)組是些非常重要但又容易混淆的概念。理解它們的特性和應(yīng)用場(chǎng)景,對(duì)于提升嵌入式程序的效率
    的頭像 發(fā)表于 08-10 08:11 ?1463次閱讀
    面試???1:函數(shù)<b class='flag-5'>指針</b>與<b class='flag-5'>指針</b>函數(shù)、數(shù)組<b class='flag-5'>指針</b>與<b class='flag-5'>指針</b>數(shù)組

    限流式滑動(dòng)變阻器與分壓式滑動(dòng)變阻器的區(qū)別

    限流式滑動(dòng)變阻器與分壓式滑動(dòng)變阻器是兩種常見的電子元件,它們?cè)陔娐分衅鸬秸{(diào)節(jié)電阻值的作用。盡管它們的基本功能相似,但在設(shè)計(jì)、工作原理、應(yīng)用場(chǎng)景等方面存在些差異。 、結(jié)構(gòu)差異 限流式
    的頭像 發(fā)表于 08-05 14:32 ?2178次閱讀

    滑動(dòng)變阻器的接線方式及原理

    滑動(dòng)變阻器是種常見的電子元件,用于調(diào)節(jié)電路中的電阻值,從而控制電流的大小。在電路設(shè)計(jì)和實(shí)驗(yàn)中,滑動(dòng)變阻器的使用非常廣泛。 滑動(dòng)變阻器的工作原理 要了解
    的頭像 發(fā)表于 08-05 11:42 ?3543次閱讀

    如何測(cè)滑動(dòng)變阻器的阻值范圍

    測(cè)量滑動(dòng)變阻器的阻值范圍是項(xiàng)重要的電子實(shí)驗(yàn)操作,它可以幫助我們了解滑動(dòng)變阻器的性能和特性,從而更好地應(yīng)用在電路設(shè)計(jì)中。 測(cè)量原理 滑動(dòng)變阻器是
    的頭像 發(fā)表于 07-24 14:59 ?4479次閱讀

    如何判斷滑動(dòng)變阻器哪部分接入電路

    滑動(dòng)變阻器是種常見的電子元件,用于調(diào)節(jié)電路中的電阻值。在實(shí)際應(yīng)用中,如何判斷滑動(dòng)變阻器哪部分接入電路是個(gè)重要的問題。 、
    的頭像 發(fā)表于 07-24 14:48 ?3684次閱讀