有两个集合num1和num2,其中num1是num2的子集。让你找出对于每个num1的元素,在num2中再其右边的比其大的第一个数。
由于集合最大不超过1000,因此O(n2)的遍历是一定能过的。但是,这种又和位置有关又和大小有关的要求,我想到了单调队列和单调栈。为什么呢?对于一个数,当从右向左开始遍历时,如果i < j 且 num[i] >= num[j],那么对于i左边的数来说num[j]一定是没有用了。基于此,我从右向左遍历时,维护一个单调递增堆栈。当遍历到i时,检查栈顶和num[i]的大小关系,如果栈顶top <= num[i],则出栈,直到top > num[i] 或 栈为空。然后将该数压入栈。这样,弹出去的那些数都是既在右边又小的数,在后面的遍历过程中就没有作用了。这样一次遍历就能完成啦。
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& findNums, vector<int>& nums) {
stack<int> st;
unordered_map<int, int> cnt;
for (auto ite = nums.rbegin(); ite != nums.rend(); ite++) {
while (!st.empty() && st.top() < *ite) st.pop();
cnt[*ite] = st.empty() ? -1 : st.top();
st.push(*ite);
}
for (int i = 0; i < findNums.size(); i++) {
findNums[i] = cnt[findNums[i]];
}
return findNums;
}
};
针对问题1的改进,假设num2是可以头尾拼接一起无限接下去的,把num1换成num2,然后问的问题也是一样的。
由于num2是头尾拼接的,因此实际上我们只用考虑两个num2拼接在一起的数组即可。我也不需要新建一个数组把2个num2同时的copy过去,我只需要把指针i从2n-1开始循环,用num2[i%n]表示当前的数。那么剩下的方法都一样,从右端开始维护一个单调栈即可。
class Solution {
public:
vector<int> nextGreaterElements(vector<int>& nums) {
int n = nums.size();
if (n == 0) return vector<int>{};
vector<int> ret(nums.size(), 0);
stack<int> st;
for (int i = 2*n-1; i >= 0; i--) {
int num = nums[i%n];
while (!st.empty() && num >= st.top()) st.pop();
ret[i%n] = st.empty() ? -1 : st.top();
st.push(num);
}
return ret;
}
};
给你一个数字表示的字符串,问你他的next Permutation是什么?
这题其实思路并不是很难,由于int最多才32位所以你暴力循环也绝对不会超时。但这题的重点在于细节的考虑与打磨,否则WA好多次。思路如下:
1.从右往左开始遍历,对于每一位i,找到其右边的大于i的数字的最小值,假设处于第j位。
2.交换i位与j位的值。
3.对于i位以后的数字,将他们从小到大排序。(Details)
class Solution {
public:
int nextGreaterElement(int n) {
vector<int> digits;
//首先通过这种经典的方法得到数字n的每一位
while (n) {
digits.push_back(n%10);
n /= 10;
}
reverse(digits.begin(), digits.end());
int pos = -1;
for (int i = digits.size()-1; i >= 0; i--) {
int k = -1, Min = 10;
for (int j = digits.size()-1; j > i; j--) {
if (digits[j] > digits[i]) {
k = j;
Min = min(Min, digits[j]);
}
}
if (k != -1) {
pos = i;
swap(digits[i], digits[k]);
break;
}
}
if (pos == -1) return -1;
sort(digits.begin()+pos+1, digits.end());
int res = 0;
for (int i = 0; i < digits.size(); i++)
res = 10*res + digits[i];
return res;
}
};