单调栈模板
单调栈用来求出数组nums[i]中第一个大于(小于)。
stack<int> stk; //存放的是index 当然有时候可以是值 看情况
vector<int> bigger;
for(int i =0; i< nums.size(); i++)
{
while(!stk.empty() && nums[stk.back()] < nums[i])
{
//这里可以加出栈条件
bigger[stk.back() ] = i;
stk.pop();
//此时栈是单调减的
}
//这里可以加入入站条件
stk.push_back(i);
}
leetcode 316 去除重复字母
此题要求对字符串去除重复的字母,并且保持字符串的字典序。
int removeDuplicateLetters(string s)
{
vector<int> num(26,0), vis(26,0);
//num用来记录s中26个字母的数量
for(auto ch: s)
{
num[ch-'a']++;
}
//vis用来记录栈中是否有第i个字母
string stk;
for(auto ch :s)
{
if(!vis[ch-'a']) //栈中不存在ch
{
while(!stk.empty()&& ch < stk.back() )
{
//保持字典序意味着我们要用单调递增的栈
//一旦ch 小于栈尾会影响单调性 应该弹栈了
//但是stk.back()不一定能弹栈
//只有stk.back()这个字符还有剩余的时候可以弹栈
if(num[stk.back()] > 0)
{
vis[stk.back() -'a'] = 0; //栈中不在有栈尾这个元素
stk.pop_back();
}
else{
break; //注意要跳出循环 因为如果这时候back不能弹 back之前的都不能弹
}
}
stk.push_back(ch);
vis[ch-'a'] = 1;
}
//不论ch进不进栈 可用的ch都少了一个
num[ch - 'a']--;
}
//遍历结束
return stk;
}
- 去除重复字符这个“去除“是通过vis来控制的,当栈中存在ch的时候,就没有必要再向栈中加入ch。
- num保证栈中至少有一个ch
leetcode321 拼接最大数
这道题的基本思路就是 从nums1中选i个最大的数, 从 nums2中选择k-i个最大的数。然后将他们merge在一起。
整体的框架:
vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k)
{
int n1 = nums1.size(), n2 = nums2.size();
vector<int> res1(k,0),res2(k,0); //res1 res2分别存储nums1, nums2的最大的几个
vector<int> res(k,0);
for(int i = 0;i < =k; i++)
{
if(i <= n1 && (k-i) <= n2)//可选
{
maxL(nums1,i, res1);
maxL(nums2,k-i,res2);
mergevec(res1, res2, i,k-i); //合并
if(biger(res2,res))
{
res = res2;
}
}
}
return res;
}
其中maxL函数用来寻找nums数组中最大的L个,这时候就需要用到单调栈。由于有长度L的限制,弹栈的时候需要判断一下剩下的元素是否够用。
void maxL(vector<int>& nums, int L, vector<int>& res)
{
int n = nums.size(), top = -1;//此时直接用res数组当栈了
std::fill(res.begin(), res.end(),0);
for(int i =0; i < n; i++)
{
while(top >=0 && nums[i] > res[top])
{
//后面有更大的元素 需要弹栈
//当前的栈仍旧需要L个元素 数组中还剩下 n-1-i+1 = n-i个元素
//如果弹栈 需要的元素数量为L+1 数组中还剩 n-i个元素
//如果弹栈 L+1 <= n-i
if(L<n-i)
{
top--,L++;
}
else
{break;} //一定break不然死循环 卡在栈尾了
}
if(L>0)//仍有需要的元素
{
res[++top] = nums[i];
L--;
}
}
}
获取了nums1的i个最大的元素和nums2的k-i个最大个元素 ,需要将他们合并。我这个合并函数写的不太好,凑合能用。
void mergevec(vector<int>& nums1, vector<int>& nums2, int l1, int l2)
{
if(l1== 0){return ;}//merge nums1 into num2
if(l2 == 0){ nums2 = nums1;return;}
int i = 0, j= 0,k= l1+l2;
vector<int> temp(k,0);
for(int t = 0; t<k;t++)
{
if(i>= l1)
{
temp[t] = nums2[j++];
continue;
}
if(j >l2)
{
temp[t] = nums1[i++];
continue;
}
if(nums1[i] >nums2[j])
{
temp[t] = nums1[i++];
}
else if(nums1[i] <nums2[j])
{
temp[t] = nums1[j++];
}
else//这里有一个等于的情况
{
int p1 = i,p2= j;
while(true)
{
if(p1 == l1)//nums2最终是大的那一个
{
temp[t] = nums2[j++];
break;
}
if(p2 == l2)
{
temp[t] = nums1[i++];
}
if(nums1[p1] < nums2[p2])
{
temp[t] = nums2[j++];
break;
}
if(nums1[p1] > nums2[p2])
{
temp[t] = nums1[i++];
break;
}
p1++,p2++;
}
}
}
nums2 = temp;
}
其实就是等于的状态下判断一下哪个后缀大一些,感觉用后面的bigger函数也能做,不过我没试。
判断数组之间大小的函数biger:
bool biger(vector<int>& nums1, vector<int>& nums2)
{
int k = nums1.size();
for(int i =0; i < k;i++)
{
if (nums1[i] > nums2[i])
{
return true;
}
else if(nums1[i] <nums2[i])
{
return false;
}
}
return false;
}
这样就解决了这个问题。