數據結構算法 | 單調棧

文章目錄

  • 算法概述
  • 題目
    • 下一個更大的元素 I
      • 思路
      • 代碼
    • 下一個更大元素 II
      • 思路
      • 代碼
    • 132 模式
      • 思路
      • 代碼
    • 接雨水
      • 思路


算法概述

當題目出現 「找到最近一個比其大的元素」 的字眼時,自然會想到 「單調棧」 。——三葉姐

單調棧以嚴格遞增or遞減的規則將無序的數列進行選擇性排序。


題目

下一個更大的元素 I

給你兩個 沒有重復元素 的數組 nums1nums2 ,其中 nums1nums2 的子集。(題源力扣)

請你找出 nums1 中每個元素在 nums2 中的下一個比其大的值。

nums1 中數字 x 的下一個更大元素是指 xnums2 中對應位置的右邊的第一個比 x 大的元素。如果不存在,對應位置輸出 -1

示例:

輸入: nums1 = [4,1,2], nums2 = [1,3,4,2].
輸出: [-1,3,-1]
解釋:

  • 對于 num1 中的數字 4 ,你無法在第二個數組中找到下一個更大的數字,因此輸出 -1 。
  • 對于 num1 中的數字 1 ,第二個數組中數字 1 右邊的下一個較大數字是 3 。
  • 對于 num1 中的數字 2 ,第二個數組中沒有下一個更大的數字,因此輸出 -1 。

