每日一题: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.mzph.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;如果有一个清晰的…

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;用一…

每日一题: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;如果是升序的就按照正…

Linux创建多个子线程并回收

创建子线程的逻辑相比子进程要更容易理解一些&#xff0c;因为线程没有像进程那样复制很多东西另起炉灶&#xff0c;子线程从传入的开始函数开始运行&#xff0c;但是难点在于传入参数和回收时获取退出状态&#xff0c;因为这两个原本都是void *类型的&#xff0c;而我们在使用…

Qt for Android环境配置

最近想写一个小APP&#xff0c;但是又不想用Android Studio进行开发&#xff0c;想要用C进行开发&#xff0c;听说Qt可以进行Android开发&#xff0c;就想尝试一下&#xff0c;结果花了一天时间来配置环境。。。而且发现windows下配置环境更简单一些&#xff08;我中途还切换到…

Leetcode第284场周赛

绪论 最近发现Leetcode每周的周赛难度挺适合我的&#xff0c;而且时间也比较友好&#xff08;不像Codeforces每次都是半夜&#xff09;。所以连续参加了三周的周赛。这次才想起来应该记录一下自己的参赛历程。一方面是总结经验&#xff0c;另一方面有了记录就更有动力去提升&a…

Leetcode第286场周赛

绪论 上周因为有事没有参加周赛&#xff0c;这周没有错过。这次周赛拿到了人生第一个AK&#xff0c;参加大大小小的比赛这么多次&#xff0c;从来没有AK过&#xff0c;泪目了。 感觉这次比赛的思维难度对我来讲稍高一些&#xff0c;前三道题就花了一个小时&#xff0c;而以往…

第287场周赛

绪论 虽然是上周日参加的比赛&#xff0c;但是这周没有怎么学习&#xff0c;每天就是玩耍。也导致对周赛的总结迟迟没有进行。想着再拖下去下次周赛都要开始了&#xff0c;在这里补一下。 这场比赛总体比上场简单一些&#xff0c;但是最后一道题因为忘记初始化类内变量导致调试…

第288场周赛

绪论 虽然没有AK&#xff0c;但是不知道为什么排名比以前AK了都靠前。可能是因为最后一道题有些难度&#xff0c;缩小了我和大佬之间的差距。最后一个小时写最后一道题&#xff0c;累死累活想了一个贪心遍历的算法&#xff0c;当时是一直RE&#xff0c;后来下来调了调又WA了。 …

Clion远程部署和运行

绪论 作为Clion的忠实粉丝&#xff0c;现在的我的几乎所有的coding都是通过Clion完成。因为需要在服务器上进行开发&#xff0c;又离不开Clion&#xff0c;就了解了如何通过Clion远程部署和开发。 主要是借鉴了博客&#xff1a;使用Clion优雅的完全远程自动同步和远程调试c。如…

Ubuntu install ‘Bash to dock‘

绪论 在Ubuntu环境搭建这篇博客中记录了使用Dash To Dock来配置Ubuntu的菜单项&#xff0c;使得实现macOS一样的效果。为了配置新电脑的环境&#xff0c;我还是想安装这个软件。但是如今在Ubuntu Software中已经找不到这个软件了&#xff0c;我在网上借鉴了一些博客的经验才得…

Leetcode第309场周赛

Date: September 4, 2022 Difficulty: medium Rate by others: ⭐⭐⭐⭐ Time consuming: 1h30min 题目链接 竞赛 - 力扣 (LeetCode) 题目解析 2399. 检查相同字母间的距离 class Solution {public:bool checkDistances(string s, vector<int>& distance) {vec…

C++ 条件变量的使用

绪论 并发编程纷繁复杂&#xff0c;其中用于线程同步的主要工具——条件变量&#xff0c;虽然精悍&#xff0c;但是要想正确灵活的运用却并不容易。 对于条件变量的理解有三个难点&#xff1a; 为什么wait函数需要将解锁和阻塞、唤醒和上锁这两对操作编程原子的&#xff1f;为…