算法刷題筆記 滑動窗口(C++實現,非常詳細)

文章目錄

    • 題目描述
    • 基本思路
    • 實現代碼

題目描述

  • 給定一個大小為n ≤ 10^6的數組。
  • 有一個大小為k的滑動窗口,它從數組的最左邊移動到最右邊。
  • 你只能在窗口中看到k個數字。
  • 每次滑動窗口向右移動一個位置。以下是一個例子:
    • 該數組為 [1 3 -1 -3 5 3 6 7],k為3
      在這里插入圖片描述
  • 你的任務是確定滑動窗口位于每個位置時,窗口中的最大值和最小值。

輸入格式

  • 輸入包含兩行。
  • 第一行包含兩個整數nk,分別代表數組長度和滑動窗口的長度。
  • 第二行有n個整數,代表數組的具體數值。同行數據之間用空格隔開。

輸出格式

  • 輸出包含兩個。
  • 第一行輸出,從左至右,每個位置滑動窗口中的最小值。
  • 第二行輸出,從左至右,每個位置滑動窗口中的最大值。

基本思路

這道題是單調隊列系列題目的最基礎的模板題,但是對于像我這樣的初學者來說仍然難度較大,因此我將對該題的思路進行詳細解析。

  • 算法題的核心是將一個有直觀、暴力的思路的算法優化為一個邏輯上更加復雜,但是時間和空間上更占優勢的算法。那么,對于這道題,我們就需要首先思考蠻力算法的求解步驟是什么。本題的蠻力算法非常直觀,就是一個簡單的雙重遍歷。例如,對于數組[1 3 -1 -3],并假設窗口大小為3,那么我們就從數組的第一個元素開始遍歷,向右滑動窗口,每次遍歷到的數組元素作為窗口的左端點。以這個例子為例,可以得到兩個窗口[1 3 -1][3 -1 -3],再分別從這兩個窗口中找出最大值和最小值即可。代碼實現上,可以大致如下:
#include <cstdio>
#include <vector>
using namespace std;// 本例子中n為4,k為3,假設原始數組為arr,本例子以最大值為例
for(int i = 0; i < n - k + 1; ++ i)
{// 使用一個向量表示當前窗口,并向該窗口中添加元素vector<int> window;for(int j = i; i < i + k; ++ j) window.push_back(arr[j]);// 查找該向量中的最大值并輸出int max = window[0];for(int j = 1; j < window.size(); ++ j) if(window[j] > max) max = window[j];printf("%d ", max);
}
  • 但是,我們仔細考慮一下,這種方法存在明顯的冗余性。兩個相鄰的滑動窗口之間,有且只有一個元素不相同,而窗口中的其他元素都是完全一樣的,因此會經過多輪重復遍歷,創建多個大部分元素都相同的向量,算法的時間復雜度為O(nk)。既然存在冗余元素,那么我們就需要從數據結構和算法的角度上考慮對算法進行優化。
  • 直觀上,我們可以發現既然相鄰的兩個窗口只有一個元素存在區別,即相當于下一個窗口的元素是去除了上一個窗口中的首元素,并且在后面添加了一個新元素,這就很類似于數據結構中常用的隊列數據結構。因此,如果能夠使用隊列來代替向量,那么就可以提高算法的效率。實現代碼如下:
