【第一天| 二分法、双指针】

第一天 | 704. 二分查找、35. 收索插入位置、 34. 在排序数组中查找元素的第一个和最后一个位置、27. 移除元素

704. 二分查找

文章讲解:
视频讲解:

思路: 因为是有序数组,查找目标值,直接二分查找。要注意区间的确定。

 int search(vector<int>& nums, int target) {
        int i =0;
        int j=nums.size();//左闭右开
        while(i<j)//i>=j将没有意义
        {
            int mid = (i+j)/2;//取中间值
            if(nums[mid]>target)//中间值大于target,说明target落在左半区间
            {
                j = mid;//移动右边界j至mid,因为是左闭右开,所以不能是mid-1,不然会漏掉nums[mid-1]这个值
            }
           else if(nums[mid]<target)//中间值小于target,说明target落在右半区间,移动左边界i
            {
                i=mid+1;//左闭右开,nums[mid]已经不等于target,所有从i为mid+1
            }
            else return mid;//如果恰好等于target直接返回
        }
        return -1;
        
    }

35. 搜索插入位置

文章讲解

思路: 本题跟二分查找区别在于target不一定存在于数组中。但是依然可以用 二分法查找找到target应该存在的位置。如果在数组中找到了target直接返回当前索引,如果没有找到target,最后返回右边界就行,因为这是target最应该落入的位置,左边都小于taget,右边都大于target。

为什么一定是右边界而不是左边界:实际上都行, 因为左闭右开区间查找,左边界左边的值都一定小于target。while循环结束后,i一定等于j。而nums[j]一定是大于target的,这就意味着nums[i]==nums[j]>target。 nums[i] 左边的值都一定小于target。所以target插入i或者j都是一样的。

int searchInsert(vector<int>& nums, int target) {
        int i=0;
        int j=nums.size();
        while(i<j)
        {
            int mid=(i+j)/2;
            if(nums[mid]>target)
            {
                j=mid;
            }
            else if(nums[mid]<target)
            {
                i=mid+1;
            }
            else return mid;
        }
        return j;
    }

34. 在排序数组中查找元素的第一个和最后一个位置

文章讲解

思路: 本题找的是一个区间,而不是一个数。所以一开始很难跟二分查找联系起来。但是仔细想想。我们找target的区间,实际上可以拆分为2个任务。
1.找到第一个大于等于target的位置left
2. 找到第一个大于target的位置rightright-1即为最后一个大于target的位置。
最后返回[left, right-1]

如何查找第一个大于target的位置?
在上一题,搜索插入位置,实际上就是查找一个左边小于target右边大于target的位置。那么查找第一个大于target的位置,我们只需要找到一个位置,这个位置的左边的值都小于等于target,右边的值都大于target。

int findborder(vector<int>& nums, int target)
    {
        int l = 0;
        int r = nums.size();
        
        while(l<r)
        {
            int mid = l+((r-l)>>1);
            if( nums[mid]>target )   r= mid;
            else l=mid+1;
        }
        return l;//return r 也可以
    }

如何查找第一个大于等于target的位置?
原理同上,我们只需要找到一个位置,这个位置的左边的值都小于target,右边的值都大于等于target。

int findborder(vector<int>& nums, int target)
    {
        int l = 0;
        int r = nums.size();
        
        while(l<r)
        {
            int mid = l+((r-l)>>1);
            if( nums[mid]>=target) )   r= mid;
            else l=mid+1;
        }
        return l;
    }

将上面2段代码整合一下

int findborder(vector<int>& nums, int target, bool find)//find为true该函数查找第一个等于taregt的位置
    {
        int l = 0;
        int r = nums.size();
        
        while(l<r)
        {
            int mid = l+((r-l)>>1);
            if( (find&&nums[mid]>=target) || nums[mid]>target )   r= mid;
            else l=mid+1;
        }
        return l;
    }

最后判断一下,找到的left存在于数组范围内,且nums[left]==target.因为如果数组中不存在等于target的子串,那么left就是大于等于target的位置,甚至会超出数组范围。

if(left>=0 && left<size && nums[left]==target ) return {left, right};
        return {-1,-1};

整体代码如下:

class Solution {
    int findborder(vector<int>& nums, int target, bool find)
    {
        int l = 0;
        int r = nums.size();
        
        while(l<r)
        {
            int mid = l+((r-l)>>1);
            if( (find&&nums[mid]>=target) || nums[mid]>target )   r= mid;
            else l=mid+1;
        }
        return l;
    }
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int size = nums.size();
        int left = findborder(nums, target, true);
        int right = findborder(nums, target, false)-1;//注意这里减一
        if(left>=0 && left<size && nums[left]==target ) return {left, right};
        return {-1,-1};
    }
};

27. 移除元素

文章讲解

思路: 本题要求原地修改数组。直观的想法就是把数组后面不等于val的元素跟前面等于val的元素对调一下。所以我用首尾指针的方法来实现。

int removeElement(vector<int>& nums, int val) {
        if(nums.size()==0) return 0;//如果为空数组直接返回0
        int i=0;
        int j = nums.size()-1;
        while(i<j)
        {
            while(i<j&&nums[i]!=val)  i++;
            while(i<j&&nums[j]==val)j--;
            if(i<j) swap(nums[i++] , nums[j--]);
        }
        if(nums[i]!=val) i++;//对调后,有一种情况是i==j且nums[i]!=val,需要把i再往前移一格
        return i;
    }

总结

今天前三题都是二分查找。逐渐加深对二分查找的理解。最后一题复习了双指针。

我对二分查找的理解:二分查找查找的是符合划分条件的位置。
例如 704. 二分查找 实际上我可以查找第一个大于等于target的位置。最后判断该位置是否在数组范围内且等于target.

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int i =0;
        int j=nums.size();
        while(i<j)
        {
            int mid = (i+j)/2;
            if(nums[mid]>=target)  j = mid;
            else    i=mid+1;  
        }
        
        if(i<nums.size()&& i>=0&&nums[i]==target) return i;
        return -1;
    }
};
  1. 搜索插入位置 同上,程序可以几乎不变
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int i=0;
        int j=nums.size();
        while(i<j)
        {
            int mid=(i+j)/2;
            if(nums[mid]>=target)	j=mid;
            else    i=mid+1;

        }
        return i;
    }
};
  1. 在排序数组中查找元素的第一个和最后一个位置 多了一步,查找第一个大于target的位置。
class Solution {
    int findborder(vector<int>& nums, int target, bool find)
    {
        int l = 0;
        int r = nums.size();
        
        while(l<r)
        {
            int mid = l+((r-l)>>1);
            if( (find&&nums[mid]>=target) || nums[mid]>target )   r= mid;
            else l=mid+1;
        }
        return l;
    }
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int size = nums.size();
        int left = findborder(nums, target, true);
        int right = findborder(nums, target, false)-1;//注意这里减一
        if(left>=0 && left<size && nums[left]==target ) return {left, right};
        return {-1,-1};
    }
};

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值