难度 : 简单
暴力枚举 :
枚举第一个数组中得所有数字,枚举第二个数组,首先找到第一层枚举的数字,然后再在第二个数组中枚之后的数字,如果有更大的数字,然后就记录直接返回,否则就返回-1
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
int n = nums1.size();
vector<int> ans(n);
for (int i = 0; i < n; i ++) {
auto pos = find(nums2.begin(), nums2.end(), nums1[i]);
int t = -1;
if (pos != nums2.end())
{
int j = pos - nums2.begin();
for (int k = j; k < nums2.size(); k ++)
if (nums2[k] > nums1[i]) {
t = nums2[k];
break;
}
}
ans[i] = t;
}
return ans;
}
};
时间复杂度 O ( n m ) O(nm) O(nm) n
是nums1
的长度, m
是nums2
的长度
空间复杂度 O ( n ) O(n) O(n) 用到了ans
数组和若干变量
用一个哈希表来记录nums2
中所有元素位置然后可以在 O ( 1 ) O(1) O(1)的时间复杂度下来查找该元素,之后思路和暴力枚举类似
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
unordered_map<int, int> hash;
for (int i = 0; i < nums2.size(); i ++) hash[nums2[i]] = i;
vector<int> res;
for (int i = 0; i < nums1.size(); i ++)
{
bool flag = true;
for (int j = hash[nums1[i]] + 1; j < nums2.size(); j ++)
if (nums2[j] > nums1[i])
{
res.push_back(nums2[j]);
flag = false;
break;
}
if (flag) res.push_back(-1);
}
return res;
}
};
时间复杂度 O ( n 2 ) O(n^2) O(n2) 本质还是需要两层循环
空间复杂度 O ( n ) O(n) O(n) 上面类似
我们注意到,我们要在nums2
中找nums1
中每个元素位置之后第一个的比该元素更大的数,此时我们就可以想到联想到单调栈
简单回顾一下单调栈的思路 :
常见模板(基于C++
的STL
库)
stack<type> stk;
for (int i = 0; i < n; i ++) {
while (stk.size() && check(stk.top(), element)) stk.pop();
if (stk.size()) work();
else ...
stk.push(element); //把当前元素推入栈
}
回归正题 本题的单调栈写法如下 :
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
int n = nums2.size();
stack<int> stk;
unordered_map<int, int> num_larger;
for (int i = n - 1; i >= 0; i --) {
while (stk.size() && stk.top() <= nums2[i]) stk.pop();
if (stk.size()) {
num_larger[nums2[i]] = stk.top();
} else {
num_larger[nums2[i]] = -1;
}
stk.push(nums2[i]); // 将元素入栈
}
vector<int> res;
for (int i = 0; i < nums1.size(); i ++) {
res.push_back(num_larger[nums1[i]]);
}
return res;
}
};
时间复杂度 O ( n ) O(n) O(n)
空间复杂度 O ( n ) O(n) O(n) 与上面类似
find
部分,可以稍微减少一点时间,但不影响整体的时间复杂度难度 : 中等
和上一道题目类似, 只是需要稍微改变一下实现方式就行,可以将数组拉长为自己的两倍,本代码使用的是循环数组,可以简化这一部分代码,但是可能比较难想到
实现代码如下
class Solution {
public:
vector<int> nextGreaterElements(vector<int>& nums) {
int n = nums.size();
vector<int> res(n, -1);
stack<int> stk;
for (int i = 2 * n - 1; i >= 0; i --) {
while (!stk.empty() && nums[i % n] >= stk.top()) stk.pop();
if (i < n && stk.size()) res[i] = stk.top();
stk.push(nums[i % n]);
}
return res;
}
};
难点
难度 : 中等
next_permutation()
暴力解决true
, 一直到最后排列回来原点返回false
实现代码如下
class Solution {
public:
using LL = long long;
int nextGreaterElement(int n) {
function<string(int)> get = [&](int x) -> string {
string res;
while (x) {
int t = x % 10;
res += '0' + t;
x /= 10;
}
reverse(res.begin(), res.end());
return res;
};
string res = get(n);
while (next_permutation(res.begin(), res.end())) {
LL t = stoll(res);
if (t <= INT32_MAX && t > n) return t;
}
return -1;
}
};
时间复杂度 O ( n ! ) O(n!) O(n!) n
是该数字的长度,就是最坏情况下的组合数
注意: 这个时间复杂度 很高,需要很大的代价,不建议使用这个算法
其他简单写法 :待补充
难度 : 困难
我们要找的是某个数后面的第二个比这个数大的数字,我们先思考第一个比这个数字更大的数的情况,显然是单调栈的,思考如果用一个递减的单调栈s
,当枚举一个数字x
时,可能会在栈中弹出一些数字, 这些数字都是比枚举的x
小的(可以自己模拟一下),而且是最接近的(下标),所以这些弹出去的数字右侧的第一个比自己更大的数字就是x
,但是我们怎么来找在x
之后的满足大于前面已经弹出的数字的数呢?我们可以把之前弹出去的数字记录下来,保存到一个新的栈t
中,并且保持这个栈是也递减的,我们枚举x
的时候去查看t
的栈顶top
是否满足x > top
, 如果满足,就说明top
的第二大的数字就是x
,之后把已经top
出栈,把s
新弹出(要保持单调减性)的元素入栈t
,反复操作就可以得到最终的答案数组res
为什么是正确的呢? ( 注意: 非证明,只是说明)
s
栈顶的,并且栈顶是第一个大于他们的数,在下一个nums
中枚举的数字x
时,首先处理的是栈t
中的元素,如果x
是大于t
栈顶时,说明x就是大于t的第二个数为什么t
也要保持单调递减呢?
x
是不是大于栈t
的栈顶,我们要保证小于x
的要全部出栈,所以栈底到栈顶一定是从大到小的,这样能确保小于x
的全部出栈tips: 为了方便处理,栈中存储的是下标,注意下面栈的操作所有的变量表示其实都是该变量的下标
算法的具体实现步骤:
nums
中的每个数字x
x
是不是大于栈t
的栈顶,如果是,就直接记录答案,并且将栈顶出栈,否则就跳过这一步s
栈,就是维护单调递减性质,我们首先找到第一个小于x
的下标idx
,然后将idx
到end
这一段整体插入栈t
的顶部 (因为s
是递减的并且小于x
的已经全部出了栈t
所以直接插入并不会影响栈t
的单调性) ,然后将栈s
顶部出栈,并且将x
入栈栈的数组形式: 我们把数组的左边当作栈底,最右端当作栈顶,入栈就相当于push_back()
,出栈一个元素相当于pop_back()
, 处理的时候把它当作栈处理,操作简便
实现代码:
class Solution {
public:
vector<int> secondGreaterElement(vector<int>& nums) {
int n = nums.size();
vector<int> res(n, -1), s, t;
for (int i = 0; i < n; i ++) {
while (t.size() && nums[t.back()] < nums[i]) {
res[t.back()] = nums[i];
t.pop_back();
}
int j = s.size();
while (j && nums[s[j - 1]] < nums[i]) j --;
t.insert(t.end(), s.begin() + j, s.end());
s.resize(j);
s.push_back(i);
}
return res;
}
};
时间复杂度 O ( n ) O(n) O(n)
代码tips :
res[t.back()]
就不足为奇了t.insert(t.end(), s.begin() + j, s.end());
就是将栈s
中已经出栈的连续元素原封不动的插入t
的末尾resize(n)
函数中,如果 n < size
那么会将n + 1, n + 2...size
这些全部删除,相当于一次pop
全部
总结: