滑动窗口算法学习(一)
目錄
思想
案例
連續(xù)元素最大之和
題目
分析
解法
求相同字母異序詞的下標(biāo)
題目
分析
解法
?
思想
滑動窗口是指一個窗口,在一段區(qū)間上從左到右滑動,一直到區(qū)間的尾部,滑動窗口的長度是可以動態(tài)變化的(也可以固定)。一般情形下可以使用兩個指針分別指向窗口的兩邊,指針 i 指向窗口的左邊界,指針 j 指向窗口的右邊界
一些情況下,使用滑動窗口處理字符串和數(shù)組 能提高效率,減少循環(huán)的嵌套使用,降低時間復(fù)雜度
滑動窗口在網(wǎng)絡(luò)、大數(shù)據(jù)、流量控制等都有應(yīng)用。
下面將簡單介紹使用滑動窗口處理字符串和數(shù)組的案例
案例
連續(xù)元素最大之和
題目
有一個整型數(shù)組,給定一個正整數(shù)k,求數(shù)組里連續(xù)k個數(shù)之和的最大值
Input : arr[] = {100, 200, 300, 400} ?k = 2
Output : 700
Input : arr[] = {1, 4, 2, 10, 23, 3, 1, 0, 20} ?k = 4
Output : 39
Input : arr[] = {2, 3} k = 3 Output : Invalid?
分析
上述問題能想到的最簡單的解法就是遍歷整個數(shù)組,從下標(biāo)為0開始,每次循環(huán)獲取k個連續(xù)的元素,計算其和之后比較獲得最大的和,這樣一來需要兩次循環(huán)(一層遍歷數(shù)組,一層遍歷k個元素)。
我們可以使用滑動窗口來簡化代碼,因為k是給定的固定值,所以窗口大小是固定的,相當(dāng)于窗口向右滑動的時候,只需要減去左側(cè)的值(被移除窗口范圍的舊值),再加上右側(cè)的值(新移入窗口范圍的元素),就可以得到新的連續(xù)K個數(shù)之和,遍歷之后就能得到最大值,只需要一層遍歷就足夠了
解法
public void cal(int[] array, int k) {if (array.length == 0 || k <= 0 || k > array.length) {System.out.println("Invalid");return;}int maxSum = 0;//計算初始窗口位置的K個數(shù)之和for (int i = 0; i < k; i++) {maxSum += array[i];}int windows_sum = maxSum;for (int i = 1; i <= array.length - k; i++) {windows_sum = windows_sum - array[i - 1] + array[k + i - 1];if (windows_sum > maxSum) {maxSum = windows_sum;}}System.out.println("result= " + maxSum);}求相同字母異序詞的下標(biāo)
題目
給定一個字符串s,和一個非空字符串p,要求尋找在s中所有和p滿足相同字母異序詞的子串(s,p全都是由26個小寫英文字母組成,并且長度不大于20100),并返回對應(yīng)的子串的第一個字符的下標(biāo)
Input: s: "cbaebabacd" p: "abc"
Output: [0, 6]
分析
要求找到相同字母異序詞,只需要子串里各個字母的個數(shù)相同即可。
1.使用map集合來表示子串,key為26個英文字母,value為每個字母對應(yīng)的個數(shù)
2.使用int數(shù)組來表示子串(每個元素表示對應(yīng)字母的個數(shù),第一個元素表示’a’的個數(shù),第二個表示’b’的個數(shù),以此類推…),每次滑動的時候判斷目標(biāo)窗口和滑動窗口里的int數(shù)組是否一致
解法
解法一:
使用map里記錄每個子串里的每個字母的元素個數(shù),由于采用判斷map是否有該元素的方式來加快遍歷速度,故每次判斷字母后需要減去1,每結(jié)束一次循環(huán)判斷還需要還原現(xiàn)場(把map還原成初始狀態(tài)initMap)
public static List<Integer> findAnagrams(String s, String p) {List<Integer> result = new ArrayList<Integer>();if (s == null || p == null || p.length() > s.length()) {return result;}Map<Character, Integer> map = new HashMap<Character, Integer>();initMap(p, map);int windowsLen = p.length();//代表左右窗口指針int left = 0;int right = left + windowsLen - 1;while (right < s.length()) {boolean flag = true;List<Character> list = new ArrayList<Character>();for (int i = right; i >= left; i--) {char c = s.charAt(i);Integer count = map.get(c);//i對應(yīng)的字母不在目標(biāo)串里,因為兩個子串的長度一致,故所有包含i上的字母的子串都不滿足條件if (count == null || count == 0) {left = i + 1;right = left + windowsLen - 1;flag = false;break;} else {map.put(c, map.getOrDefault(c, 0) - 1);//把在比較過程中滿足的字母存在list里(因為這里在比較字符串的時候采用相減的方式而不是==)list.add(c);}}//如果退出時,flag表示整個窗口的元素都在map里,說明滿足條件if (flag == true) {result.add(left);left++;right = left + windowsLen - 1;}//還原現(xiàn)場,讓map的數(shù)據(jù)保持和p的組成一致for (Character c : list) {map.put(c, map.getOrDefault(c, 0) + 1);}}return result;}private static void initMap(String p, Map<Character, Integer> map) {map.clear();for (char c : p.toCharArray()) {map.put(c, map.getOrDefault(c, 0) + 1);}}?
解法二:
上述代碼比較繁瑣,解法二使用int數(shù)組,直接比較數(shù)組,邏輯上更加清晰
public List<Integer> findAnagrams(String s, String p) {List<Integer> ans = new ArrayList<>();if (s.length() < p.length()) {return ans;}int[] dict = new int[26];//使用26長度的整型數(shù)組來表示給定字符串p的組成for (char c : p.toCharArray()) {dict[c - 'a']++;}//用來記錄滑動窗口里的字符串的組成int[] cur = new int[26];for (int i = 0; i < p.length() ; i++) {cur[s.charAt(i) - 'a']++;}for (int i = p.length() ; i < s.length(); i++) {if (isSame(dict, cur)) {ans.add(i - p.length() );}cur[s.charAt(i - p.length() ) - 'a']--;cur[s.charAt(i) - 'a']++;}return ans;}//判斷兩個int數(shù)組是否一致private boolean isSame(int[] a, int[] b) {for (int i = 0; i < 26; i++) {if (a[i] != b[i]) {return false;}}return true;}?
?
總結(jié)
以上是生活随笔為你收集整理的滑动窗口算法学习(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 设计模式--装饰模式
- 下一篇: Redis之数据结构底层实现