Java滑動(dòng)窗口算法題目練習(xí)(附詳細(xì)圖文及代碼)
長(zhǎng)度最小的子數(shù)組

題目解析:這里給我們一個(gè)全是正整數(shù)的數(shù)組和一個(gè)目標(biāo)值,讓我們找一段連續(xù)的區(qū)間,這個(gè)區(qū)間的值之和是大于等于目標(biāo)值的,從這個(gè)數(shù)組中找到一個(gè)最小的區(qū)間長(zhǎng)度,如果不存在的話就返回0
算法原理:1.首先我們是可以使用暴力解法,雙重for循環(huán)進(jìn)行遍歷出所有的情況,當(dāng)滿足區(qū)間的值大于等于目標(biāo)值的話就進(jìn)行結(jié)果更新,反之繼續(xù)向后操作,我們可以發(fā)現(xiàn)這里是有許多的是多余的,就像如果此時(shí)的這個(gè)區(qū)間的值已經(jīng)大于這個(gè)目標(biāo)值了,如果繼續(xù)向后操作的話,這個(gè)數(shù)組是正整數(shù)數(shù)組,肯定還是大于等于目標(biāo)值,但是長(zhǎng)度變長(zhǎng)了,我們要找到是最短的,因此我們可以不需要讓其重復(fù)操作,直接開(kāi)始下一次循環(huán)就行
2."同向雙指針"也叫滑動(dòng)窗口算法,這里我們可以使用left和right兩個(gè)指針向同一個(gè)方向移動(dòng),并且不回退,此時(shí)的思想就和上面暴力解法優(yōu)化思想一樣,一直進(jìn)行將right下標(biāo)對(duì)應(yīng)的值放入sum變量中,當(dāng)sum>=target時(shí)候就可以將minlen更新了,此時(shí)不必將right向后移動(dòng)了,只需要將left下標(biāo)的值減去,讓left向后移動(dòng),這樣重復(fù)操作進(jìn)行查找,直到right遍歷完整個(gè)數(shù)組就完成了




class Solution {
public int minSubArrayLen(int target, int[] nums) {
int sum = 0;
int len = Integer.MAX_VALUE;//剛開(kāi)始定義長(zhǎng)度是int可以表示最大整數(shù)
int n = nums.length;
for(int left = 0,right = 0; right < n;right++){
sum += nums[right];//入窗口
//結(jié)果判斷
while(sum >= target){
len = Math.min(len , right - left + 1);//結(jié)果更新
sum -= nums[left++]; //出窗口
}
}
return len == Integer.MAX_VALUE ? 0 : len;
}
}
時(shí)間復(fù)雜度:O(N),空間復(fù)雜度:O(1)
無(wú)重復(fù)字符的最長(zhǎng)子串

題目解析:這里就是給了我們一個(gè)字符串s,讓我們?cè)诶锩嬲页?strong>最長(zhǎng)連續(xù)不重復(fù)的子串長(zhǎng)度,如果是空字符串就返回0
算法原理:1.暴力解法+哈希表(判斷字符是否重復(fù)出現(xiàn)),就是將其字符串中所有情況全部遍歷一遍,將其存放在哈希表中,遍歷的過(guò)程中判斷是否已經(jīng)存放在哈希表中,如果存在這時(shí)候就更新結(jié)果,并就跳出內(nèi)循環(huán),就這樣一直重復(fù)操作
2.滑動(dòng)窗口+哈希表,上面的暴力解法中會(huì)出現(xiàn)一些多余的操作,這里我們用left和right這兩個(gè)同向雙指針,不回退,right用來(lái)遍歷整個(gè)字符串并將其放入哈希表中,并當(dāng)right下標(biāo)的字符存放后發(fā)現(xiàn)其如果沒(méi)重復(fù)就更新結(jié)果 ,如果存在重復(fù),那肯定是此下標(biāo)有重復(fù)的,這時(shí)候就將left對(duì)應(yīng)的字符從哈希表中刪除,但我們可能要?jiǎng)h除很多次,因?yàn)槲覀儾⒉恢榔淠莻€(gè)是重復(fù)的,所以此時(shí)是個(gè)循環(huán),直到?jīng)]有重復(fù)為止繼續(xù)更新結(jié)果,就這樣一直right放入窗口,判斷是否有重復(fù)字符,有的話就一直出left到?jīng)]有重復(fù)字符為止,更新結(jié)果,就這樣一直到right遍歷完整個(gè)字符串



