704、二分查找
给定一个 n
个元素有序的(升序)整型数组 nums
和一个目标值 target
,写一个函数搜索 nums
中的 target
,如果目标值存在返回下标,否则返回 -1
。
二分查找常用的两种方法:左闭右闭和左闭右开
左闭右闭:
重点在于left和right的关系判定,假如是左闭右闭,那就是[left,right],此时right是可以等于right的。
比如left = 0,right= nums.size()-1;此时left和right都是可以取到的,是左闭右闭。
当target>nums[mid]时,说明在目标值在右半部分,而且肯定不是nums[mid],所以时left = mid+1。
当target<nums[mid]时,说明目标值在左半部分,而且也能肯定,nums[mid]不是目标值,所以right = mid-1;
注意:这里有个要点时求中间值:mid = left+(right-left)/2;这样防止溢出
class Solution {
public:
int search(vector<int>& nums, int target) {
int n = nums.size();
int left = 0,right = n-1;
int mid = 0;
while(left<=right){
mid = left +((right-left)/2);
if(target>nums[mid]){
left = mid+1;
}else if(target <nums[mid]){
right = mid-1;
}else {
return mid;
}
}
return -1;
}
};
左闭右开:
如果是[left,right),那么left就不能存在等于right的可能。比如此时left = 0,right = nums.size().此时就是left可以取到,但right是不能取到的,就是左闭右开。
那么当taget>nums[mid]时,说明目标值在右半部分,而且nums[mid]肯定不是目标值,所以left = mid+1;
当target<nums[mid]时,说明目标值在左半部分,那么由于我们本来就是左闭右开的区间,右边的right本来就不包含right这个数值的,所以我们的right只需要等于mid就可以了,我们的mid本来就不再区间之内,所以就再想上面的那个方法一样再减1了。
当target>nums[mid]时,那就跟之前一样了,说明在目标值在右半部分,而且肯定不是nums[mid],所以时left = mid+1
class Solution {
public:
int search(vector<int>& nums, int target) {
int n = nums.size();
int left = 0,right = n;
int mid = 0;
while(left<right){
mid = left +((right-left)/2);
if(target>nums[mid]){
left = mid+1;
}else if(target <nums[mid]){
right = mid;
}else {
return mid;
}
}
return -1;
}
};
27、移除元素
给你一个数组 nums
和一个值 val
,你需要原地移除所有数值等于 val
的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1)
额外空间并原地修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
方法1:暴力
两层for循环,第一层遍历数组,当找到要删除的元素后,第二层循环让后面的元素往前覆盖一位,实现删除指定元素。注意细节,这里的i--和n--。很明显,时间复杂是O(n^2)
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int n = nums.size();
for(int i = 0;i<n;i++){
if(nums[i] == val){
for(int j = i+1;j<n;j++){
nums[j-1] = nums[j];//向前覆盖
}
i--;//之前的下标i的数被删除,后面的覆盖过来,需要i前移一位,在下次循环中再次验证下标i的新数是不是等于val,不然就漏了
n--;//数组大小也减1
}
}
return n;
}
};
方法2:双指针
快慢指针:这是一个O(n)复杂度的一个方法。快指针遍历,当快指针指向元素不等于目标值时,就把这个元素赋值给慢指针指向的元素,同时慢指针后移;当快指针指向元素是目标元素时,快指针直接下一个,此时慢指针正指向这个目标元素,那么直接让快指针指向的不是目标元素的值去覆盖慢指针指向的目标值,以此来实现删除的目的,并且这个方法没有改变元素的相对位置。vector中的erase函数就是一个类似的O(n)的时间复杂度
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int n = nums.size();
int fast = 0,slow = 0;//fast表示新数组需要的元素的下标,slow表示新数组的最后一个元素的下标值
//就是将新数组所需元素放到新数组的最后面
for(int fast = 0;fast<n;fast++){
if(nums[fast]!= val){
nums[slow] = nums[fast];
slow++;
}
}
}
};