地址:962. 最大宽度坡
题目
给定一个整数数组 A
,坡是元组 (i, j)
,其中 i < j
且 A[i] <= A[j]
。这样的坡的宽度为 j - i
。
找出 A
中的坡的最大宽度,如果不存在,返回 0 。
示例
输入:[6,0,8,2,1,5] 输出:4 解释: 最大宽度的坡为 (i, j) = (1, 5): A[1] = 0 且 A[5] = 5.
思路
二分查找
- 关于右边界的选择应该是降序的,比如存在j1,j2,且a[j1] <= a[j2],此时的右边界就该选择j2
- 当a = [0,8,2,7,5]时,对于i=0的候选右边界位置candidates = [(v=5, j=4), (v=7, j=3), (v=8, j=1)]。我们要时刻维护候选列表 candidates 按照索引值降序,对应值升序。
算法实现
- 从右边开始循环降序,这一步就遵循了上面所说的索引值降序的原则,下面只需要保证对应值升序即可。
- 每一次更新i值都要进行二分查找,观察现在遍历到的键值与候选键值的关系
- 如果循环结束的左边指针指向的位置<候选队列的长度,说明在二分查找的过程中候选键中所有键值value都>=当前i指向的数值,由于维护的candidates按照对应值升序的原则,故没有新候选j加入
- 但如果二分查找完毕后左边指针位置=候选列表长度,说明在此过程中存在nums[i]>candidates的所有键值,可以加入
代码
class Solution {
public:
int maxWidthRamp(vector<int>& nums) {
int len = nums.size();
int ans = 0;
vector<vector<int>> cd;
cd.push_back({nums[len-1],len-1});
//候选j降序,值升序的规则
for(int i=len-2;i>=0;--i){ //倒着统计候选位置j
int l = 0,r = cd.size(); //记录的是侯选j的个数
while(l < r){
int mid = l + (r - l) / 2;
if(cd[mid][0]<nums[i]){ //如果没有遇见比候选键大的值就别进来了
l = mid + 1;
}
else
r = mid;
}
if(l < cd.size()){
int j = cd[l][1];
ans = max(ans,j-i);
}
else{
cd.push_back({nums[i],i}); //继续统计降序的j
for(int k=0;k<cd.size();k++){
cout<<cd[k][0]<<" "<<cd[k][1]<<endl;
}
}
}
return ans;
}
};