算法篇之單調棧

單調棧算法入門

單調棧是一種特殊的數據結構應用,它的核心在于維護一個棧,使得棧內元素保持單調遞增或者單調遞減的順序。這種數據結構在解決很多算法問題時非常有效,例如求數組中每個元素的下一個更大元素、每日溫度問題等。

一、單調棧的基本概念

單調棧有兩種類型:

  1. 單調遞增棧:棧中元素從棧底到棧頂是遞增的
  2. 單調遞減棧:棧中元素從棧底到棧頂是遞減的

兩種方法實現起來沒有太大區別,單調棧的核心在于維護一個元素單調增單調減的順序,一般實現都是將數組值或數組對應索引存到棧中,使得該數組元素或索引對應的元素在棧中保持單調性。以單調遞增舉例,一旦遇到比棧頂元素小的元素便彈出棧頂元素,然后將該元素壓入棧中。這是一般單調棧題目的最常用做法

二.單調棧的基本思路

  1. 確定單調性:根據問題需求決定使用遞增棧還是遞減棧
    • 找下一個更大元素 → 遞減棧
    • 找下一個更小元素 → 遞增棧
  2. 存儲內容
    • 可以存儲元素值
    • 也可以存儲元素索引(當需要計算距離或位置時)
  3. 遍歷方向
    • 可以從左到右遍歷
    • 也可以從右到左遍歷(根據問題需求)
  4. 邊界處理
    • 注意處理棧為空的情況
    • 注意處理遍歷完成后棧中剩余元素

三、單調棧的基本實現

以存儲數組元素為例,簡單看一下單調棧的實現

import java.util.Deque;
import java.util.LinkedList;public class MonotonicStack {// 單調遞增棧示例public static void increasingStack(int[] nums) {Deque<Integer> stack = new LinkedList<>();for (int num : nums) {// 當棧不為空且當前元素小于棧頂元素時,彈出棧頂元素while (!stack.isEmpty() && num < stack.peek()) {System.out.println(stack.pop() + " -> " + num);}stack.push(num);}// 處理棧中剩余元素(沒有下一個更小元素)while (!stack.isEmpty()) {System.out.println(stack.pop() + " -> -1");}}// 單調遞減棧示例public static void decreasingStack(int[] nums) {Deque<Integer> stack = new LinkedList<>();for (int num : nums) {// 當棧不為空且當前元素大于棧頂元素時,彈出棧頂元素while (!stack.isEmpty() && num > stack.peek()) {System.out.println(stack.pop() + " -> " + num);}stack.push(num);}// 處理棧中剩余元素(沒有下一個更大元素)while (!stack.isEmpty()) {System.out.println(stack.pop() + " -> -1");}}public static void main(String[] args) {int[] nums = {3, 1, 4, 2, 5};System.out.println("單調遞增棧結果:");increasingStack(nums);System.out.println("\n單調遞減棧結果:");decreasingStack(nums);}
}

四、單調棧的典型應用

1. 力扣496.下一個更大的元素I

題目描述:nums1 中數字 x下一個更大元素 是指 xnums2 中對應位置 右側第一個x 大的元素。

給你兩個 沒有重復元素 的數組 nums1nums2 ,下標從 0 開始計數,其中nums1nums2 的子集。

對于每個 0 <= i < nums1.length ,找出滿足 nums1[i] == nums2[j] 的下標 j ,并且在 nums2 確定 nums2[j]下一個更大元素 。如果不存在下一個更大元素,那么本次查詢的答案是 -1

返回一個長度為 nums1.length 的數組 ans 作為答案,滿足 ans[i] 是如上所述的 下一個更大元素

示例 1:

輸入:nums1 = [4,1,2], nums2 = [1,3,4,2].
輸出:[-1,3,-1]
解釋:nums1 中每個值的下一個更大元素如下所述:
- 4 ,用加粗斜體標識,nums2 = [1,3,4,2]。不存在下一個更大元素,所以答案是 -1 。
- 1 ,用加粗斜體標識,nums2 = [1,3,4,2]。下一個更大元素是 3 。
- 2 ,用加粗斜體標識,nums2 = [1,3,4,2]。不存在下一個更大元素,所以答案是 -1 。
class Solution {public int[] nextGreaterElement(int[] nums1, int[] nums2) {HashMap<Integer, Integer> hs = new HashMap<>();int[] res = new int[nums1.length];Deque<Integer> stack = new LinkedList<>();Arrays.fill(res, -1);for (int i = 0; i < nums1.length; i++) {hs.put(nums1[i], i);}stack.push(0);for (int i = 1; i < nums2.length; i++) {if (nums2[i] > nums2[stack.peek()]) {while (!stack.isEmpty() && nums2[i] > nums2[stack.peek()]) {if (hs.containsKey(nums2[stack.peek()])) {res[hs.get(nums2[stack.peek()])] = nums2[i];}stack.pop();}}stack.push(i);}return res;}
}

