代码随想录|704.二分查找、27.移除元素

数组

  1. 数组在内存中的表现是一段连续的空间,每一个地址都是相同的数据类型
  2. 增加或者"删除"(覆盖)操作,需要移动对应下标"之后"的所有元素

二分法

使用条件:有序数组,无重复元素
循环更改中位数,要注意判断区间在整个循环过程中应当保持一致。
比如[left,right],那么在更改的时候left = middle+1或者right = middle -1。右边界应当为size()-1。并且判断循环结束的条件为left>right
如果为[left,right)那么在更改的时候left = middle +1或者right = middle,右边界此时应当为size(),此时结束的条件为left>=right

leetcode704二分查找

文档讲解:代码随想录
视频讲解:手把手带你撕出正确的二分法 | 二分查找法 | 二分搜索法 | LeetCode:704. 二分查找
状态:方法错误,不能全过

  1. 自己的思路
    用middle和数组长度作为判断和循环终止条件。会出现很多问题,首先是对于头和尾的元素无法考虑进去,且middle是通过数组长度/2得到的整数,可能会丢弃掉某些值

  2. 二分查找思路
    注意到题目中,升序(有序)并且无重复的两个条件,所以可以考虑二分查找方法。
    在使用二分查找方法的过程中,需要注意循环终止条件和中值的变换要保持一致以及相互对应。
    下方为使用[left,right]的方法解决,得到时间复杂度为O(logN)。

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int length = nums.size()-1;
        int left = 0;
        int right = length;
//如果是[left,right),right = length+1 ,left<right , right = middle;

        while(left <= right)
        {
            int middle = (right+left)/2;

            //此时中位数小,说明target在右边,更新left,计算middle
            if(nums[middle] < target)
            {
                left = middle+1;
            }
            else if(nums[middle] > target)
            {
                right = middle-1;
            }
            else
            {
                return middle;
            }
        }
        return -1;
    }
};

leetcode35搜索插入位置

文档讲解:代码随想录
状态:对于头和尾的插入

  1. 思路
    看到有序和无重复,相当二分查找。本题遇上题不一样的是需要将-1变为要插入的位置。
    本题使用的左闭右开的方法,所以我们的判断区间是[left,right)。right即是我们的middle值,也是我们需要插入的位置。
    如果我们要插入的是头部,那么此时的区间为[0,0),如果我们要插入的是尾部那么此时的区间也是[left,right==nums.size()),所以最后返回的下标也是right。
    得到时间复杂度O(logN)
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int length = nums.size();
        int left = 0;
        int right = length;

        while(left < right)
        {
            int middle = (left+right)/2;
            if(nums[middle] < target)
            {
                left = middle+1;
            }
            else if(nums[middle] > target)
            {
                right = middle;
            }
            else
            {
                return middle;
            }
        }
        //如果没有则进行插入操作
        //插入后,后面元素需要向后移动
        //我们需要判断如果target插入到头和尾的时候
        //如果是头[0,0)
        //如果是尾[left,right)
        return right;
    }
};

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

文档讲解:代码随想录
状态:边界状态的判断

  1. 本题最大区别就是存在重复数据了,返回的是一个数组,可以将else中的return变为将开始位置和结束位置添加到数组里面
  2. 我们使用了分别判断左边界和右边界的方法,
    获取左边界,就相当于一直获取右范围,直到右范围对应的值小于了target,如果没有获得左边界的值,那么就说明target超过了这个数组的右端。
    获取右边界,就相当于一直获取左范围,直到左范围对应的值大于了target,如果没有获得右边界的值,那么就说明target小于这个数组的左端。
class Solution {
public:

    //获取左边界,同时判断target大于右边界的情况
    int GetLeftInd(vector<int>& nums, int target){
        int length = nums.size()-1;
        int left = 0;
        int right = length;
        int leftInd  = -2;
        while(left <= right)
        {
            int middle = (left+right)/2;
            if(nums[middle] >= target)
            {
                //leftInd的值会比正确的index小1
                right = middle - 1;
                leftInd = right;
            }
            else
            {
                left = middle+1;
            }
        }
        return leftInd;
    }