我們這里是使用int類型的數(shù)組來(lái)模擬哈希表,因?yàn)樽址麑?duì)應(yīng)的是ASCLL碼值
因此我們可以根據(jù)每次存放將其下標(biāo)的值++,大于1說(shuō)明有重復(fù)的就進(jìn)行出窗口
class Solution {
public int lengthOfLongestSubstring(String ss) {
int[] hash = new int[128];//這里使用數(shù)組來(lái)表示其hash,如果其對(duì)應(yīng)的位置>1說(shuō)明有重復(fù)的
int len = 0;
char[] s = ss.toCharArray();//將其字符串轉(zhuǎn)換成字符數(shù)組,方便后面使用
//使用同向雙指針,滑動(dòng)窗口
for(int left = 0,right = 0;right < s.length;right++){
//入窗口
hash[s[right]]++;//字符對(duì)應(yīng)的ASCLL碼值作為下標(biāo)++
//判斷,如果>1,說(shuō)明有重復(fù)的了,就要出窗口
while(hash[s[right]] > 1){
//出窗口
hash[s[left++]]--;
}
//更新結(jié)果
len = Math.max(len,right - left+1);
}
return len;
}
}
最大連續(xù)1的個(gè)數(shù)|||

題目解析:題目就是找到最大連續(xù)1的個(gè)數(shù),并且最多可以反轉(zhuǎn)k個(gè)0
因此我們可以將其題目轉(zhuǎn)換成找出一個(gè)最長(zhǎng)子數(shù)組并且里面0的個(gè)數(shù)步超過(guò)k個(gè)
算法原理:1.暴力枚舉+count(記錄當(dāng)前區(qū)間0的個(gè)數(shù))
2.滑動(dòng)窗口+count(記錄當(dāng)前區(qū)間0的個(gè)數(shù))




class Solution {
public int longestOnes(int[] nums, int k) {
int len = 0;
int n = nums.length;
int count = 0;//用于統(tǒng)計(jì)當(dāng)前區(qū)間0的個(gè)數(shù)
for(int left = 0,right = 0;right < n;right++){
//記錄當(dāng)前0的個(gè)數(shù)
if(nums[right] == 0){
count++;
}
//出窗口
while(count > k){
if(nums[left++] == 0){
count--;
}
}
//更新結(jié)果
len = Math.max(len , right - left + 1);
}
return len;
}
}
時(shí)間復(fù)雜度:O(N),空間復(fù)雜度:O(1)
將x減到0的最小操作數(shù)

題目解析:就是給了我們一個(gè)目標(biāo)值x讓我們每次從nums數(shù)組最左邊或者最右邊找一個(gè)數(shù),將其減去,減去就要從數(shù)組中移除這個(gè)數(shù),就這樣一直重復(fù)操作,找出一個(gè)最小操作數(shù),我們可以發(fā)現(xiàn)這個(gè)問(wèn)題十分繁瑣,因?yàn)槲覀兠看我膊恢钡绞菑淖筮呥€是右邊
因此我們可以正難則反 我們可以將題目轉(zhuǎn)換為在這個(gè)數(shù)組連續(xù)區(qū)間找一個(gè)最長(zhǎng)的長(zhǎng)度和為 整個(gè)數(shù)組的和 - x,最后返回?cái)?shù)組長(zhǎng)度 - 找到的長(zhǎng)度就行
算法原理:首先算出總的數(shù)組和sum ,我們的目標(biāo)值target = sum - x,我們直接從數(shù)組中找出一個(gè)最長(zhǎng)長(zhǎng)度區(qū)間,使其值等于target
這里找到target目標(biāo)值是采用滑動(dòng)窗口即同向雙指針,我們使用left和right兩個(gè)指針,right遍歷整個(gè)數(shù)組,total記錄當(dāng)前區(qū)間的值,
當(dāng) total == target時(shí)候就更新結(jié)果
當(dāng)total > target就出窗口,刪除left下標(biāo)的值,并讓left++,直到total <= target為止
反之就是小于,就一直將right下標(biāo)的值入窗口