2. 力扣739.每日溫度

題目描述:給定一個整數數組 temperatures ,表示每天的溫度,返回一個數組 answer ,其中 answer[i] 是指對于第 i 天,下一個更高溫度出現在幾天后。如果氣溫在這之后都不會升高,請在該位置用 0 來代替。

示例 1:

輸入: temperatures = [73,74,75,71,69,72,76,73]
輸出: [1,1,4,2,1,1,0,0]
class Solution {public int[] dailyTemperatures(int[] temperatures) {int[] res = new int[temperatures.length];Deque<Integer> stack = new LinkedList<>();stack.push(0);for (int i = 1; i < temperatures.length; i++) {if (temperatures[i] > temperatures[stack.peek()]) {while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {res[stack.peek()] = i - stack.peek();stack.pop();}stack.push(i);} else {stack.push(i);}}return res;}
}

五、復雜度分析

  • 時間復雜度:O(n),每個元素最多入棧和出棧一次
  • 空間復雜度:O(n),最壞情況下所有元素都入棧

通過掌握單調棧算法,可以高效解決許多與元素大小比較相關的問題,同時應該理解,對于一般的單調棧題目,其實都可以用暴力解法求解,但是單調棧顯然在時間復雜度上更勝一籌。

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

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

相關文章

Kubernetes控制平面組件:調度器Scheduler(二)

云原生學習路線導航頁&#xff08;持續更新中&#xff09; kubernetes學習系列快捷鏈接 Kubernetes架構原則和對象設計&#xff08;一&#xff09;Kubernetes架構原則和對象設計&#xff08;二&#xff09;Kubernetes架構原則和對象設計&#xff08;三&#xff09;Kubernetes控…

【網絡】數據鏈路層知識梳理

全是通俗易懂的講解&#xff0c;如果你本節之前的知識都掌握清楚&#xff0c;那就速速來看我的筆記吧~ 自己寫自己的八股&#xff01;讓未來的自己看懂&#xff01; &#xff08;全文手敲&#xff0c;受益良多&#xff09; 數據鏈路層 我們來重新理解一下這個圖&#xff1a;…

機器學習(神經網絡基礎篇)——個人理解篇6(概念+代碼)

1 在聲明一個類中&#xff0c;構建一個屬于類的函數&#xff0c;前面為什要加上“self”&#xff1f; 就像下面這一串代碼&#xff1a; class TwoLayerNet:def __init__(self, input_size, hidden_size, output_size,weight_init_std0.01):# 初始化權重self.params {}self.p…

Cribl 對Windows-xml log 進行 -Removing filed-06

Removing Fields Description? The Eval Function can be used to add or remove fields. In this example we will remove the extracted fields while preserving _raw, _time,index,source, sourcetype. Steps - Adding an Eval Function

chili3d調試6 添加左側面板

注釋前 一個一個注釋看對應哪個窗口 無事發生 子方法不是顯示的窗口 注釋掉看看 沒了 注釋這個看看 零件頁面沒了 這個瀏覽器居然完全不用關的&#xff0c;刷新就重載了 注釋看看 無工具欄版本 sidebar&#xff1a; 往框框里面加入 div({ className: style.input }, user_…

Linux學習——了解和熟悉Linux系統的遠程終端登錄

Linux學習——了解和熟悉Linux系統的遠程終端登錄 一.配置Ubuntu系統的網絡和用戶 1、設置虛擬機網絡為橋接模式 打開VMWare&#xff0c;選擇編輯虛擬機設置&#xff0c;在網絡適配器設置中&#xff0c;選擇“橋接模式”&#xff0c;保存設置并啟動Ubuntu。 2、配置Ubuntu的…

【JAVA EE初階】多線程(1)

這樣的代碼&#xff0c;雖然也能打印hello thread&#xff0c;但是沒有創建新的線程&#xff0c;而是直接在main方法所在的主線程中執行了run的邏輯 start方法&#xff0c;是調用系統api&#xff0c;真正在操作系統內部創建一個線程。這個新的線程會以run作為入口方法&#xff…

javase 學習

一、Java 三大版本 javaSE 標準版 &#xff08;桌面程序&#xff1b; 控制臺開發&#xff09; javaME 嵌入式開發&#xff08;手機、小家電&#xff09;基本不用&#xff0c;已經淘汰了 javaEE E業級發開&#xff08;web端、 服務器開發&#xff09; 二、Jdk ,jre jvm 三…

【Linux】Linux 操作系統 - 05 , 軟件包管理器和 vim 編輯器的使用 !

文章目錄 前言一、軟件包管理器1 . 軟件安裝2 . 包管理器3 . Linux 生態 二、軟件安裝 、卸載三、vim 的使用1 . 什么是 vim ?2 . vim 多模式3 . 命令模式 - 命令4 . 底行模式 - 命令5. 插入模式6 . 替換模式7 . V-BLOCK 模式8 . 技巧補充 總結 前言 本篇筆者將會對軟件包管理…

python基礎知識點(1)

python語句 一行寫一條語句 一行內寫多行語句&#xff0c;使用分號分隔建議每行寫一句&#xff0c;且結束時不寫分號寫在[ ]、{ }內的跨行語句&#xff0c;被視為一行語句\ 是續行符,實現分行書寫功能 反斜杠表示下一行和本行是同一行 代碼塊與縮進 代碼塊復合語句&#xf…

C#/.NET/.NET Core技術前沿周刊 | 第 35 期(2025年4.14-4.20)

前言 C#/.NET/.NET Core技術前沿周刊&#xff0c;你的每周技術指南針&#xff01;記錄、追蹤C#/.NET/.NET Core領域、生態的每周最新、最實用、最有價值的技術文章、社區動態、優質項目和學習資源等。讓你時刻站在技術前沿&#xff0c;助力技術成長與視野拓寬。 歡迎投稿、推薦…

HTML表單與數據驗證設計

HTML 表單與數據驗證設計&#xff1a;構建可靠的用戶數據采集系統 引言 互聯網的核心是數據交互&#xff0c;而HTML表單是這一交互的主要入口。作為前端工程師&#xff0c;設計高質量的表單不僅關乎用戶體驗&#xff0c;更直接影響數據收集的準確性和系統安全。 在我的學習實…

基于STM32的Keil環境搭建與點燈

本人使用的STM32開發板為正點原子的STM32F103ZE&#xff0c;在此記錄完整的搭建與點燈過程。 一、Keil的安裝與配置 安裝Keil 首先進入Keil下載官網&#xff1a;https://www.keil.com/download/product/ 點擊MDK-ARM&#xff0c;并填寫相關信息&#xff0c;之后開始下載最新版…

React-useRef

如果我們想在hooks里面獲同步取最新的值&#xff0c;那么則可以使用useRef, 關鍵源碼如下&#xff1a; function mountRef<T>(initialValue: T): {|current: T|} {const hook mountWorkInProgressHook();const ref {current: initialValue};hook.memoizedState ref;re…

幽靈依賴與常見依賴管理

文章目錄 前言1. 演示&#xff1a;檢測和修復幽靈依賴步驟1&#xff1a;安裝 depcheck步驟2&#xff1a;在項目根目錄運行 depcheck可能的輸出步驟3&#xff1a;修復幽靈依賴 2. 依賴管理的好習慣 1. 場景設定現在有如下依賴需求&#xff1a; 2. 依賴沖突的表現3. 解決依賴沖突…

如何使用人工智能大模型,免費快速寫工作總結?

如何使用人工智能大模型&#xff0c;免費快速寫工作總結&#xff1f; 詳細學習視頻https://edu.csdn.net/learn/40406/666581

[Java實戰經驗]異常處理最佳實踐

一些好的異常處理實踐。 目錄 異常設計自定義異常為異常設計錯誤代碼&#xff08;狀態碼&#xff09;設計粒度全局異常處理異常日志信息保留 異常處理時機資源管理try-with-resources異常中的事務 異常設計 自定義異常 自定義異常設計&#xff0c;如業務異常定義BusinessExce…

Makefile 入門指南

Makefile 入門指南 最簡單的例子 單文件編譯 假設我們有一個main.cpp文件&#xff0c;最簡單的Makefile如下&#xff1a; # 最簡單的單文件編譯 # 目標:依賴文件 main: main.cpp# 編譯命令g main.cpp -o main使用步驟&#xff1a; 將上述內容保存為名為Makefile的文件&…

PyTorch數據操作基礎教程:從張量創建到高級運算

本文通過示例代碼全面講解PyTorch中張量的基本操作&#xff0c;包含創建、運算、廣播機制、索引切片等核心功能&#xff0c;并提供完整的代碼和輸出結果。 1. 張量創建與基本屬性 import torch# 創建連續數值張量 x torch.arange(12, dtypetorch.float32) print("原始張…

【Redis】Redis中的常見數據類型(一)

文章目錄 前言一、Redis前置知識1. 全局命令2、數據結構和內部編碼3. 單線程架構 二、String 字符串1. 常見命令2. 計數命令3.其他命令4. 內部編碼5. 典型使用場景 三、Hash哈希1. 命令2.內部編碼3. 使用場景4. 緩存方式對比 結語 前言 Redis 提供了 5 種數據結構&#xff0c;…