704.二分查找
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
思路讲解:
典型的二分法,二分思路比较简单,注意使用前提:有序数组、无重复元素(有重复也可以使用,但要确定下标)
具体查看代码随想录原文:二分查找思路讲解
代码
左闭右闭(target在[left,right]区间里)
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0;
int right=nums.size()-1;
while(left<=right){
int mid=left+((right-left)/2); //防溢出
if(nums[mid]>target)
right=mid-1;
if(nums[mid]<target)
left=mid+1;
if(nums[mid]==target)
return mid;
}
return -1;
}
};
需要注意三点:
1.不能简单写成 mid=(left+right)/2,当left和right都很大时,可能会溢出。
2. while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
3. if (nums[mid] > target) right 要赋值为 mid - 1,因为当前这个nums[mid]一定不是target,那么接下来要查找的左区间结束下标位置就是 mid - 1
复杂度:T = O(logn) M = O(1)
左闭右开(target在[left,right)里)
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0;
int right=nums.size();
while(left<right){
int mid=left+((right-left)/2); //防溢出
if(nums[mid]>target)
right=mid;
if(nums[mid]<target)
left=mid+1;
if(nums[mid]==target)
return mid;
}
return -1;
}
};
1.while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
2.if (nums[mid] > target) right 更新为 mid,因为当前nums[mid]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为mid,即:下一个查询区间不会去比较nums[mid]。
复杂度:T = O(logn) M = O(1)
27.移除元素
- 给你一个数组
nums
和一个值val
,你需要原地移除所有数值等于val
的元素,并返回移除后数组的新长度。 - 不要使用额外的数组空间,你必须仅使用
O(1)
额外空间并原地修改输入数组。 - 元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
思路讲解
这道题目重点在于理解数组的覆盖原则:如果数值等于val,那我们就可以直接将其进行覆盖,从而可以达成目标。具体覆盖有两种方法:暴力法--当判断该值是val时,将val后面的值前移一位进行覆盖。和双指针法:使用两个指针,第一个指针不断对数组进行遍历,寻找不为val的元素第二个指针对满足目标的元素进行存储,并对数组下标进行更新。
具体查看代码随想录原文:移除元素思路讲解
代码
暴力法
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int i=0;
int j=0;
int size=nums.size();
for(i;i<size;i++){
if(nums[i]==val){
for(j=i+1;j<nums.size();j++){
nums[j-1]=nums[j]; //集体右移一位
}
i--;
size--;
}
}
return size;
}
};
复杂度:T = O(n^2) M = O(1)
双指针法
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int findex=0; //快指针
int sindex=0; //慢指针
for(findex=0;findex<nums.size();findex++){
if(nums[findex]!=val){
nums[sindex]=nums[findex];
sindex++;
}
}
return sindex; //新长度
}
};
注意:典型的双指针法:通过一个双指针在一个for循环中完成了两个for循环的任务。 从而降低了时间复杂度。
快指针:目标数组的下一个需要判断的值
慢指针:目标数组的下一个需要添加的长度
复杂度:T = O(n) M = O(1)
附加题
35.搜索插入位置
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int left=0;
int right=nums.size()-1;
int mid=0;
while(left<=right){
mid=left+(right-left)/2;
if(nums[mid]>target)
right=mid-1;
if(nums[mid]<target)
left=mid+1;
if(nums[mid]==target)
return mid;
}
return left;
}
};
复杂度:T = O(nlogn) M = O(1)
34. 在排序数组中查找元素的第一个和最后一个位置
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int left=0;
int right=nums.size()-1;
while(left<=right){
int mid=left+(right-left)/2;
if(nums[mid]>target)
right=mid-1;
if(nums[mid]<target)
left=mid+1;
if(nums[mid]==target)
{
int leftindex=mid;
int rightindex=mid;
while(leftindex>0&&nums[leftindex]==nums[leftindex-1])
leftindex--; //找左边界
while(rightindex<nums.size()-1&&nums[rightindex]==nums[rightindex+1])
rightindex++; //找右边界
return{leftindex,rightindex};
}
}
return{-1,-1};
}
};
复杂度:T = O(nlogn) M = O(1)