数组
二分
在循环中 始终坚持根据查找区间的定义来做边界处理。
第一种写法,我们定义 target 是在一个在左闭右闭的区间里,也就是[left, right] (这个很重要非常重要)。
区间的定义这就决定了二分法的代码应该如何写,因为定义target在[left, right]区间,所以有如下两点:
while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
class Solution {
public:int search(vector<int>& nums, int target) {int left = 0;int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]while (left <= right) { // 当left==right,区间[left, right]依然有效,所以用 <=int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2if (nums[middle] > target) {right = middle - 1; // target 在左区间,所以[left, middle - 1]} else if (nums[middle] < target) {left = middle + 1; // target 在右区间,所以[middle + 1, right]} else { // nums[middle] == targetreturn middle; // 数组中找到目标值,直接返回下标}}// 未找到目标值return -1;}
};
27. 移除元素
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素
数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。(平时是封装了,调用了一层接口:.earse()
是O(n)的,后面的元素逐个前移)
26.删除有序数组中的重复项
给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。要求删除重复元素,实际上就是将不重复的元素移到数组的左侧。
比较 p 和 q 位置的元素是否相等。
如果相等,q 后移 1 位
如果不相等,将 q 位置的元素复制到 p+1 位置上,p 后移一位,q 后移 1 位
重复上述过程,直到 q 等于数组长度。
class Solution {
public:int removeDuplicates(vector<int>& nums) {int p=0,q=0;while(q<nums.size()){if(nums[p]==nums[q]) q++;else{nums[p+1]=nums[q];p++;q++;}}return p+1;}
};
283.移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
class Solution {
public:void moveZeroes(vector<int>& nums) {int p=0,q=0;while(q<nums.size()){if(nums[p]==0 && nums[q]!=0) swap(nums[p],nums[q]),p++,q++;else if(nums[p]==0 && nums[q]==0) q++;else p++,q++;}}
};
977.有序数组的平方
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
分界是 第一个非零的数。双指针实现归并排序
class Solution {
public:vector<int> sortedSquares(vector<int>& nums) {int reg = 0;while(reg < nums.size()){if(nums[reg]>=0) break;else{reg ++;}}vector<int> res;//[0,reg-1]减 [reg,size]增int p = reg-1,q = reg;while(p>=0 && q<nums.size()){if(pow(nums[p],2) <= pow(nums[q],2)) res.push_back(pow(nums[p],2)),p--;else res.push_back(pow(nums[q],2)),q++;}while(p>=0) res.push_back(pow(nums[p--],2));while(q<nums.size()) res.push_back(pow(nums[q++],2));return res;}
};
209.长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [ n u m s l , n u m s l + 1 , . . . , n u m s r − 1 , n u m s r ] [nums_l, nums_{l+1}, ..., nums_{r-1}, nums_r] [numsl,numsl+1,...,numsr−1,numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
- 超时了,前缀和+二分
class Solution { public:int midsearch(vector<int> a,int l,int r,int target){if (a[r] < target) return -1;while(l<r){int mid = (l+r)>>1;if(a[mid]>=target) r=mid;else if(a[mid]<target) l=mid+1;}return l;}int minSubArrayLen(int s, vector<int>& nums) {int n = nums.size();if(n==0) return 0;vector<int> sums(n+1,0);for(int i=1;i<=n;i++)sums[i]=sums[i-1]+nums[i-1];int ans = INT_MAX;for(int i=1;i<=n;i++){int target = s + sums[i-1];int bound = midsearch(sums,1,n,target);if(bound !=-1)ans = min(ans,bound-(i-1));}return ans == INT_MAX ? 0 : ans;} };
- 滑动窗口:滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)
循环的索引,一定是表示 滑动窗口的终止位置class Solution { public:int minSubArrayLen(int s, vector<int>& nums) {int l=0,r=0;int tmp=0;int res = INT_MAX;while(r<nums.size()){//循环是rtmp += nums[r];//窗口都是以r加的while(tmp>=s){res=min(res,r-l+1);//更新结果tmp-=nums[l];//退l,l++l++;} r++;}return res==INT_MAX?0:res;} };
不要以为for里放一个while就以为是O(n^2)啊, 主要是看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)。
904. 水果成篮
你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。
你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:
你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。
给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。
class Solution {
public:int totalFruit(vector<int>& fruits) {int l=0,r=0;int ans=0;unordered_map<int,int> box; while(r<fruits.size()){box[fruits[r]]++;//窗口都是以r加的while(box.size()>2){auto it=box.find(fruits[l]);it->second--;if(it->second==0) box.erase(it);l++;}ans=max(ans,r-l+1);r++;}return ans;}
};