每日一題:leetcode456.132模式

題目描述

在這里插入圖片描述

題目分析

我覺得這道題應該是我做過最難的中等題之一了,這是昨天的每日一題,但是昨天用nlogn的做法做出來以后在看題解,發現有些看不懂(覺得題解有點故弄玄虛)。然后今天中午又花了一點時間才搞懂,感覺從這道題里面學到一點東西。

剛拿到這道題的時候我沒有什么思路,第一想法當然是n3n^3n3的暴力,但是顯然是不行的,第二想法是,用線段樹維護區間最大值,然后遍歷12,這樣的復雜度是n2lognn^2lognn2logn,雖然好了一點但仍然會超時。我自己沒有想到枚舉一個,然后通過數據結構求其他兩個的思路,覺得這方面有點欠缺,即這種思路的轉換,仿佛變量比較多就必須每一個都枚舉才可以。

遍歷3,用紅黑樹維護2

看到題解從左往右枚舉3,然后用一個變量保存1(左邊的最小值),用一個紅黑樹(使用set)保存右邊所有的值,在里面查找比1大比3小的數。這樣的做法很符合直覺。
我用這樣的思路很快寫了一個和題解差不多的代碼:

O(nlogn)代碼

class Solution {
public:bool find132pattern(vector<int>& nums) {using vector_size = vector<int>::size_type;vector_size n = nums.size();if (n < 3) return false;int left_min = nums[0];multiset<int> right_all(nums.begin() + 2, nums.end());for (vector_size i = 1; i < n - 1; ++i) {if (left_min < nums[i]) {auto iter = right_all.upper_bound(left_min);if (iter != right_all.end() && *iter < nums[i]) {return true;}}left_min = min(left_min, nums[i]);right_all.erase(right_all.find(nums[i + 1]));}return false;}
};

紅黑樹的查找和刪除都是logn的,真不錯。

其他思路都統統使用了單調棧,其實單調棧本身的非常簡單,就是一個棧,里面是有序的,如果一個新加入的元素破壞了順序,就彈出,直到不破壞為止,再入棧。

遍歷1,用單調棧維護32

這道題的神奇之處在于用了一個遞減的單調棧,并用一個變量保存所有出棧元素的最大值,因為入棧、出棧操作的先后順序導致這個出棧元素的最大值肯定比棧中某個元素小而且在其前面,因為是那個元素將它出棧的,而這樣就得到了32,我們只要遍歷到比2小的1即可。這種方法的復雜度應該是最低的。

O(n)代碼

class Solution {
public:bool find132pattern(vector<int>& nums) {int n = nums.size();if (n < 3) return false;stack<int> stk;     //單調棧int max_pop = INT_MIN;        //彈出的元素的最大值for (int i = n - 1; i >= 0; --i) {if (max_pop != INT_MIN) {if (nums[i] < max_pop) return true;}while (!stk.empty() && nums[i] > stk.top()) {max_pop = max(max_pop, stk.top());stk.pop();}stk.push(nums[i]);}return false;}
};

剛才習慣性地使用vector<int>::size_type類型,但是循環的時候卻RE,這是因為size_type類型是一個unsigned類型,在---1的時候會變成一個非常大的數字導致死循環。應該盡量還是用int才好。

遍歷3,用單調棧維護2,用前綴數組維護1

雖然同樣是使用單調棧解決問題,但是用單調棧維護2的思路則和上面完全不同。我在看題解的時候一度非常迷惑,但是想明白原理以后才發現原來兩者之間沒有什么關系。

上面已經提到了,上面的解法的精髓在于維護了所有彈出元素的最大值,通過入棧、出棧的邏輯順序得到了數據的物理順序。但是現在這種解法則是完全不一樣的角度。

我們用一個前綴數組處理出任意位置左邊的最小值,得到1,用單調棧維護右邊的比本身3小的最大值,得到2,通過判斷12的大小來決定是否存在132序列。

問題的難點在于如何用單調棧維護右邊比當前遍歷到的3小的最大值,做法如下:將把當前值插入到單調棧中彈出的數據的最大值作為2

我認為難以理解的地方在于,把一些比較小的數組彈出了,怎么能夠保證后面不會用到這些數字呢?

想要理解,可以用一個簡單的數學歸納:

