给定一个整数数组 temperatures
,表示每天的温度,返回一个数组 answer
,其中 answer[i]
是指对于第 i
天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0
来代替。
暴力解法,两层for循环,去更新结果数组
class Solution {
public:
vector dailyTemperatures(vector& temperatures) {
vector res(temperatures.size());
for(int i=0;i=temperatures[j]){
dis++;
}else{
dis++;
flag = false;
break;
}
}
if(flag){
dis=0;
}
res[i] = dis;
}
return res;
}
};//超时
对于温度列表中的每个元素 temperatures[i],需要找到最小的下标 j,使得 i < j 且 temperatures[i] < temperatures[j]。由于温度范围在 [30, 100] 之内,因此可以维护一个数组 next 记录每个温度第一次出现的下标。数组 next 中的元素初始化为无穷大,在遍历温度列表的过程中更新 next 的值。
反向遍历温度列表。对于每个元素 temperatures[i],在数组 next 中找到从 temperatures[i] + 1 到 100 中每个温度第一次出现的下标,将其中的最小下标记为 warmerIndex,则 warmerIndex 为下一次温度比当天高的下标。如果 warmerIndex 不为无穷大,则 warmerIndex - i 即为下一次温度比当天高的等待天数,最后令 next[temperatures[i]] = i。
class Solution {
public:
vector dailyTemperatures(vector& temperatures) {
int n=temperatures.size();
vector ans(n),next(101,INT_MAX);
for(int i=n-1;i>=0;i--){
int index=INT_MAX;
for(int t = temperatures[i]+1;t<=100;++t){
index = min(index,next[t]);
}
if(index!=INT_MAX){
ans[i]=index-i;
}
next[temperatures[i]]=i;
}
return ans;
}
};
时间复杂度:O(nm)),其中 n 是温度列表的长度,m 是数组 next 的长度,在本题中温度不超过 100,所以 m 的值为 100。反向遍历温度列表一遍,对于温度列表中的每个值,都要遍历数组 next 一遍。空间复杂度:O(m),其中 m 是数组 next 的长度。除了返回值以外,需要维护长度为 m 的数组 next 记录每个温度第一次出现的下标位置。
通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时我们就要想到可以用单调栈了。时间复杂度为O(n)。单调栈的本质是空间换时间,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素高的元素,优点是整个数组只需要遍历一次。更直白来说,就是用一个栈来记录我们遍历过的元素,因为我们遍历数组的时候,我们不知道之前都遍历了哪些元素,以至于遍历一个元素找不到是不是之前遍历过一个更小的,所以我们需要用一个容器(这里用单调栈)来记录我们遍历过的元素。
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
stack<int> st;
vector<int> res(temperatures.size(),0);
st.push(0);//0表示下标
for(int i=1;i<temperatures.size();i++){
if(temperatures[i]<temperatures[st.top()]){
st.push(i);
}else if(temperatures[i]==temperatures[st.top()]){
st.push(i);
}else{
while(!st.empty() && temperatures[i] > temperatures[st.top()]){
res[st.top()] = i-st.top();
st.pop();
}
st.push(i);
}
}
return res;
}
};
时间复杂度:O(n);空间复杂度:O(n)
nums1
中数字 x
的 下一个更大元素 是指 x
在 nums2
中对应位置 右侧 的 第一个 比 x
大的元素。给你两个 没有重复元素 的数组 nums1
和 nums2
,下标从 0 开始计数,其中nums1
是 nums2
的子集。对于每个 0 <= i < nums1.length
,找出满足 nums1[i] == nums2[j]
的下标 j
,并且在 nums2
确定 nums2[j]
的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1
。返回一个长度为 nums1.length
的数组 ans
作为答案,满足 ans[i]
是如上所述的 下一个更大元素 。
暴力解法,,两层for循环
class Solution {
public:
vector nextGreaterElement(vector& nums1, vector& nums2) {
vector res(nums1.size(),-1);
for(int i=0;i
从题目示例中我们可以看出最后是要求nums1的每个元素在nums2中下一个比当前元素大的元素,那么就要定义一个和nums1一样大小的数组result来存放结果。题目说如果不存在对应位置就输出 -1 ,所以result数组如果某位置没有被赋值,那么就应该是是-1,所以就初始化为-1。
在遍历nums2的过程中,我们要判断nums2[i]是否在nums1中出现过,因为最后是要根据nums1元素的下标来更新result数组。没有重复元素,我们就可以用map来做映射了。根据数值快速找到下标,还可以判断nums2[i]是否在nums1中出现过。
栈头到栈底的顺序,要从小到大,也就是保持栈里的元素为递增顺序。只要保持递增,才能找到右边第一个比自己大的元素。接下来就要分析如下三种情况,一定要分析清楚。
class Solution {
public:
vector nextGreaterElement(vector& nums1, vector& nums2) {
stack st;
vector res(nums1.size(),-1);
if(nums1.size()==0){
return res;
}
unordered_map umap;// key:下标元素,value:下标
for(int i=0;i nums2[st.top()]){
if(umap.count(nums2[st.top()])>0){
int index = umap[nums2[st.top()]];
res[index] = nums2[i];
}
st.pop();
}
st.push(i);
}
}
return res;
}
};
给定一个循环数组 nums
( nums[nums.length - 1]
的下一个元素是 nums[0]
),返回 nums
中每个元素的 下一个更大元素 。数字 x
的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1
。
直接把两个数组拼接在一起,然后使用单调栈求下一个最大值不就行了!这样做确实可以!
class Solution {
public:
vector<int> nextGreaterElements(vector<int>& nums) {
vector<int> nums1(nums.begin(),nums.end());
nums.insert(nums.end(),nums1.begin(),nums1.end());
vector<int> res(nums.size(),-1);
if(nums.size()==0){
return res;
}
stack<int> st;
st.push(0);
for(int i=1;i<nums.size();i++){
if(nums[i]<nums[st.top()]){
st.push(i);
}else if(nums[i]==nums[st.top()]){
st.push(i);
}else{
while(!st.empty() && nums[i] > nums[st.top()]){
res[st.top()] = nums[i];
st.pop();
}
st.push(i);
}
}
res.resize(nums.size()/2);
return res;
}
};
这种写法确实比较直观,但做了很多无用操作,例如修改了nums数组,而且最后还要把result数组resize回去。resize倒是不费时间,是O(1)的操作,但扩充nums数组相当于多了一个O(n)的操作。
class Solution {
public:
vector nextGreaterElements(vector& nums) {
int len = nums.size();
vector res(len,-1);
if(len == 0){
return res;
}
stack st;
st.push(0);
for(int i=1;inums[st.top()]){
res[st.top()]=nums[i%len];
st.pop();
}
st.push(i%len);
}
}
return res;
}
};
给定 n
个非负整数表示每个宽度为 1
的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
本题暴力解法也是也是使用双指针。首先要明确,要按照行来计算,还是按照列来计算。
首先,**如果按照列来计算的话,宽度一定是1了,我们再把每一列的雨水的高度求出来就可以了。**可以看出每一列雨水的高度,取决于,该列 左侧最高的柱子和右侧最高的柱子中最矮的那个柱子的高度。
class Solution {
public:
int trap(vector<int>& height) {
int sum=0;
for(int i=0;i<height.size();i++){
if(i==0 || i==height.size()-1){
continue;
}
int rH=height[i];
int lH= height[i];
for(int r=i+1;r<height.size();r++){
if(height[r]>rH){
rH = height[r];
}
}
for(int l=i-1;l>=0;l--){
if(height[l]>lH){
lH = height[l];
}
}
int h=min(rH,lH) - height[i];
if(h>0){
sum+=h*1;
}
}
return sum;
}
};//超时
因为每次遍历列的时候,还要向两边寻找最高的列,所以时间复杂度为O(n^2),空间复杂度为O(1)。双指针法可以优化
class Solution {
public:
int trap(vector& height) {
if(height.size()<=2){
return 0;
}
vector maxleft(height.size(),0);
vector maxright(height.size(),0);
int size=maxright.size();
maxleft[0] = height[0];
for(int i=1;i=0;i--){// 记录每个柱子右边柱子最大高度
maxright[i]=max(maxright[i+1],height[i]);
}
int sum=0;
for(int i=0;i0){
sum +=count;
}
}
return sum;
}
};
通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时我们就要想到可以用单调栈了。而接雨水这道题目,我们正需要寻找一个元素,右边最大元素以及左边最大元素,来计算雨水面积。
首先单调栈是按照行方向来计算雨水,使用单调栈内元素的顺序,从栈头(元素从栈头弹出)到栈底的顺序应该是从小到大的顺序。因为一旦发现添加的柱子高度大于栈头元素了,此时就出现凹槽了,栈头元素就是凹槽底部的柱子,栈头第二个元素就是凹槽左边的柱子,而添加的元素就是凹槽右边的柱子。
遇到相同的元素,更新栈内下标,就是将栈里元素(旧下标)弹出,将新元素(新下标)加入栈中。
栈里要保存什么数值:使用单调栈,也是通过 长 * 宽 来计算雨水面积的。长就是通过柱子的高度来计算,宽是通过柱子之间的下标来计算,那么栈里有没有必要存一个pair
class Solution {
public:
int trap(vector& height) {
if(height.size()<=2){
return 0;
}
stack st;
st.push(0);
int sum=0;
for(int i=1;i height[st.top()]){
int mid=st.top();
st.pop();
if(!st.empty()){
int h=min(height[st.top()],height[i])-height[mid];
int w=i-st.top()-1;
sum += h*w;
}
}
st.push(i);
}
}
return sum;
}
};
此时大家应该可以发现其实就是**栈顶和栈顶的下一个元素以及要入栈的元素,三个元素来接水!**那么雨水高度是 min(凹槽左边高度, 凹槽右边高度) - 凹槽底部高度,代码为:int h = min(height[st.top()], height[i]) - height[mid];
雨水的宽度是 凹槽右边的下标 - 凹槽左边的下标 - 1(因为只求中间宽度),代码为:int w = i - st.top() - 1 ;
当前凹槽雨水的体积就是:h * w
。
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。求在该柱状图中,能够勾勒出来的矩形的最大面积。
暴力求解,超时
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int sum=0;
for(int i=0;i<heights.size();i++){
int left=i;
int right=i;
for(;left>=0;left--){
if(heights[left]<heights[i]){
break;
}
}
for(;right<heights.size();right++){
if(heights[right]<heights[i]){
break;
}
}
int w=right-left-1;
int h=heights[i];
sum=max(sum,h*w);
}
return sum;
}
};
本题要记录记录每个柱子 左边第一个小于该柱子的下标,而不是左边第一个小于该柱子的高度。
class Solution {
public:
int largestRectangleArea(vector& heights) {
int len = heights.size();
vector minleft(len);
vector minright(len);
// 记录每个柱子 左边第一个小于该柱子的下标
minleft[0]=-1;
for(int i=1;i=0 && heights[t]>=heights[i]){
t = minleft[t];
}
minleft[i] = t;
}
minright[len-1]=len;
for(int i=len-2;i>=0;i--){
int t=i+1;
while(t=heights[i]){
t = minright[t];
}
minright[i]=t;
}
int res=0;
for(int i=0;i
这里就涉及到了单调栈很重要的性质,就是单调栈里的顺序,是从小到大还是从大到小。因为本题是要找每个柱子左右两边第一个小于该柱子的柱子,所以从栈头(元素从栈头弹出)到栈底的顺序应该是从大到小的顺序!
只有栈里从大到小的顺序,才能保证栈顶元素找到左右两边第一个小于栈顶元素的柱子。此时大家应该可以发现其实就是栈顶和栈顶的下一个元素以及要入栈的三个元素组成了我们要求最大面积的高度和宽度。主要就是分析清楚如下三种情况:
class Solution {
public:
int largestRectangleArea(vector& heights) {
int res=0;
stack st;
heights.insert(heights.begin(),0);// 数组头部加入元素0
heights.push_back(0);
st.push(0);
for(int i=1;iheights[st.top()]){
st.push(i);
}else if(heights[i] == heights[st.top()]){
st.push(i);
}else{
while(!st.empty() && heights[i] < heights[st.top()]){
int mid=st.top();
st.pop();
if(!st.empty()){
int left=st.top();
int right=i;
int w=right-left-1;
int h=heights[mid];
res=max(w*h,res);
}
}
st.push(i);
}
}
return res;
}
};