uiautomation遍历windows所有窗口_万字长文!滑动窗口看这篇就够了!
大家好,我是小浩。今天是小浩算法 “365刷題計劃” 滑動窗口系列 - 整合篇。之前給大家講解過一些滑動窗口的題目,但未作系統整理。
所以我就出了這個整合合集,整合工作中除了保留原有滑動窗口系列的題目,也加入了一些新的內容。希望大家轉發支持!
TCP滑動窗口
滑動窗口最大值
滑動窗口的模式
無重復字符的最長子串
找到字符串中所有字母異位詞
滑動窗口協議(Sliding Window Protocol),屬于TCP協議的一種應用,用于網絡數據傳輸時的流量控制,以避免擁塞的發生。該協議允許發送方在停止并等待確認前發送多個數據分組。由于發送方不必每發一個分組就停下來等待確認,因此該協議可以加速數據的傳輸,提高網絡吞吐量。
大多數人接觸滑動窗口應該是在TCP協議中,當我們從一個機器向另一個機器傳輸數據時,并不能一下子就傳給對方。而是操作系統將這些數據變成連續的數據包,然后一部分一部分的傳給對方。因為網絡就好像一個沙漏,中間的連接管會決定網絡傳輸的真實速度!
如果數據一口氣發送給對方,說小點就是網絡壓力過大丟包重試,說大點沒準就直接導致網絡崩潰。那這里和滑動窗口有什么關系呢?當這些數據包一批一批的發送給對方的時候,其內部實質就是通過一個滑動窗口來維護數據。
下面兩張圖畫的挺好,我就不造輪子了:
那為什么TCP這里要用滑動窗口來實現數據傳輸呢?因為我不是專門講TCP的滑動窗口,所以我就簡單說下:滑動窗口的大小可以依據策略動態調整,應用可根據自身的處理能力來控制窗口的大小,實現流量限制等功能。
(大概就是這么個玩意)
上面的知識,聽懂了也好,沒聽懂也罷。至少到這里你需要知道兩件事:
滑動窗口這玩意,并不是說面試的時候考考,真實場景也會用到!很重要!
滑動窗口的核心,說白了就丫一成語,張弛有度。(動態調整,容易擴展,可長可短,巴拉巴拉.....)
那我們算法里的滑動窗口,其實也是一碼事。常用來處理一些連續問題。不管是固定窗口大小的,還是非固定窗口大小的(可變窗口),統統都屬于滑動窗口類型的題目!下面我將通過幾道經典例題,為大家講解。
02PART滑動窗口最大值先上一道難度比較高的題目!
第239題:給定一個數組 nums,有一個大小為 k 的滑動窗口從數組的最左側移動到數組的最右側。你只可以看到在滑動窗口內的 k 個數字?;瑒哟翱诿看沃幌蛴乙苿右晃?。返回滑動窗口中的最大值。
給定一個數組 nums,有一個大小為 k 的滑動窗口從數組的最左側移動到數組的最右側。你只可以看到在滑動窗口內的 k 個數字?;瑒哟翱诿看沃幌蛴乙苿右晃弧?/p>
返回滑動窗口中的最大值所構成的數組。
示例:
輸入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
輸出: [3,3,5,5,6,7]?
解釋:?
? 滑動窗口的位置? ? ? ? ? ? ? ? 最大值
---------------? ? ? ? ? ? ? ?-----
[1? 3? -1] -3? 5? 3? 6? 7? ? ? ?3
?1 [3? -1? -3] 5? 3? 6? 7? ? ? ?3
?1? 3 [-1? -3? 5] 3? 6? 7? ? ? ?5
?1? 3? -1 [-3? 5? 3] 6? 7? ? ? ?5
?1? 3? -1? -3 [5? 3? 6] 7? ? ? ?6
?1? 3? -1? -3? 5 [3? 6? 7]? ? ? 7
本題對于題目沒有太多需要額外說明的,應該都能理解,直接進行分析。我們很容易想到,可以通過遍歷所有的滑動窗口,找到每一個窗口的最大值,來進行暴力求解。那一共有多少個滑動窗口呢,小學題目,可以得到共有 L-k+1 個窗口。
假設?nums = [1,3,-1,-3,5,3,6,7],和 k = 3,窗口數為 6
根據分析,直接完成代碼:
1//java2class?Solution?{
3????public?int[]?maxSlidingWindow(int[]?nums,?int?k)?{
4????????int?len?=?nums.length;
5????????if?(len?*?k?==?0)?return?new?int[0];
6????????int?[]?win?=?new?int[len?-?k?+?1];
7????????//遍歷所有的滑動窗口
8????????for?(int?i?=?0;?i?1;?i++)?{
9????????????int?max?=?Integer.MIN_VALUE;
10????????????//找到每一個滑動窗口的最大值
11????????????for(int?j?=?i;?j?12????????????????max?=?Math.max(max,?nums[j]);
13????????????}
14????????????win[i]?=?max;
15????????}
16????????return?win;
17????}
18}
It's Bullshit!結果令我們很不滿意,時間復雜度達到了O(LK),如果面試問到這道題,基本上只寫出這樣的代碼,一定就掛掉了。那我們怎么樣優化時間復雜度呢?有沒有可以O(L)的實現呢?=_=
這里不賣關子,其實這道題比較經典,我們可以采用隊列,DP,堆等方式進行求解,所有思路的主要源頭應該都是在窗口滑動的過程中,如何更快的完成查找最大值的過程。但是最典型的解法還是使用雙端隊列。具體怎么來求解,一起看一下。
首先,我們了解一下,什么是雙端隊列:是一種具有隊列和棧的性質的數據結構。雙端隊列中的元素可以從兩端彈出或者插入。
我們可以利用雙端隊列來實現一個窗口,目的是讓該窗口可以做到張弛有度(漢語博大精深,也就是長度動態變化。其實用游標或者其他解法的目的都是一樣的,就是去維護一個可變長的窗口)
然后我們再做一件事,只要遍歷該數組,同時在雙端隊列的頭去維護當前窗口的最大值(在遍歷過程中,發現當前元素比隊列中的元素大,就將原來隊列中的元素祭天),在整個遍歷的過程中我們再記錄下每一個窗口的最大值到結果數組中。最終結果數組就是我們想要的,整體圖解如下。
假設?nums = [1,3,-1,-3,5,3,6,7],和 k = 3
(個人認為我畫的這個圖是目前全網對于雙端隊列本題解法比較清晰的一個...所以我覺得如果不點個贊的話...晤..)
根據分析,得出代碼:
1//go2func?maxSlidingWindow(nums?[]int,?k?int)?[]int?{
3????if?len(nums)?==?0?{
4????????return?[]int{}
5????}
6????//用切片模擬一個雙端隊列
7????queue?:=?[]int{}
8????result?:=?[]int{}
9????for?i?:=?range?nums?{
10????????for?i?>?0?&&?(len(queue)?>?0)?&&?nums[i]?>?queue[len(queue)-1]?{
11????????????//將比當前元素小的元素祭天
12????????????queue?=?queue[:len(queue)-1]
13????????}
14????????//將當前元素放入queue中
15????????queue?=?append(queue,?nums[i])
16????????if?i?>=?k?&&?nums[i-k]?==?queue[0]?{
17????????????//維護隊列,保證其頭元素為當前窗口最大值
18????????????queue?=?queue[1:]
19????????}
20????????if?i?>=?k-1?{
21????????????//放入結果數組
22????????????result?=?append(result,?queue[0])
23????????}
24????}
25????return?result
26}
Perfact,題目完成!看著一下子超越百分之99的用戶,是不是感覺很爽呢?
03PART滑動窗口的模式滑動窗口的題目其實是一定模式的。對于大部分滑動窗口類型的題目,一般是考察字符串的匹配。比較標準的題目,會給出一個模式串B,以及一個目標串A。然后提出問題,找到A中符合對B一些限定規則的子串或者對A一些限定規則的結果,最終再將搜索出的子串完成題意中要求的組合或者其他。
比如:給定一個字符串 s 和一個非空字符串 p,找到 s 中所有是 p 的字母異位詞的子串,返回這些子串的起始索引。
又或者:給你一個字符串 S、一個字符串 T,請在字符串 S 里面找出:包含 T 所有字母的最小子串。
再如:給定一個字符串 s 和一些長度相同的單詞 words。找出 s 中恰好可以由 words 中所有單詞串聯形成的子串的起始位置。
都是屬于這一類的標準題型。而對于這一類題目,我們常用的解題思路,是去維護一個可變長度的滑動窗口。無論是使用雙指針,還是使用雙端隊列,又或者用游標等其他奇技淫巧,目的都是一樣的。? ?
04PART無重復字符的最長子串在上文中,我們使用雙端隊列完成了滑動窗口的一道頗為困難的題目,以此展示了什么是滑動窗口。在本節中我們將繼續深入分析,探索滑動窗口題型一些具有模式性的解法。
第3題:給定一個字符串,請你找出其中不含有重復字符的 最長子串 的長度。
示例 1:
輸入: "abcabcbb"
輸出: 3?
解釋: 因為無重復字符的最長子串是 "abc",所以其長度為 3。
示例 2:
輸入: "bbbbb"
輸出: 1
解釋: 因為無重復字符的最長子串是 "b",所以其長度為 1。
示例 3:
輸入: "pwwkew"
輸出: 3
解釋: 因為無重復字符的最長子串是 "wke",所以其長度為 3。
請注意,你的答案必須是 子串 的長度,"pwke" 是一個子序列,不是子串。
直接分析題目,假設我們的輸入為“abcabcbb”,我們只需要維護一個窗口在輸入字符串中進行移動。如下圖:
當下一個元素在窗口沒有出現過時,我們擴大窗口。
當下一個元素在窗口中出現過時,我們縮小窗口,將出現過的元素以及其左邊的元素統統移出:
在整個過程中,我們記錄下窗口出現過的最大值即可。而我們唯一要做的,只需要盡可能擴大窗口。
那我們代碼中通過什么來維護這樣的一個窗口呢?anyway~ 不管是隊列,雙指針,甚至通過map來做,都可以。
我們演示一個雙指針的做法:
1//java2public?class?Solution?{
3????public?static?int?lengthOfLongestSubstring(String?s)?{
4????????int?n?=?s.length();
5????????Set?set?=?new?HashSet<>(); 6????????int?result?=?0,?left?=?0,?right?=?0; 7????????while?(left? 8????????????//charAt:返回指定位置處的字符 9????????????if?(!set.contains(s.charAt(right)))?{10????????????????set.add(s.charAt(right));11????????????????right++;12????????????????result?=?Math.max(result,?right?-?left);13????????????}?else?{14????????????????set.remove(s.charAt(left));15????????????????left++;16????????????}17????????}18????????return?result;19????}20}
通過觀察,我們能看出來。如果是最壞情況的話,我們每一個字符都可能會訪問兩次,left一次,right一次,時間復雜度達到了O(2N),這是不可饒恕的。不理解的話看下圖:
假設我們的字符串為“abcdc”,對于abc我們都訪問了2次。
那如何來進一步優化呢?
其實我們可以定義字符到索引的映射,而不是簡單通過一個集合來判斷字符是否存在。這樣的話,當我們找到重復的字符時,我們可以立即跳過該窗口,而不需要對之前的元素進行再次訪問。
1//java2public?class?Solution?{
3????public?static?int?lengthOfLongestSubstring(String?s)?{
4????????int?n?=?s.length(),?result?=?0;
5????????Map?map?=?new?HashMap<>();? 6????????for?(int?right?=?0,?left?=?0;?right? 7????????????if?(map.containsKey(s.charAt(right)))?{ 8????????????????left?=?Math.max(map.get(s.charAt(right)),?left); 9????????????}10????????????result?=?Math.max(result,?right?-?left?+?1);11????????????map.put(s.charAt(right),?right?+?1);12????????}13????????return?result;14????}15}
修改之后,我們發現雖然時間復雜度有了一定提高,但是還是比較慢!如何更進一步的優化呢?我們可以使用一個256位的數組來替代hashmap,以進行優化。(因為ASCII碼表里的字符總共有128個。ASCII碼的長度是一個字節,8位,理論上可以表示256個字符,但是許多時候只談128個。具體原因可以下去自行學習~)
1//java2class?Solution?{
3????public?int?lengthOfLongestSubstring(String?s)?{
4????????int?len?=?s.length();
5????????int?result?=?0;
6????????int[]?charIndex?=?new?int[256];
7????????for?(int?left?=?0,?right?=?0;?right? 8????????????char?c?=?s.charAt(right);
9????????????left?=?Math.max(charIndex[c],?left);
10????????????result?=?Math.max(result,?right?-?left?+?1);
11????????????charIndex[c]?=?right?+?1;
12????????}
13????????return?result;
14????}
15}
我們發現優化后時間復雜度有了極大的改善!這里簡單說一下原因,對于數組和hashmap訪問時,兩個誰快誰慢不是一定的,需要思考hashmap的底層實現,以及數據量大小。但是在這里,因為已知了待訪問數據的下標,可以直接尋址,所以極大的縮短了查詢時間。
05PART找到字符串中所有字母異位詞如果完成上面兩道題的學習,相信大家對滑動窗口已不陌生。下面繼續完成一道題目,來進行鞏固學習。
第438題:給定一個字符串 s 和一個非空字符串 p,找到 s 中所有是 p 的字母異位詞的子串,返回這些子串的起始索引。
字符串只包含小寫英文字母,并且字符串 s 和 p 的長度都不超過 20100。
說明:
字母異位詞指字母相同,但排列不同的字符串。
不考慮答案輸出的順序。
示例 1:
輸入:
s: "cbaebabacd" p: "abc"
輸出:
[0, 6]
解釋:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的字母異位詞。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的字母異位詞。
示例 2:
輸入:
s: "abab" p: "ab"
輸出:
[0, 1, 2]
解釋:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的字母異位詞。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的字母異位詞。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的字母異位詞。
直接套用之前的模式,使用雙指針來模擬一個滑動窗口進行解題。分析過程如下:
假設我們有字符串為“cbaebabacd”,目標串為“abc”
我們通過雙指針維護一個窗口,由于我們只需要判斷字母異位詞,我們可以將窗口初始化大小和目標串保持一致。(當然,你也可以初始化窗口為1,逐步擴大)
而判斷字母異位詞,我們需要保證窗口中的字母出現次數與目標串中的字母出現次數一致。這里因為字母只有26個,直接使用數組來替代map進行存儲(和上一講中的ASCII使用256數組存儲思想一致)。
pArr為目標串數組,sArr為窗口數組。我們發現初始化數組,本身就滿足,記錄下來。(這里圖示用map模擬數組,便于理解)
然后我們通過移動窗口,來更新窗口數組,進而和目標數組匹配,匹配成功進行記錄。每一次窗口移動,左指針前移,原來左指針位置處的數值減1,表示字母移出;同時右指針前移,右指針位置處的數值加1,表示字母移入。詳細過程如下:
最終,當右指針到達邊界,意味著匹配完成。然后我們根據分析,給出代碼(整個題解都是套用之前的解題套路。)
class?Solution?{????public?List?findAnagrams(String?s,?String?p)?{
????????if?(s?==?null?||?p?==?null?||?s.length()?return?new?ArrayList<>();
????????List?list?=?new?ArrayList<>();int[]?pArr?=?new?int[26];int[]?sArr?=?new?int[26];for?(int?i?=?0;?i?????????????sArr[s.charAt(i)?-?'a']++;??
????????????pArr[p.charAt(i)?-?'a']++;
????????}for?(int?i?=?0;?i?????????????int?index?=?p.charAt(i)?-?'a';
????????}int?i?=?0;int?j?=?p.length();//?窗口大小固定為p的長度while?(j?????????????if?(isSame(pArr,?sArr))
????????????????list.add(i);????????????//sArr[s.charAt(i)?-?'a']--?左指針位置處字母減1
????????????sArr[s.charAt(i)?-?'a']--;
????????????i++;//sArr[s.charAt(j)?-?'a']++?右指針位置處字母加1
????????????sArr[s.charAt(j)?-?'a']++;
????????????j++;
????????}if?(isSame(?pArr,?sArr))
????????????list.add(i);return?list;
????}public?boolean?isSame(int[]?arr1,?int[]?arr2)?{for?(int?i?=?0;?i?????????????if?(arr1[i]?!=?arr2[i])return?false;return?true;
????}
}
當然,這種解法并不是最優解!大家有興趣的話,可以下去自行再研究一下如何在該解法的基礎上再進行優化。
06PART啰嗦一下關于滑動窗口的整合篇到這里就完事了,大家應該可以發現,不管我們是使用雙指針,又或是雙端隊列來完成滑動窗口的題目,核心思想都是差不多的。
寫原創文章不容易,尤其是算法類文章。但我后面還是會繼續堅持輸出更多優質的內容。感謝大家一直以來的支持!如果覺得還不錯的話,幫我轉發一下!感謝!
80道漫畫圖解算法題匯總(0406版本)
動態規劃入門看這篇就夠了,萬字長文!
如果你問我對學習算法有什么建議,可以看:
漫畫:小白為了面試如何刷題?(嘔心瀝血算法指導篇)
漫畫:嘔心泣血算法指導篇(真正的干貨,怒懟那些說算法沒用的人)
?小浩算法,每日一題
關注領取《圖解算法》高清版
進群的小伙伴請加右側私人微信(備注:進群)
總結
以上是生活随笔為你收集整理的uiautomation遍历windows所有窗口_万字长文!滑动窗口看这篇就够了!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: yii 执行指定迁移文件_MySQL迁移
- 下一篇: win7系统未响应卡住_电脑怎么修复wi