    //获取右边界,同时处理target小于左边界的情况
    int GetRightInd(vector<int>& nums, int target){
        int length = nums.size()-1;
        int left = 0;
        int right = length;
        int RightInd;
        while(left <= right)
        {
            int middle = (left+right)/2;
            if(nums[middle] <= target)
            {
                //rightInd的值会比正确的index大1
                left = middle + 1;
                RightInd = left;
            }
            else
            {
                right = middle-1;
            }
        }
        return RightInd;        
    }

public:
    vector<int> searchRange(vector<int>& nums, int target) {
        //定义一个返回数组
        int left = GetLeftInd(nums,target);
        int right = GetRightInd(nums,target);

        //当target小于最左值,或者大于最右值时候
        if(left == -2 || right == -2)
        {
            return {-1,-1};
        }
        else if(right-left>1)
        {
            return {left+1,right-1};
        }
        else
        {
            return {-1,-1};
        }
    }
};

X的平方根

  1. 二分法
    相当于从0到x的整数范围内寻找最大的那个整数a使得a*a<=x。注意到该数组序列是一个升序的,然后无重复的序列。不断的去寻找中间值a。
class Solution {
public:
    int mySqrt(int x) {
        int left = 0;
        int right = x;
        int res = -1;

        while(left <= right)
        {
            int middle = (left+right)/2;
			//long long为leetcode给的数据
            if((long long)middle * middle > x)
            {
                right = middle -1;
            }
            //当middle*middle<=x时,才可能得到正确的整数平方根
            //但还是不够,因为可能得到较小值,直到left和right相遇。
            else
            {
                res = middle;
                left = middle + 1;
            }
        }
        return res;
    }
};

由于题意是middlemiddle <= x的middle所以应当在使用左值返回,如果找middlemiddle>=x的数,那么应当使用right+1返回

有效的完全平方数

  1. 与上题一样,同样使用二分法,只不过需要更改判断条件,只有a*a == x的时候返回true,其余情况返回false。
class Solution {
public:
    bool isPerfectSquare(int num) {
        int left = 0;
        int right = num;

        while(left <= right)
        {
            int middle = (left+right)/2;
            if((long long) middle*middle > num)
            {
                right = middle-1;
            }
            else if((long long) middle*middle < num)
            {
                left = middle+1;
            }
            else
            {
                return true;
            }
        }
        return false;
    }
};

移除元素

文档讲解:代码随想录
视频讲解:《代码随想录》算法视频公开课 (opens new window):数组中移除元素并不容易!LeetCode:27. 移除元素
状态:暴力解法

  1. 暴力解法
    双重循环,第一重循环用于寻找判断当前下标是否与要删除的值相等,第二重循环用于将当前下标的之后的数向前移动,更新数组。
    要注意在第二重循环中,要注意当更新数组后,同时要更新下标值和循环终止的长度。
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int length = nums.size();
        for(int i=0;i<length;i++)
        {
            if(nums[i] == val)
            {
                for(int j = i;j<length-1;j++)
                {
                    nums[j] = nums[j+1];
                }
                i--;//i之后的值下标都减一了
                length--;//数组长度也要减少
            }
        }
        return length;
    }
};
  1. 双指针
    通过一个快指针和慢指针在一个for循环下完成两个for循环的工作
    快指针:寻找新数组的元素,新数组就是不含有目标元素的数组
    慢指针:指向更新,新数组下标的位置

快指针带动慢指针移动

在本题中,
快指针用于判断当前指向的数是否是要删除的数,如果是,移动快指针,保持慢指针。
慢指针用来存储新数组的下标,就是当快指针指向的值不是要删除的值,那么将慢指针指向的值更新为快指针指向的值。
如果是删除的值,那么就不做处理,只是移动快指针。

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int left = 0;
        int right = 0;
        while(right < nums.size())
        {
            if(nums[right] != val)
            {
                nums[left] = nums[right];
                left++;
            }
            right++;
        }
        return left;
    }
};

双指针的解题一般为

//循环体
	//判断,本文是判断不等的所以if不等,如果保留相等的,那么就是if相等
	//判断的条件需要和题目相同即是同一个真命题,如果是否命题那又要用一个循环来判断并且更复杂
		//慢指针移动+题目要求操作,只在满足题目需求是才能移动
	//快指针移动,不论什么情况都要移动
//
  • 20
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值