class Solution {
public int minOperations(int[] nums, int x) {
//這里采用正難則反的思想,我們是每次從最右邊和最左邊找數(shù)相加,看何時(shí)等于x
//我們可以轉(zhuǎn)換為在這個(gè)數(shù)組連續(xù)區(qū)間找一個(gè)最長(zhǎng)的長(zhǎng)度和為 整個(gè)數(shù)組的和 - x
int sum = 0;//nums整個(gè)數(shù)組的和
int n = nums.length;
for(int i = 0; i < n;i++){
sum += nums[i];
}
//此時(shí)我們的目標(biāo)值變成sum - x
int target = sum - x;
//如果全部sum和都<x說(shuō)明找不到
if(target < 0){
return -1;
}
int total = 0;
int len = -1;
//后面就使用滑動(dòng)窗口來(lái)找最長(zhǎng)子串使其長(zhǎng)度等于target目標(biāo)值
for(int left = 0,right = 0;right < n;right++){
//入窗口
total += nums[right];
//判斷
while(total > target){
total -= nums[left++];//出窗口
}
//等于的時(shí)候才更新結(jié)果
if(total == target){
len = Math.max(len,right - left + 1);
}
}
return len == -1 ? -1 : n - len;
}
}
水果成藍(lán)

題目解析:就是給我們一個(gè)fruits數(shù)組,里面又好多種類,讓我們用兩個(gè)籃子放水果,并且每個(gè)籃子只能放一種水果,并且不可以跳著摘水果,遇到第三種水果直接停止采摘,獲取其中最多摘多少水果
問(wèn)題可以轉(zhuǎn)換為:找出一個(gè)最長(zhǎng)的子數(shù)組,并且里面不超過(guò)兩種水果
算法原理:暴力解法+hash,可以使用雙重for循環(huán)進(jìn)行遍歷,left和right這兩個(gè)變量,但是其中有一些多余的操作,例如當(dāng)水果的種類大于2時(shí),我們此時(shí)采用的是,讓left++,right回到left的位置繼續(xù)進(jìn)行遍歷,但是我們可以發(fā)現(xiàn),原本[left,right]這個(gè)區(qū)間種類>2,讓其回去,此時(shí)讓left++,其中的中類要么不變,要么減小,肯定不會(huì)增加
滑動(dòng)窗口+hash
先不斷的將right下標(biāo)放入hash中
當(dāng)種類>2的時(shí)候就要出窗口left下標(biāo)對(duì)應(yīng)的水果數(shù)量–,當(dāng)其是0的時(shí)候說(shuō)明其種類減少,將其從hash中刪除,left++
更新結(jié)果
len = Math.max(len,right - left+1)



class Solution {
public int totalFruit(int[] fruits) {
//此時(shí)我們使用一個(gè)數(shù)組來(lái)實(shí)現(xiàn)hash
int n = fruits.length;
int[] hash = new int[n+1];
int len = 0;//最長(zhǎng)的長(zhǎng)度
for(int left = 0,right = 0,kinds = 0;right < n ;right++){
//入窗口
if(hash[fruits[right]] == 0){
kinds++;//如果這個(gè)窗口沒(méi)有添加過(guò)這個(gè)元素,此時(shí)種類增多
}
hash[fruits[right]]++;
//當(dāng)kinds>2進(jìn)行出窗口
while(kinds > 2){
//出窗口
hash[fruits[left]]--;
//此時(shí)如果這個(gè)連續(xù)的區(qū)間沒(méi)有了這個(gè)元素,此時(shí)的種類就減小
if(hash[fruits[left]] ==0){
kinds--;
}
left++;
}
//更新結(jié)果
len = Math.max(len,right - left + 1);
}
return len;
}
}
時(shí)間復(fù)雜度:O(N)
空間復(fù)雜度:O(N)
找到字符串中所有字母異位詞