#include <cstdio>
#include <deque>
using namespace std;// 首先創建一個隊列,并以第一個窗口中的元素進行初始化
deque<int> window;
for(int i = 0; i < k; ++ i) window.push_back(arr[i]);
// 每輪遍歷隊首元素出棧,并從隊尾入隊一個元素
for(int i = k; i < n - k + 1; ++ i)
{window.pop_front();window.push_back(arr[i]);// 查找當前隊列中的最大值int max = window.front();for(int item : window) if(item > max) max = item;printf("%d ", max);
}
  • 基于隊列的實現代碼的確能夠有更高的時間效率,但是是否可以進一步優化呢?我們發現,盡管使用隊列可以更加方便地創建和維護一個窗口,而不像向量那樣需要每次完全重新新建一個,但是在查找最大值時,仍然需要遍歷整個隊列。如果我們想要繼續提高效率,就必須簡化查找過程,避免耗時的循環遍歷,此時就應該使用單調隊列進行處理。

  • 仍然以[1 3 -1 -3]為例,當我們每一輪循環更新隊列時,我們可以修改我們的更新策略。下面進行舉例說明,以查找最大值為例。

    • 第一輪:隊列初始為空,因此直接將第一個元素1放入隊尾即可。
    • 第二輪:隊列目前為[1],當前遍歷到的元素為3;由于3大于當前的隊尾元素1,因此如果3也放入隊尾后,在查找最大值的過程中,元素1一定不會成為任何一個窗口的最大值了。這是因為當元素1和元素3在同一個窗口中時,31大,因此最大值不可能是1;當元素1和元素3不在同一個窗口中時,只有一種可能,就是當前窗口中已經不包含1了,這是因為3在原始數組中排在1的后面,只要1在窗口中,3一定在窗口中,所以這種情況下,窗口中已經不包含有元素1,所以自然不會成為最大值。因此,可以認為隊列中的1為冗余元素,可以直接將其出隊。只有某個元素可能成為某個窗口的最大值時,才會被放入隊尾進入隊列中,而所有確定下來的冗余元素都出隊。所以,在第二輪迭代中首先通過上述比較過程,讓隊尾的1出隊,此時隊列為空,則直接把當前元素3放入隊列中。
    • 第三輪:隊列目前為[3],當前遍歷到的元素是-1。由于隊尾元素3-1更大,因此直接將-1入隊放入隊尾即可,這是因為3會在-1之前從隊頭離開隊列,此時-1就有可能成為某個窗口的最大值元素。
    • 第四輪:和第三輪類似,隊列目前是[3 -1],當前遍歷到的元素是-3。由于隊尾元素-1-3更大,因此直接將-3放入隊尾。
  • 那么,應該如何確定何時要將隊頭元素出隊呢?隊頭元素出隊表示該元素已經不在當前的窗口中,最簡單的處理方法就是用另一個隊列記錄所有隊列中的元素在數組中的下標,并在每一輪的遍歷過程中,通過下標判定隊首元素是否在窗口中即可。下標隊列和元素隊列中的元素應該是一一對應的,需要同時添加和同時刪除。

實現代碼

