二分查找的条件:1、元素有序,2、无重复元素
如果有重复元素会导致返回的下标可能不唯一。
二分查找的步骤:
定义两个指针,一个为左指针left,一个为右指针right。
左指针指向数组的开始,也就是下标为0的位置,右指针可以指向数组的最后一个元素,也可以指向NULL,这个涉及到边界问题。本题指向数组的最后一个元素,也就是下标为数组长度-1的位置。
求左右指针的中点mid,也就是左指针+右指针除2,比较target与数组在中点mid的值nums[mid]。如果target=nums[mid]则直接返回mid,如果target>nums[mid],则意味着要查找的值在数组下标mid和right之间,所以直接调整左指针的位置到mid+1。同理,如果target<nums[mid],则意味着要查找的值在数组下标left和mid之间,所以直接调整右指针的位置到mid-1。
具体过程如下图:
代码如下:
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size()-1;
while(right>=left)
{
int mid= (left+right)/2;
if(target==nums[mid])
{
return mid;
}
else if(target>nums[mid])
{
left = mid+1;
}
else
{
right = mid-1;
}
}
return -1;
}
};
注意事项:循环的边界问题。
大家写二分法经常写乱,主要是因为对区间的定义没有想清楚,区间的定义就是不变量。要在二分查找的过程中,保持不变量,就是在while寻找中每一次边界的处理都要坚持根据区间的定义来操作,这就是循环不变量规则。-------------代码随想录
写二分法,区间一般定义为两种,左闭右闭也就是[left,right],或者左闭右开也就是[left,right)。
也就是我上面所说的,右指针指向哪里的问题。如果右指针指向数组的最后一个元素也就是nums.size()-1的位置,就是左闭右闭得情况。如果右指针指向数组最后一个元素后的空集,也就是左闭右开的情况。
左闭右闭的情况,因为target在[left,right]之间,所以循环条件要使用left<=right,因为left=right是有意义的。
左闭右开的情况,因为target在[left,right)之间,所以循环条件要使用left<right,因为left=right没意义。
看了题目,第一时间的想法就是暴力解法,直接两个for循环。
因为是要移除数组的元素,所以移除的操作其实为覆盖操作,就比如要移除下标为i的元素,其实就是要i后面的元素一直往前覆盖。因为数组是连续的一片存储空间。
暴力解法:
两个for循环,第一个for循环直接遍历整个数组,第二个for循环做移除(覆盖)元素的操作。
用暴力解法没什么难度,需要注意的是,熟练掌握移除数组元素的操作,也就是第二个for循环的操作。
还有就是每次移除完元素记得将数组长度-1,又因为i之后的元素都向前移了一位,所以记得把i也-1,不然可能会漏掉元素。
代码如下:
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int len = nums.size();
for(int i = 0;i<len;i++)
{
if(nums[i]==val)
{
for(int j =i;j<len-1;j++)
{
nums[j] = nums[j+1];
}
i--;
len --;
}
}
return len;
}
};
暴力解法没什么难度,双指针法是真的奇妙
双指针法
本题使用双指针法的解法是:
定义两个指针,一个快指针,一个慢指针,两个指针都从数组的第一个元素也就是0号下标以相同的速度开始移动。当指针所指的值与val相同时,快指针继续移动,而慢指针不动。
这里最神奇的是移动过程,它并不是下标移动,而是指针所指的值移动,也就是说每次移动都是一个赋值的过程,在没遇到第一个val之前,将快指针指向的值一直赋给慢指针指向的值,因为它们是以相同的速度移动,所以这个值其实一直没变。
直到遇到了val,这时候,快指针会继续移动,而慢指针会停下,可以想象,这个移动的过程会变成移除元素的过程。因为快指针继续向前走,而慢指针会停顿,所以这时候快指针给慢指针赋值就会变成后面元素覆盖前面元素的过程,就成了数组移除元素的过程。
代码如下:
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slow = 0;
for(int fast = 0;fast<nums.size();fast++)
{
if(val!=nums[fast])
{
nums[slow++] = nums[fast];
}
}
return slow;
}
};