字母異位詞是通過(guò)重新排列不同單詞或短語(yǔ)的字母而形成的單詞或短語(yǔ),并使用所有原字母一次。
題目解析:就是給我們s和p兩個(gè)字符串,讓我們?cè)賡中找到所有p的異位詞子串,并將其索引,最后返回其中所有的索引
算法原理:首先我們想到的是暴力解法+hash,一直遍歷s串中所有和p長(zhǎng)度相同的子串,并且要同過(guò)hash比較其中是否相同,相同就將其下標(biāo)放入一個(gè)集合中,但是仍有一些多余的部分
滑動(dòng)窗口+hash:left = 0,right = 0
使用left和right來(lái)確定窗口,并且此時(shí)的窗口長(zhǎng)度是一定的,不斷讓right向后走,left也想后走,right并不需要回頭,因?yàn)槲覀円M(jìn)入下一個(gè)窗口僅需將left下標(biāo)對(duì)應(yīng)的字符出hash,將right+1下標(biāo)入hash就行,此時(shí)就進(jìn)入下一個(gè)窗口,讓后進(jìn)行判斷是否相同就行



class Solution {
public List<Integer> findAnagrams(String ss, String pp) {
List<Integer> ret = new ArrayList<>();
char[] s = ss.toCharArray();
char[] p = pp.toCharArray();
//使用兩個(gè)數(shù)組來(lái)模擬hash
int[] hash1 = new int[26];//用來(lái)放pp字符串的所有字符信息
for(char ch : p){
hash1[ch - 'a']++;
}
int[] hash2 = new int[26];//此時(shí)的用來(lái)放窗口中每個(gè)字符出現(xiàn)的次數(shù)
int n = pp.length();
//此時(shí)的count是用來(lái)統(tǒng)計(jì)有效字符個(gè)數(shù)
for(int left = 0,right = 0,count = 0;right<ss.length();right++){
char in = s[right];
if(++hash2[in - 'a'] <= hash1[in - 'a']){//入窗口
count++;
}
//判斷
if(right - left + 1 > n){
//出窗口
char out = s[left++];
if(hash2[out - 'a']-- <= hash1[out - 'a']){
count--;
}
}
//更新結(jié)果
if(count == n){
ret.add(left);
}
}
return ret;
}
}
時(shí)間復(fù)雜度:O(N+M) ,兩個(gè)字符串串長(zhǎng)度
空間復(fù)雜度:O(N) , s字符串長(zhǎng)度
串聯(lián)所有單詞的子串

題目解析:就是在一個(gè)s字符串中找出包含words中所有單詞,并返回所有下標(biāo),
算法原理:此題目和上一題:找出所有字母的異位詞是一樣的意思,只不過(guò)我們此時(shí)這里的字母變成了單詞,因此這個(gè)題目和上一個(gè)原理一樣,只不過(guò)此時(shí)要注意將其單詞看成一個(gè)整體,這樣就和上一個(gè)題目一樣了

class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> ret = new ArrayList<>();
//使用map來(lái)存放words
Map<String,Integer> hash1 = new HashMap<>();
for(String word : words){
hash1.put(word,hash1.getOrDefault(word,0)+1);
}
int n = words[0].length();//單詞長(zhǎng)度
int m = words.length;//有多少單詞
//此時(shí)要循環(huán)n次滑動(dòng)窗口,因?yàn)槲覀儗⒚總€(gè)單詞這些字符看成了一個(gè)整體放入hash
for(int i = 0;i < n;i++){
//使用count來(lái)統(tǒng)計(jì)有效單詞個(gè)數(shù)
Map<String,Integer> hash2 = new HashMap<>();
for(int left = i,right = i,count = 0;right <= s.length() - n;right += n){
String in = s.substring(right,right+n);
hash2.put(in,hash2.getOrDefault(in,0)+1);
if(hash2.get(in) <= hash1.getOrDefault(in,0)){
count++;
}
//出窗口
if(right - left + 1 > m*n){
String out = s.substring(left,left+n);
if(hash2.get(out) <= hash1.getOrDefault(out,0)){
count--;
}
hash2.put(out,hash2.get(out) - 1);
if(hash2.get(out) == 0){
hash2.remove(out);
}
left += n;
}
if(count == m){
ret.add(left);
}
}
}
return ret;
}
}
時(shí)間復(fù)雜度:O( m * n)
空間復(fù)雜度:O( n )
n是words數(shù)組長(zhǎng)度,m是s的長(zhǎng)度
最小覆蓋子串

