先看最简单的:删除有序数组中的重复项
. - 力扣(LeetCode)
给你一个 非严格递增排列 的数组 nums
,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums
中唯一元素的个数。
考虑 nums
的唯一元素的数量为 k
,你需要做以下事情确保你的题解可以被通过:
- 更改数组
nums
,使nums
的前k
个元素包含唯一元素,并按照它们最初在nums
中出现的顺序排列。nums
的其余元素与nums
的大小不重要。 - 返回
k
。
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int left=0,right=1;
while(right<nums.size())
{
if(nums[left]!=nums[right])
{
nums[left+1]=nums[right];
left++;
right++;
}
else{
right++;
}
}
return left+1;
}
};
想法是使用滑动窗口,左右两个指针,初始时左指针指向0右指针指向1,若两指针不等,则将右边复制到左边。左右指针各进一步。
若两指针相等,则只需将右边指针右移指向下一个即可。
最后left加一即为新数组的长度。
该原地去重思想后面会用到。
进阶题一:
给你一个长度为 n
下标从 0 开始的数组 nums
,数组中所有数字均为非负整数。对于 0
到 n - 1
之间的每一个下标 i
,你需要找出 nums
中一个 最小 非空子数组,它的起始位置为 i
(包含这个位置),同时有 最大 的 按位或运算值 。
- 换言之,令
Bij
表示子数组nums[i...j]
的按位或运算的结果,你需要找到一个起始位置为i
的最小子数组,这个子数组的按位或运算的结果等于max(Bik)
,其中i <= k <= n - 1
。
一个数组的按位或运算值是这个数组里所有数字按位或运算的结果。
请你返回一个大小为 n
的整数数组 answer
,其中 answer[i]
是开始位置为 i
,按位或运算结果最大,且 最短 子数组的长度。
子数组 是数组里一段连续非空元素组成的序列。
思路:
附上代码:
class Solution {
public:
vector<int> smallestSubarrays(vector<int> &nums) {
int n = nums.size();
vector<int> ans(n);
vector<pair<int, int>> ors; // 按位或的值 + 对应子数组的右端点的最小值
for (int i = n - 1; i >= 0; --i) {
ors.emplace_back(0, i);//小集合总是在后面插入的,因此ors是递减的
for (int j = 0; j < ors.size(); ++j)
{//更新集合,把nums[i]插入集合
ors[j].first |= nums[i];
}
int j=0,k=0;//原地去重的两个指针,j用来遍历,k用来记录已去重的最后一个元素下标,方便比较
for(;j<ors.size();j++)
{//原地去重,和421相同,集合更新保证ors的或值是递减的(不一定是严格递减的,可能有重复,所以需要去重)
if (ors[k].first != ors[j].first)
{//不重复,先递增k,因为k的定义和421有一点不同,再记录下来
ors[++k] = ors[j];
}
else //重复了,只记录最小的下标,k不递增
ors[k].second = ors[j].second; // 合并相同值,下标取最小的
}
ors.resize(k + 1);//k是已去重的最后一个元素下标,因此保留k+1个元素
// 本题只用到了 ors[0],如果题目改成任意给定数字,可以在 ors 中查找
ans[i] = ors[0].second - i + 1;
}
return ans;
}
};
进阶题一:
给你一个 非负 整数数组 nums
和一个整数 k
。
如果一个数组中所有元素的按位或运算 OR
的值 至少 为 k
,那么我们称这个数组是 特别的 。
请你返回 nums
中 最短特别非空 子数组的长度,如果特别子数组不存在,那么返回 -1
。
示例 1:
输入:nums = [1,2,3], k = 2
输出:1
解释:
子数组 [3]
的按位 OR
值为 3
,所以我们返回 1
。
示例 2:
输入:nums = [2,1,8], k = 10
输出:3
解释:
子数组 [2,1,8]
的按位 OR
值为 11
,所以我们返回 3
。
示例 3:
输入:nums = [1,2], k = 0
输出:1
解释:
子数组 [1]
的按位 OR
值为 1
,所以我们返回 1
。
class Solution {
public:
int minimumSubarrayLength(vector<int> &nums, int k) {
int ans = INT_MAX;
vector<pair<int, int>> ors; // 保存 (右端点为 i 的子数组 OR, 该子数组左端点的最大值)
for (int i = 0; i < nums.size(); i++) {
ors.emplace_back(0, i);
int j = 0;
for (auto &p : ors) {
auto &[or_, left] = p;
or_ |= nums[i];
if (or_ >= k) {
ans = min(ans, i - left + 1);
}
if (ors[j].first == or_) {
ors[j].second = left; // 原地去重:合并相同值,左端点取靠右的
} else {
ors[++j] = p;
}
}
ors.resize(j + 1); // 去重:移除多余元素
}
return ans == INT_MAX ? -1 : ans;
}
};
使用了第二题的模板,只需在内层循环中添加一个 if
即可:如果子数组 OR 值大于等于 kkk,更新子数组长度的最小值。