#include <cstdio>
#include <deque>
using namespace std;const int N = 1e6 + 10;
int arr[N];int main(void)
{int n, k;scanf("%d%d", &n, &k);for(int i = 0; i < n; ++ i) scanf("%d", &arr[i]);deque<int> q;deque<int> index;// 最小值部分for(int i = 0; i < n; ++ i){while(!q.empty() && q.back() >= arr[i]){q.pop_back();index.pop_back();}q.push_back(arr[i]);index.push_back(i);if(i >= k && i - k == index.front()){q.pop_front();index.pop_front();}if(i > k - 2) printf("%d ", q.front());}// 清空兩個隊列,對稱地求解最大值q.clear();index.clear();printf("\n");for(int i = 0; i < n; ++ i){while(!q.empty() && q.back() <= arr[i]){q.pop_back();index.pop_back();}q.push_back(arr[i]);index.push_back(i);if(i >= k && i - k == index.front()){q.pop_front();index.pop_front();}if(i > k - 2) printf("%d ", q.front());}return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/41260.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/41260.shtml
英文地址,請注明出處:http://en.pswp.cn/web/41260.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

用HttpURLConnection復現http響應碼405

目錄 使用GET方法&#xff0c;訪問GET接口&#xff0c;服務端返回405使用GET方法&#xff0c;訪問POST接口&#xff0c;服務端返回405使用POST方法&#xff0c;訪問GET接口&#xff0c;服務端返回405 使用GET方法&#xff0c;訪問GET接口&#xff0c;服務端返回405 發生場景&a…

Linux shell編程學習筆記63:free命令 獲取內存使用信息

0 前言 在系統安全檢查中&#xff0c;內存使用情況也是一塊可以關注的內容。Linux提供了多個獲取內存信息的命令很多。今天我們先研究free命令。 1 free命令的功能、用法和選項說明 1.1 free命令的功能 free 命令可以顯示系統內存的使用情況&#xff0c;包括物理內存、交換…

Java多語言跨境電商外貿商城源碼 tiktok商城系統源碼 跨境電商源碼

Java多語言跨境電商外貿商城源碼 tiktok商城系統源碼 跨境電商源碼 技術棧 PC端使用&#xff1a;vueelementui 用戶端使用&#xff1a;uniapp 管理端使用&#xff1a;vueelementui 后臺服務使用&#xff1a;springbootmybatisplusmysql 功能描述&#xff1a; 對接PayPal…

【面試題】字節一面面試題

自我介紹&#xff0c;項目介紹MQ的使用場景&#xff0c;不同的MQ之前的區別&#xff0c;為什么使用公司的MQ數據庫怎么部署的&#xff08;應該是問節點&#xff0c;庫表&#xff09;事務隔離級別innodb為什么選可重復讀作為隔離級別數據庫三大日志&#xff0c;保存先后順序undo…

vue3+electron項目搭建,遇到的坑

我主要是寫后端,所以對前端的vue啊vue-cli只是知其然,不知其所以然 這樣也導致了我在開發前端時候遇到了很多的坑 第一個坑, vue2升級vue3始終升級不成功 第二個坑, vue add electron-builder一直卡進度,進度條走完就是不出提示succes 第一個坑的解決辦法: 按照網上說的升級v…

使用Java實現高性能的文件上傳下載服務

使用Java實現高性能的文件上傳下載服務 大家好&#xff0c;我是微賺淘客系統3.0的小編&#xff0c;也是冬天不穿秋褲&#xff0c;天冷也要風度的程序猿&#xff01; 1. 引言 在現代Web應用中&#xff0c;文件上傳和下載服務是非常常見的功能需求。如何實現高性能、可靠且安全…

Ubuntu 20.04下多版本CUDA的安裝與切換 超詳細教程

目錄 前言一、安裝 CUDA1.找到所需版本對應命令2.下載 .run 文件3.安裝 CUDA4.配置環境變量4.1 寫入環境變量4.2 軟連接 5.驗證安裝 二、安裝 cudnn1.下載 cudnn2.解壓文件3.替換文件4.驗證安裝 三、切換 CUDA 版本1.切換版本2.檢查版本 前言 當我們復現代碼時&#xff0c;總會…

深入分析SSL/TLS服務器的證書(C/C++代碼實現)

SSL&#xff08;Secure Sockets Layer&#xff09;和TLS&#xff08;Transport Layer Security&#xff09;是網絡安全領域的重要協議&#xff0c;它們在保護網絡通信中發揮著至關重要的作用。這些協議通過加密和身份驗證機制&#xff0c;確保數據在傳輸過程中的機密性和完整性…

建投數據與中再數科簽署戰略合作協議

近日&#xff0c;建投數據科技股份有限公司&#xff08;以下簡稱“建投數據”&#xff09;與中再保數字科技有限責任公司&#xff08;以下簡稱“中再數科”&#xff09;簽署戰略合作協議。雙方通過資源整合共享&#xff0c;實現優勢互補&#xff0c;共同探索產品及服務的跨領域…

初見:AntDB智能運維“三劍客“之ACC

前情回顧 在前兩個章節中&#xff0c;我們介紹了 AntDB 智能運維"三劍客"的 ADC 和 MTK。 初見&#xff1a;AntDB智能運維"三劍客"之ADC 初見&#xff1a;AntDB智能運維"三劍客"之MTK 本文將繼續介紹 AntDB 數據庫智能運維平臺 ACC。 AntDB 介紹…

如何設置PHP wkhtmltopdf

首先參考&#xff1a;Composer三步曲&#xff1a;安裝、使用、發布 在 php 路徑下&#xff0c;應能打開命令行輸入php -v能夠看到php版本信息。 然后執行以下三條&#xff1a; php -r "copy(https://install.phpcomposer.com/installer, composer-setup.php);"php…

minist數據集分類模型的訓練

minist數據集訓練 訓練方法&#xff1a;利用pytorch來實現minist數據集的分類模型訓練 訓練模型如下圖所示 模型代碼&#xff1a; import torch from torch import nn from torch.nn import Flattenclass Net(nn.Module):def __init__(self):super().__init__()self.module …

ChatGPT對話:Scratch編程中一個單詞,如balloon,每個字母行為一致,如何優化編程

【編者按】balloon 7個字母具有相同的行為&#xff0c;根據ChatGPT提供的方法&#xff0c;優化了代碼&#xff0c;方便代碼維護與復用。初學者可以使用7個字母精靈&#xff0c;復制代碼到不同精靈&#xff0c;也能完成這個功能&#xff0c;但不是優化方法&#xff0c;也沒有提高…

__builtin_constant_p 常量檢查函數

__builtin_constant_p 詳細介紹 功能&#xff1a;__builtin_constant_p 是 GCC (GNU Compiler Collection) 提供的一個內置函數&#xff0c;用于在編譯時檢測一個表達式是否是常量。它返回一個整型值&#xff1a; 如果表達式 exp 是編譯時常量&#xff0c;則返回 1。否則&…

【sklearn模型訓練全指南】深入理解機器學習模型的構建過程

標題&#xff1a;【sklearn模型訓練全指南】深入理解機器學習模型的構建過程 在機器學習中&#xff0c;模型訓練是一個核心過程&#xff0c;它涉及到從數據中學習并獲得預測能力。scikit-learn&#xff08;簡稱sklearn&#xff09;作為Python中一個廣泛使用的機器學習庫&#…

FairJob:促進在線廣告系統公平性研究

在人工智能&#xff08;AI&#xff09;與人類動態的交匯處&#xff0c;既存在機遇也存在挑戰&#xff0c;特別是在人工智能領域。盡管取得了進步&#xff0c;但根植于歷史不平等中的持續偏見仍然滲透在我們的數據驅動系統中&#xff0c;這些偏見不僅延續了不公平現象&#xff0…

Centos新手問題——yum無法下載軟件

起因&#xff1a;最近在學習centos7&#xff0c;在VM上成功安裝后&#xff0c;用Secure進行遠程登陸。然后準備下載一個C編譯器&#xff0c;看網絡上的教程&#xff0c;都是用yum來下載&#xff0c;于是我也輸入了命令&#xff1a; yum -y install gcc* 本以為會自動下載&…

使用Python繪制雷達圖

使用Python繪制雷達圖 雷達圖效果代碼 雷達圖 雷達圖&#xff0c;也稱為蛛網圖或星型圖&#xff0c;是一種二維圖表&#xff0c;用于顯示多變量數據。每個變量在一個從中心點向外輻射的軸上表示&#xff0c;軸的數量與變量的數量相同。雷達圖通常用于比較多個樣本的多維數據&a…

docker部署redis/mongodb/

一、redis 創建/root/redis/conf/redis.conf 全部執行命令如下 docker run -it -d --name redis -p 6379:6379 --net mynet --ip 172.18.0.9 -m 400m -v /root/redis/conf:/usr/local/etc/redis -e TXAsia/Shangehai redis redis-server /usr/local/etc/redis/redis.conf 部署…

C#——密封類詳情

密封類 密封類是密封方法的擴展&#xff0c;用于確保某個類不會被繼承。在C#中&#xff0c;你可以使用sealed關鍵字來聲明一個密封類。 public sealed class SealedClass {// 類成員定義 } 如果使用密封類繼承的話&#xff0c;程序會報錯&#xff01;&#xff01;&#xff0…