題目解析:在s這個(gè)字符串中找到一個(gè)最下子串,并且其中每個(gè)字母數(shù)量要不小于t中的
算法原理:
暴力解法:雙重for循環(huán)+hash,雙重for循環(huán)遍歷所有情況,用hash存放,并有方法來(lái)判斷其是否符合條件
滑動(dòng)窗口+hash:left = 0 , right = 0;暴力解法中,我們遇到滿足情況進(jìn)行更新結(jié)果時(shí)候,其實(shí)并不需要將其right從新返回left,重新放入hash中,我們只需要將left下標(biāo)對(duì)應(yīng)字符出去就行,因?yàn)榇藭r(shí)只會(huì)出現(xiàn)兩種情況1.還滿足條件right不動(dòng)2.不滿足條件 right繼續(xù)右移


class Solution {
public String minWindow(String ss, String tt) {
char[] s = ss.toCharArray();
char[] t = tt.toCharArray();
//使用數(shù)組來(lái)模擬hash
int[] hash1 = new int[128];
int kinds = 0;//記錄tt中出現(xiàn)字符的種類
for (char ch : t) {
if (hash1[ch]++ == 0) {
kinds++;
}
}
int minlen = Integer.MAX_VALUE;
int begin = -1;//記錄其起始位置和長(zhǎng)度即可
int[] hash2 = new int[128];//記錄窗口中字符
for (int left = 0, right = 0, count = 0; right < ss.length(); right++) {
char in = s[right];
//當(dāng)這個(gè)字符的數(shù)量和hash1相同時(shí)候,count++
if (++hash2[in] == hash1[in]) {
count++;
}
while (kinds == count) {
//更新結(jié)果
if (right - left + 1 < minlen) {
begin = left;
minlen = right - left + 1;
}
char out = s[left++];
if (hash2[out]-- == hash1[out]) {
count--;
}
}
}
return begin == -1 ? new String() : ss.substring(begin, begin + minlen);
}
}
時(shí)間復(fù)雜度:O(m + n)
空間復(fù)雜度:O(1)
點(diǎn)名

題目解析:就是一個(gè)遞增的數(shù)組,并且其下標(biāo)和其對(duì)應(yīng)的值是對(duì)應(yīng)的,
到此這篇關(guān)于Java滑動(dòng)窗口算法題目練習(xí)的文章就介紹到這了,更多相關(guān)Java滑動(dòng)窗口算法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring?Cloud?Gateway集成Sentinel流控詳情
這篇文章主要介紹了Spring?Cloud?Gateway集成Sentinel流控詳情,Sentinel支持對(duì)Spring?Cloud?Gateway、Zuul等主流的API?Gateway進(jìn)行限流,需要的朋友可以參考一下2022-09-09
Java 8對(duì)LinkedHashSet元素進(jìn)行排序的操作方法
LinkedHashSet 是 Java 集合框架中的一個(gè)類,它繼承自 HashSet,并實(shí)現(xiàn)了 Set 接口,然而,LinkedHashSet 不支持元素的排序,它僅僅保持插入順序,所以本文給大家介紹了Java 8 如何對(duì) LinkedHashSet 元素進(jìn)行排序,需要的朋友可以參考下2024-11-11
MAC上IntelliJ IDEA的svn無(wú)法保存密碼解決方案
今天小編就為大家分享一篇關(guān)于MAC上IntelliJ IDEA的svn無(wú)法保存密碼解決方案,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-10-10
java javax.annotation.Resource注解的詳解
這篇文章主要介紹了javax.annotation.Resource注解的詳解的相關(guān)資料,需要的朋友可以參考下2016-10-10
深入了解Java核心類庫(kù)--BigDecimal和System類
這篇文章主要為大家詳細(xì)介紹了javaBigDecimal和System類定義與使用的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能給你帶來(lái)幫助2021-07-07
SpringBoot集成redis與session實(shí)現(xiàn)分布式單點(diǎn)登錄
這篇文章主要介紹了SpringBoot集成redis與session實(shí)現(xiàn)分布式單點(diǎn)登錄,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09
基于controller使用map接收參數(shù)的注意事項(xiàng)
這篇文章主要介紹了基于controller使用map接收參數(shù)的注意事項(xiàng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
SpringBoot去除內(nèi)嵌tomcat的實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot去除內(nèi)嵌tomcat的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09


