739.每日温度
单调栈适合于求当前元素左面或者右面第一个比当前元素大或者小的元素。找右边比当前元素大的元素,需要保持单调栈里的元素是单调递增的。本题还需要多理解。
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
stack<int> st;
vector<int> result(temperatures.size(),0);
st.push(0);
for(int i=1;i<temperatures.size();i++){
if(temperatures[i]<=temperatures[st.top()]){
st.push(i);
}else{
while(!st.empty()&&temperatures[i]>temperatures[st.top()]){
result[st.top()]=i-st.top();
st.pop();
}
st.push(i);
}
}
return result;
}
};
时间复杂度是O(n)。
本题如果用暴力的话,两层for循环,会超时。 同时也要注意之后的比较结果会把之前的结果覆盖,但是我们要的是第一个比当前元素大的元素。也简单贴一下代码。时间复杂度是O(n^2)。
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
vector<int> result(temperatures.size(),0);
for(int i=0;i<temperatures.size()-1;i++){
for(int j=i+1;j<temperatures.size();j++){
if(temperatures[j]>temperatures[i]&&result[i]==0){
result[i]=j-i;
}
}
}
return result;
}
};
496.下一个更大元素I
思路和上一题大致相同。unordered_map底层实现是哈希表,查找效率是O(1),map和multimap底层实现是红黑树,查找效率是O(logn),所以选择unordered_map。
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
stack<int> st;
vector<int> result(nums1.size(),-1);
unordered_map<int,int> umap;
for(int i=0;i<nums1.size();i++) umap[nums1[i]]=i;
st.push(0);//记录的是下标,所以应该push(0),而不是push(nusm2[0])
for(int i=1;i<nums2.size();i++){
if(nums2[i]<=nums2[st.top()]){//st.top()记录的是下标
st.push(i);//同理,记录的是下标,不是元素
}else{
while(!st.empty()&&nums2[i]>nums2[st.top()]){
if(umap.count(nums2[st.top()])>0){
int index=umap[nums2[st.top()]];
result[index]=nums2[i];
}
st.pop();
}
st.push(i);
}
}
return result;
}
};
主要的问题还是:1、明确单调栈里记录的是下标而不是元素值2、栈顶元素和遍历到的当前元素的区别,还是要多理解。
剑指 Offer 14- I. 剪绳子
本题和力扣整数拆分是一样的。
class Solution {
public:
int cuttingRope(int n) {
vector<int> dp(n+1);//给我们一个数i,进行拆分后可以得到的最大乘积是dp[i]
dp[0]=0;
dp[1]=0;
dp[2]=1;//题目中说了n>=2
for(int i=3;i<=n;i++){
for(int j=1;j<=i/2;j++){
dp[i]=max(j*(i-j),max(j*dp[i-j],dp[i]));//拆成两个,三个及以上,还有之前拆分过的情况取max
}
}
return dp[n];
}
};
本题还是有点问题,为什么单独拆j就不对了。
剑指 Offer 57 - II. 和为s的连续正数序列
思路:滑动窗口,左闭右闭。因为滑动窗口的左右边界都是一直向右移动的,所以时间复杂度是O(n)。如果sum>target了,就向右移动右边界,如果sum<target,就向右移动左边界,如果相等了,就收获结果,并向右移动左边界(因为以i为开头的序列已经找到了,而且是唯一一个,所以需要向右移动)。
class Solution {
public:
vector<vector<int>> findContinuousSequence(int target) {
//滑动窗口是左闭右闭的,窗口的左边界和右边界永远只能向右移动
vector<vector<int>> res;
int i=1,j=1;
int sum=1;
while(i<=target/2){
if(sum<target){
j++;
sum+=j;
}else if(sum>target){
sum-=i;//先把当前的给减了
i++;
}else{
vector<int> arr;
for(int k=i;k<=j;k++){
arr.push_back(k);
}
res.push_back(arr);
sum-=i;
i++;
}
}
return res;
}
};
剑指 Offer 62. 圆圈中最后剩下的数字
约瑟夫环。最重要的是理解递推公式,参考力扣题解
class Solution {
public:
int lastRemaining(int n, int m) {
if(n==1) return 0;
return (lastRemaining(n-1,m)+m)%n;
}
};
以上是递归法,我们也可以用迭代法来实现。总之就是倒着推。
class Solution {
public:
int lastRemaining(int n, int m) {
int pos=0;
for(int i=2;i<=n;i++){
pos=(pos+m)%i;
}
return pos;
}
};