思路

  1. 倒序遍歷 nums2stack 暫存嚴格遞增的數據,即,若 棧不為空當前遍歷到的元素大于棧頂元素 則彈出棧頂元素。
  2. unordred_map 存儲 遍歷到的元素(key最近一個比其大的元素(value 之間的關系。經過步驟一后,若棧不為空,則棧頂元素即為 value ,否則,value-1
  3. 正序遍歷 nums1 并依賴 unordred_map 得到題目要求的結果數組。

代碼

class Solution {
public:vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {unordered_map<int, int> m;stack<int> st;for(int i = nums2.size()-1; i >= 0; i--){int t = nums2[i];while(st.size() && t>st.top()) st.pop();m[t] = st.empty() ? -1 : st.top();st.push(t);}vector<int> v;for(int i = 0; i < nums1.size(); i++){v.push_back(m[nums1[i]]);}return v;}
};

下一個更大元素 II

給定一個循環數組,輸出每個元素的下一個更大元素。數字 x 的下一個更大的元素是按數組遍歷順序,這個數字之后的第一個比它更大的數。如果不存在,則輸出 -1。(題源力扣)

示例:

輸入: [1,2,1]
輸出: [2,-1,2]
解釋:

  1. 第一個 1 的下一個更大的數是 2;
  2. 數字 2 找不到下一個更大的數;
  3. 第二個 1 的下一個最大的數需要循環搜索,結果也是 2。

思路

因為我們要循環搜索,因此要么將數組拉直——復制所有元素并添加在數組尾部;要么假設數組長度為真正長度的二倍,并且遍歷的時候對長度取余,這里我們選擇后一種方法。并將遍歷分成正序遍歷和逆序遍歷兩種方法:

正序遍歷:

  • 單調棧 st 用以記錄下標,正序遍歷給定的數組 nums,范圍為 [0, nums.size()-1] ,當前遍歷到的下標值為 i ,結果數組為 v ,結果數組數值全部被初始化為 -1
  • 棧不為空棧頂下標對應的元素 小于 當前遍歷的元素
    1. v[棧頂下標] = nums[i%n]棧頂下標對應的元素下一個最大元素 即為 當前遍歷到的元素
    2. st.pop() ,彈出棧頂元素,單調棧遵循嚴格遞增規則。
  • 當前遍歷到的元素其下標入棧,st.push(i%n)。由于每個元素都會被遍歷到并且被壓入棧中,而棧中每個元素都會被處理(沒有下一個最大元素的棧中元素直接被彈出,反之則匹配下一個最大元素)。

逆序遍歷:

  • 單調棧 st 用以記錄元素值,逆序遍歷給定的數組 nums,范圍為 [nums.size()-1, 0] ,當前遍歷到的下標值為 i ,結果數組為 v ,結果數組數值全部被初始化為 -1
  • 棧不為空棧頂元素 小于等于 當前遍歷的元素
    1. st.pop() ,彈出棧頂元素,單調棧遵循嚴格遞增規則。
  • i < nums.size() 時,開始逆序更新結果數組的元素,在此之前對單調棧進行的更新只為解決 nums 最后一次降峰的元素(下例中的 5,4 )單次遍歷無法匹配下一個更大元素的問題。以 2,3,6,4,5,4 為例:
    1. 一次遍歷只能得到結果數組 3,6,-1,5,-1,-1。對于 5,4 兩個元素而言,無法正確匹配下一個更大元素,這也是和上一題的主要區別之所在。
    2. 因此倘若我們將整個遍歷過程看作對 nums 的兩次遍歷,就會發現,第一次遍歷結果是單調棧中只有 6,也就是解決了無法正確匹配5,4 兩個元素的下一個更大元素,而第二次遍歷則是對最后一次降峰之前所有元素進行匹配下一個最大元素的操作。

注:上面提到了降峰,這其實是對數組常見的稱呼方法,意為將數組元素以折線圖的形式表示看起來像一座座山峰,對于 2,3,6,4,5,4 而言,它們是形如下圖的兩座山峰:
在這里插入圖片描述


代碼

正序遍歷:

class Solution {
public:vector<int> nextGreaterElements(vector<int>& nums) {int n = nums.size();vector<int> v(n, -1);stack<int> st;for(int i = 0; i < n*2; i++){int t = nums[i%n];while(st.size() && nums[st.top()]<t){v[st.top()] = t;st.pop();}st.push(i%n);//cout << nums[i%n] << " " << m[nums[i%n]] << endl;}return v;}
};

逆序遍歷:

class Solution {
public:vector<int> nextGreaterElements(vector<int>& nums) {int n = nums.size();vector<int> v(n, -1);stack<int> st;for(int i = n*2-1; i >= 0; i--){int t = nums[i%n];while(st.size() && st.top()<=t) st.pop();if(i <= n-1) v[i] = st.empty() ? -1 : st.top();st.push(t);//cout << nums[i%n] << " " << m[nums[i%n]] << endl;}return v;}
};

132 模式

給你一個整數數組 nums ,數組中共有 n 個整數。132模式 的子序列 由三個整數 nums[i]nums[j]nums[k] 組成,并同時滿足:i < j < knums[i] < nums[k] < nums[j] 。(題源力扣)

如果 nums 中存在 132模式 的子序列 ,返回 true ;否則,返回 false

示例:

輸入: nums = [1,2,3,4]
輸出: false
解釋: 序列中不存在 132 模式的子序列。


思路

  • 單調棧 st 用以暫存可能為 132模式2 的數據,遵循嚴格遞減規則,變量 sec 為真正的 2,將其初始化為 INT_MIN
  • 倒序遍歷數組,因為我們目的在于枚舉 2 ,而 2 必定從數組尾部優先出現,當前遍歷到的下標為 i
    1. nums[i] < sec ,確定了 1,返回 true
    2. 棧不為空當前遍歷到的元素 大于 棧頂元素 ,當前元素暫定為 3 ,更新 sec 代表的 2
    3. 優化:當前遍歷到的元素 大于 sec ,當前元素入棧。本條規則的依據是:如果 nums[i] ≤ sec,那么即使它在未來被彈出,也不會將 sec 更新為更大的值。
      • 為什么 sec 更新為更大的值 如此重要呢?因為 2 的值越大,那么我們之后找到 1 的機會也就越大。

代碼

class Solution {
public:bool find132pattern(vector<int>& nums) {int n = nums.size();stack<int> st; // 單調棧維護2st.push(nums[n-1]);int sec = INT_MIN; // second表示真正的2for(int i = n-2; i >= 0; i--){int t = nums[i];if(t < sec) return true; // 當前t作為1while(st.size() && t>st.top()){sec = st.top();st.pop();}if(t>sec) st.push(t); // 優化}return false;}
};

接雨水

給定 n 個非負整數表示每個寬度為 1 的柱子的高度圖,計算按此排列的柱子,下雨之后能接多少雨水。

示例:
在這里插入圖片描述

輸入: height = [0,1,0,2,1,0,1,3,2,1,2,1]
輸出: 6
解釋: 上面是由數組 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度圖,在這種情況下,可以接 6 個單位的雨水(藍色部分表示雨水)。


思路

單調棧 st 遵循嚴格遞減規則,暫存可以作為 接水形狀左邊界的下標。用變量 res 保存能接多少雨水,正序遍歷數組 height

  • 棧不為空當前遍歷到的元素 大于 棧頂下標對應的元素,表明有可能出現可接雨水的 “容器” 。
    1. 將棧頂下標對應元素作為容器底部 bot,然后彈出,將新的棧頂下標作為容器左邊界 left
    2. 容器能容納的雨水為 長 * 高 = i - left - 1 * (min(height[left], height[i]) - bot) ,解釋一下,長度為右邊界 i 和 左邊界 left 的 差值減一,高度為 左邊界高度和右邊界高度中較小值 與 容器底部 bot 的差值,畢竟木桶接水的多少取決于最短的一塊木板。
  • 其余情況則直接將 當前遍歷到的元素下標 壓入棧中,此時單調棧 st 遵循嚴格遞減規則。
class Solution {
public:int trap(vector<int>& height) {int n = height.size();stack<int> st;int res = 0;for(int i = 0; i < n; i++){int t = height[i];while(st.size() && height[st.top()]<t){int bot = height[st.top()];//cout << "left: " << left << endl;st.pop();if(st.size()){int left = st.top();res += (i-left-1)*(min(t, height[left])-bot);}}st.push(i);}return res;}
};

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

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

相關文章

最長下降子序列

文章目錄題目解法DP暴搜思路代碼實現貪心二分思路代碼實現題目 給出一組數據 nums&#xff0c;求出其最長下降子序列&#xff08;子序列允許不連續&#xff09;的長度。&#xff08;類似于lc的最長遞增子序列&#xff09; 示例&#xff1a; 輸入&#xff1a; 6 // 數組元素個…

Linux 服務器程序規范、服務器日志、用戶、進程間的關系

文章目錄服務器程序規范日志rsyslogd 守護進程syslog函數openlog函數setlogmask函數closelog函數用戶進程間的關系進程組會話系統資源限制改變工作目錄和根目錄服務器程序后臺化服務器程序規范 Linux 服務器程序一般以后臺進程&#xff08;守護進程[daemon]&#xff09;形式運…

IO模型 :阻塞IO、非阻塞IO、信號驅動IO、異步IO、多路復用IO

文章目錄IO模型阻塞IO非阻塞IO信號驅動IO多路復用IO異步IOIO模型 根據各自的特性不同&#xff0c;IO模型被分為阻塞IO、非阻塞IO、信號驅動IO、異步IO、多路復用IO五類。 最主要的兩個區別就是阻塞與非阻塞&#xff0c;同步與異步。 阻塞與非阻塞 阻塞與非阻塞最主要的區別就…

Tomcat服務器集群與負載均衡實現

一、前言 在單一的服務器上執行WEB應用程序有一些重大的問題&#xff0c;當網站成功建成并開始接受大量請求時&#xff0c;單一服務器終究無法滿足需要處理的負荷量&#xff0c;所以就有點顯得有點力不從心了。另外一個常見的問題是會產生單點故障&#xff0c;如果該服務器壞掉…

Linux服務器 | 事件處理模式:Reactor模式、Proactor模式

文章目錄Reactor模式Proactor模式同步I/O模型模擬Proactor模式兩者的優缺點ReactorProactor同步I/O模型通常用于實現 Reactor 模式&#xff0c;異步I/O模型通常用于實現 Proactor 模式。&#xff08;不是絕對的&#xff0c;同步I/O也可模擬出 Proactor 模式&#xff09; React…

Linux服務器 | 服務器模型與三個模塊、兩種并發模式:半同步/半異步、領導者/追隨者

文章目錄兩種服務器模型及三個模塊C/S模型P2P模型I/O處理單元、邏輯單元、存儲單元并發同步與異步半同步/半異步模式變體&#xff1a;半同步/半反應堆模式改進&#xff1a;高效的半同步/半異步模式領導者/追隨者模式組件 &#xff1a;句柄集、線程集、事件處理器工作流程兩種服…

香農信息熵之可憐的小豬

文章目錄題目解析香農熵公式樣例具體分析代碼題目 有 n 桶液體&#xff0c;其其中 正好 有一桶含有毒藥&#xff0c;其裝的都是水。它們從外觀看起來都一樣。為了弄清楚哪只水桶含有毒藥&#xff0c;你可以喂一些豬喝&#xff0c;通過觀察豬是否會死進行判斷&#xff0c;實驗對…

字符串匹配之KMP(KnuthMorrisPratt)算法(圖解)

文章目錄最長相等前后綴next數組概念代碼實現圖解GetNext中的回溯改進代碼實現代碼復雜度分析最長相等前后綴 給出一個字符串 ababa 前綴集合&#xff1a;{a, ab, aba, abab} 后綴集合&#xff1a;{a, ba, aba, baba} 相等前后綴 即上面用同樣顏色標識出來的集合元素&#…

linux下tomcat6.0與jdk安裝詳細步驟

安裝Tomcat6.0和JDK1.6 在linux系統上安裝tomcat和jdk應該說是我學習linux知識的第一課了&#xff0c;之前只 是聽說過&#xff0c;從沒接觸過&#xff0c;但我們公司項目都是部署在linux系統上的&#xff0c;那天上司突 然給我發了幾個文檔&#xff0c;讓我看一下&#xff…

Android入門(一) | Android Studio的配置與使用

文章目錄安裝配置Android Studio使用Android Studio模擬器更改Android SDK的路徑Hello World&#xff01;安裝配置Android Studio 從這一步開始&#xff1a; 一直點 next 即可&#xff0c;直到存儲路徑的選擇上&#xff0c;可以放到非 C 盤&#xff0c;這里我放到 D 盤了&am…

Android 入門(四) | Intent 實現 Activity 切換

文章目錄Intent顯式 Intent定義兩個 xml 文件android:orientationmatch_parent 和 wrap_contentIntent函數定義兩個 Activity隱式 Intent更多隱式 Intent 的用法用隱式 Intent 打開系統瀏覽器自建 Activity 以響應打開網頁的 Intent向下一個活動傳遞數據返回數據給上一個活動In…

Android入門(二) | 項目目錄及主要文件作用分析

文章目錄項目目錄分析app目錄分析AndroidManifest.xml 分析MainActivity.kt 分析build.gradle 分析最外層目錄下的 build.gradleapp 目錄下的 build.gradle項目目錄分析 我們來看一下 src/main/res 下的一些文件&#xff1a; .gradle 和 .idea &#xff1a;這兩個目錄下放置…

Android入門(三) | Android 的日志工具 Logcat

文章目錄日志工具類 android.util.LogLogcat 中的過濾器日志工具類 android.util.Log Log 從屬日志工具類 android.util.Log &#xff0c;該類提供了五個方法供我們打印日志&#xff1a; Log.v() &#xff1a;用于打印那些最為瑣碎的、意義最小的日志信息。對應級別 verbose&…

Android 客戶端與服務器交互方式

突然想到一個問題就是Android客戶端與服務器交互有幾種方式&#xff0c;因為在腦袋里想當然的就是webservices和json。要在Android手機客戶端與pc服務器交互&#xff0c;需要滿足下面幾種條件&#xff1a;跨平臺、傳輸數據格式標準、交互方便...。 為了與服務器通訊其實無非就…

Android入門(五) | Activity 的生命周期

文章目錄Activity 的狀態及生命周期實現管理生命周期FirstActivitySecondActivityDialogActivity運行結果舊活動被回收了還能返回嗎&#xff1f;Activity 的狀態及生命周期 Android 的應用程序運用 棧&#xff08;Back Stack&#xff09; 的思想來管理 Activity&#xff1a; …

Android入門(六) | Activity 的啟動模式 及 生產環境中關于 Activity 的小技巧

文章目錄Activity 的啟動模式standardsingleTopsingleTasksingleInstance技巧了解當前界面是哪個 Activity隨時隨地退出程序啟動活動的最佳寫法Activity 的啟動模式 standard&#xff1a;默認的啟動方式&#xff0c;每次啟動一個活動都會重新創建singleTop&#xff1a;如果該活…

Android入門(七) | 常用控件

文章目錄TextView 控件&#xff1a;文本信息Button 控件&#xff1a;按鈕EditText 控件&#xff1a;輸入框ImageView 控件&#xff1a;圖片ProgressBar 控件&#xff1a;進度條AlertDialog 控件&#xff1a;提示框ProgressDialog 控件&#xff1a;帶有進度條的提示框TextView 控…

Android入門(八) | 常用的界面布局 及 自定義控件

文章目錄LinearLayout &#xff1a;線性布局android:layout_gravity &#xff1a;控件的對齊方式android:layout_weight&#xff1a;權重RelativeLayout &#xff1a;相對布局相對于父布局進行定位相對于控件進行定位邊緣對齊FrameLayout &#xff1a;幀布局Percent &#xff1…

Android入門(九)| 滾動控件 ListView 與 RecyclerView

文章目錄ListView內置類型的簡單運用定制數據類型提升效率點擊事件RecyclerView布局管理器點擊事件ListView 內置類型的簡單運用 由于手機屏幕空間有限&#xff0c;能夠一次性在屏幕上顯示的內容不多&#xff0c;當我們的程序有大量數據需要顯示的時候就可以借助 ListView 來…

關于“三門問題”的一些想法

三門問題&#xff08;Monty Hall problem&#xff09;亦稱為蒙提霍爾問題、蒙特霍問題或蒙提霍爾悖論&#xff0c;大致出自美國的電視游戲節目Let’s Make a Deal。問題名字來自該節目的主持人蒙提霍爾&#xff08;Monty Hall&#xff09;。參賽者會看見三扇關閉了的門&#xf…