题目
- 给定一个数组,删除连续子序列后,剩下的元素非降序
- 求要删除的连续子序列的最小长度
思路
-
找到包含开头的非降序子序列,找到包含结尾的非降序子序列,拼接形成最长非降序子序列,中间的就是要删除掉的子序列
-
最近一直做二分,先用了二分的思路,性能仅打败 25%,后面改成了 two pointer
-
二分的思路
- 找到从头开始的非降序连续子序列 0 ~ prefix_index,找到包含结尾的非降序连续子序列,suffix_index ~ arr.size -1,尝试拼接
- 遍历 j suffix_index ~ arr.size -1,用二分找到 i 0 ~ prefix_index 中,arr[i] ≤ arr[j] 的 i 的最大值
- 更新尝试拼接后的最长非降序子序列的长度,记录最大值
-
two pointer
- 拼接的时候,用 two pointer 遍历即可,i 是包含开头的 index,j 是包含结尾的 index,因为拼接需要严格要求 arr[i] ≤ arr[j] 且 arr[i] ≤ arr[i+1] 时 i++,所以 i 和 j 的 ++,可以保证遍历出所有可能性
代码
class Solution {
public:
int upper_bound(int num, int r, vector<int>& arr) {
if (arr[0] > num) return -1;
int l = 0;
while (l < r) {
int mid = ((l + r) >> 1) + ((l + r) & 1);
if (arr[mid] > num) r = mid -1;
else l = mid;
}
return l;
}
int solution1(vector<int>& arr) { // o nlogn
int size = arr.size();
int prefix_index = 0;
for (int i = 1; i < size; i++) {
if (arr[i-1] <= arr[i]) prefix_index++;
else break;
}
int suffix_index = size - 1;
for (int i = size - 2; i >= 0; i--) {
if (arr[i] <= arr[i+1]) suffix_index--;
else break;
}
if (prefix_index >= suffix_index) return 0;
//printf("prefix_index %d suffix_index %d\n", prefix_index, suffix_index);
int ans = max(prefix_index + 1, size - suffix_index);
for (int i = suffix_index; i < size; i++) {
int index = upper_bound(arr[i], prefix_index, arr);
//printf("arr[i] %d index %d\n", arr[i], index);
ans = max(ans, size - i + index + 1);
}
return size - ans;
}
int solution2(vector<int>& arr) { // two pointer o n
int suffix_prefix = arr.size() - 1;
for (int i = arr.size()-2; i >= 0; i--) {
if (arr[i] <= arr[i+1]) suffix_prefix--;
else break;
}
int ans = suffix_prefix;
if (ans == 0) return 0;
int i = 0, j = suffix_prefix, size = arr.size();
for (int i = 0; i < suffix_prefix; i++) {
while (j < size && arr[i] > arr[j]) {
j++;
}
if (j >= size) ans = min(ans, size - i);
ans = min(ans, j - i -1);
if (arr[i] > arr[i+1]) break;
}
return ans;
}
int findLengthOfShortestSubarray(vector<int>& arr) {
//return solution1(arr);
return solution2(arr);
}
};