  1. 初始的時候棧中是有元素的
  2. 新插入的當前元素可能沒有進行彈出操作,這意味著右邊全都是比當前值大的數字
  3. 當第一次插入x發生彈出時,我們保存了彈出元素的最大值right_max,并且和其左邊最小值left_min進行比較,如果right_max>left_min,則出現132序列,如果沒有出現,則意味著x左邊全部元素大于等于right_max,也就是說right_max以及其他這次彈出的數據無論如何都不會成為2了,因此可以丟棄。

如果理解了上面的話,代碼就比較簡單了:

O(nlogn)代碼

class Solution {
public:bool find132pattern(vector<int>& nums) {int n = nums.size();if (n < 3) return false;stack<int> stk;     //單調棧維護2int max_pop;        //一次pop的最大值vector<int> left_min;left_min.reserve(nums.size());left_min.push_back(INT_MAX);for (int i = 1; i <n; ++i) {left_min[i] = min(left_min[i - 1], nums[i - 1]);}stk.push(nums[n - 1]);for (int i = n - 2; i >= 0; --i) {max_pop = INT_MIN;while (!stk.empty() && nums[i] > stk.top()) {max_pop = max(max_pop, stk.top());stk.pop();}if (left_min[i] < max_pop) return true;stk.push(nums[i]);}return false;}
};

時間復雜度應該是比第二種解法要高一個O(n)

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

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

相關文章

leetcode283.移動零

題目描述 題目分析 在寫簡單題放松&#xff0c;看到這道題第一個想法是用STL庫函數&#xff0c;雖然知道大概要用雙指針之類的&#xff0c;但是庫函數爽哇。 class Solution { public:void moveZeroes(vector<int>& nums) {stable_sort(nums.begin(), nums.end(), …

每日一題:leetcode61.旋轉鏈表

題目描述 題目分析 很容易發現&#xff0c;如果k是n的整數倍&#xff0c;相當于沒有移動。這樣直接對k%n使得k在一個可以接受的范圍。 因為是順序移動&#xff0c;各元素之間的相對位置保持不變&#xff0c;所以就想著將鏈表先變成一個環。然后再移動頭指針&#xff0c;最后再…

每日一題:leetcode173.二叉搜索樹迭代器

題目描述 題目分析 更加地覺得編程重要的不在于如何寫代碼&#xff0c;用什么具體的技巧&#xff0c;編碼本身只是一種將思維呈現的方式&#xff0c;但是如果思維是不清晰的&#xff0c;那么就算懂得再多的編碼的奇技淫巧也是沒有什么幫助的。相反&#xff0c;如果有一個清晰的…

Ubuntu20.04 Clion/Pycharm/IDEA 輸入中文+光標跟隨解決方案

ibus輸入法&#xff08;棄用&#xff09; 之前一直用的搜狗輸入法&#xff0c;但是搜狗輸入法無法在Jetbrains全家桶下使用&#xff0c;但是又需要輸入中文&#xff0c;沒有辦法我只能下載了谷歌輸入法&#xff0c;十分難用&#xff0c;但是也沒有其他辦法&#xff0c;經常到網…

leetcode11.盛最多水的容器

題目描述 題目分析 看到題目后第一個想法當然是O(n2)O(n^2)O(n2)的&#xff0c;但是數據范圍是3e4&#xff0c;應該會超時&#xff0c;而且這種數據范圍也不是讓暴力求解的 。 相當于求解∑i<jmax((j?i)?min(a[i],a[j]))\sum_{i<j}{max((j-i)*min(a[i],a[j]))}∑i<…

每日一題:leetcode190.顛倒二進制位

題目描述 題目分析 題目本身很簡單&#xff0c;沒覺得有什么技巧可以再進行優化了&#xff0c;覺得位運算是無法打亂相對順序的&#xff0c;而這里需要進行鏡像顛倒的操作。因此就踏實地寫了一個循環。 在使用位運算得到每一位的時候&#xff0c;我吸取了經驗&#xff0c;用一…

結構屈曲分析

結構屈曲分析主要用于判定結構受載后是否有失穩風險&#xff0c;作為工程應用&#xff0c;一般分為線性屈曲分析和非線性屈曲分析。 線性屈曲分析需要具備較多的前提條件&#xff0c;如載荷無偏心、材料無缺陷等&#xff0c;在實際工程應用中結構制作過程和加載方式很難達到線性…

每日一題:leetcode74.搜索二維矩陣

題目描述 題目分析 感覺這是一個放錯標簽的簡單題。題目非常簡單&#xff0c;思路應該很明確是二分&#xff0c;我很快寫了一個&#xff08;雖然不小心把!打成調試了一會&#xff09;。 class Solution { public:bool searchMatrix(vector<vector<int>>& mat…

每日一題:leetcode90.子集貳

題目描述 題目分析 感覺這道題讓自己對枚舉排列有了一個更好的認識&#xff0c;感覺自己的這種思路不錯。 假設沒有重復元素&#xff08;退化成78.子集&#xff09;&#xff0c;我們應該怎么做&#xff1f;初始的時候冪集中只有一個空集&#xff0c;然后對每個元素&#xff0…

每日一題:leetcode1006.笨階乘

題目描述 題目分析 因為順序一定且沒有括號&#xff0c;所以邏輯很簡單。我們要順序處理的矛盾在于&#xff0c;減號后面會再出現乘法和除法&#xff0c;我們不妨將對乘法和除法用一個臨時值進行計算&#xff0c;計算結束后再合并到值里面&#xff0c;一般來講乘法和除法的處理…

每日一題:leetcode80.刪除有序數組中的重復元素貳

題目描述 題目分析 又是一道貼錯標簽的簡單題&#xff0c;很明顯的雙指針&#xff0c;我的做法是用兩個變量保存是否需要記錄&#xff0c;官方題解的做法是直接判斷&#xff0c;人家的高明一些 class Solution { public:int removeDuplicates(vector<int>& nums) {…

每日一題:leetcode81.搜索旋轉排序數組Ⅱ

題目描述 題目分析 不含重復元素的題解&#xff08;leetcode33&#xff09; 這道題也是我們算法課的一道編程題&#xff0c;寫完以后發現當時的思路和現在沒有什么變化&#xff0c;果然是自己啊。我的想法是先判斷區間整體是升序的還是旋轉的&#xff0c;如果是升序的就按照正…

C++ JSON庫:JSON for Morden C++

緒論 最近因為項目的需要&#xff0c;需要對JSON進行一定的數據處理&#xff0c;因為想要用C進行編碼&#xff0c;便對C的JSON庫進行的調研&#xff0c;發現這個庫比較好用&#xff1a;JSON for Morder C。 使用指南 想要使用這個json庫&#xff0c;只需要在源文件中包含jso…

Linux信號實現精確到微秒的sleep函數:通過sigsuspend函數解決時序競態問題

原理就是先使用定時器定時&#xff0c;然后再使用pause函數或者sigsuspend函數主動阻塞掛起&#xff0c;最終恢復現場。 如果使用pause函數的話&#xff0c;優點是使用簡單&#xff0c;缺點是有可能產生時序競態&#xff0c;導致進程一直阻塞下去&#xff1a;在定時和掛起之間…

Linux創建多個子進程并通過捕獲SIGCHLD信號進行非阻塞回收

我們通過fork函數創建多個子進程&#xff0c;并通過exec函數族在子進程中進行其他的工作&#xff0c;但是為了避免僵尸進程&#xff0c;我們要對子進程進行回收。常用的回收方式是wait或者waitpid進行阻塞回收&#xff0c;因為如果非阻塞回收很難把握時機&#xff0c;而阻塞回收…

Linux創建守護進程

守護進程&#xff08;Daemon&#xff09;是運行在后臺的一種特殊進程。它獨立于控制終端并且周期性地執行某種任務或等待處理某些發生的事件。它不需要用戶輸入就能運行而且提供某種服務&#xff0c;不是對整個系統就是對某個用戶程序提供服務。Linux系統的大多數服務器就是通過…

Linux創建多個子線程并回收

創建子線程的邏輯相比子進程要更容易理解一些&#xff0c;因為線程沒有像進程那樣復制很多東西另起爐灶&#xff0c;子線程從傳入的開始函數開始運行&#xff0c;但是難點在于傳入參數和回收時獲取退出狀態&#xff0c;因為這兩個原本都是void *類型的&#xff0c;而我們在使用…

Qt發布程序

Windows&#xff1a; https://www.cnblogs.com/linuxAndMcu/p/10974927.html Ubuntu&#xff1a; https://blog.csdn.net/u014779536/article/details/107854060

K210入門

之前購買了一個Sipeed Maix M1w Dock k210的開發板&#xff0c;想著自己鼓搗鼓搗&#xff0c;在網上看到了一些好的教程&#xff0c;在這里記錄一下&#xff1a; 嵌入式AI從入門到放肆【K210篇】-- 硬件與環境&#xff1a;介紹了各種開發環境的搭建&#xff0c;但是不是特別詳細…

C++輸入輸出:cin/cout 還是 scanf/printf?

相信使用C的人都有一種迷惑或者是不自信&#xff1a;在輸入輸出的時候是不是應該使用scanf/printf更好呢&#xff0c;因為傳說cin/cout龜速&#xff0c;我當時也長期被這個所困擾&#xff0c;后來在閱讀C primer第五版的時候我自己做了一個測試&#xff0c;發現如果不使用std::…