数组--part 1 --二分查找(力扣704/35/34/69/367 )

二分查找的基础

//二分查找的两种实现方式
#include<iostream>
#include<vector>

using namespace std;

// 第一种方法:[left,right]
// const int* p 和int* const p的区别第一种就是限定值 第二种是限定地址
// const int function(){}   int function()const{}  前者返回的是const变量 后者是类内不能进行修改其成员的变量
vector<int>::const_iterator Er_search_one(vector<int>& num,int target)
{
    //初始化
    int left = 0, right = num.size()-1;
    int middle;
    //第一个重点判断是<还是<=,【left,right】,我们拿【1,1】做例子,发现=其实是可以存在的,此时就直接是1
    while (left<=right)
    {
        //记得判断是否溢出
        //由于加法存在溢出可能 middle = (left + right)/2;
        middle = left + (right - left) / 2;
        //第二个重点判断实际上就是判断是该用>还是>=,判断:想着下一次循环下变成【left,middle】,由于middle不是已经判断是不存在了嘛?所以就不需要等于了。
        if (num.at(middle) > target)
            right = middle -1;
        else if (num.at(middle) < target)
            left = middle + 1;
        else
            return num.cbegin() + middle;
    }
    //此处并不完全对,实际上后续可以判断一下是否等于cend 不然直接解引用会有错误。
    return num.cend();
}

// 第二种方法:[left,right)
vector<int>::const_iterator Er_search_two(vector<int>& num,int target)
{
    //初始化
    int left = 0, right = num.size()-1;
    int middle;
    //第一个重点判断是<还是<=,[left,right),我们拿[1,1)做例子,发现=其实是不存在的,此时就无法等于
    while (left < right)
    {
        //记得判断是否溢出
        //由于加法存在溢出可能 middle = (left + right)/2;
        middle = left + (right - left) / 2;
        //第二个重点判断实际上就是判断是该用>还是>=,判断:想着下一次循环下变成[left,middle),由于本来就忽略了middle的判断,所以大胆等于
        if (num.at(middle) > target)
            right = middle;
        else if (num.at(middle) < target)
            left = middle + 1;
        else
            return num.cbegin() + middle;
    }
    //此处并不完全对,实际上后续可以判断一下是否等于cend 不然直接解引用会有错误。
    return num.cend();
}

int main()
{
    vector<int >vec{1,2,3,4,5,6,7,8};
    cout<<"第一种写法:"<<*Er_search_one(vec,4)<<endl;
    cout<<"第二种写法:"<<*Er_search_two(vec,4)<<endl;
    return 0;
}

–写于2023/4/24

leetcode 704二分查找

链接

这里实际上采用的是左闭右闭的方法,返回非迭代器。迭代器版本见上面。

成功代码:

class Solution {
public:
    int search(vector<int>& nums, int target) {
        //采用【1,1】
        int left = 0, right = nums.size()-1, middle;
        while(left<=right)
        {
            middle = left + (right - left) / 2;
            if(nums[middle] < target)
                left = middle + 1;
            else if(nums[middle] > target)
                right = middle - 1;
            else
                return middle;
        }
        return -1;
    }
};

–写于2023/4/25

leetcode 35 搜索插入位置

链接

此题的前期思路很简单,但是最重要的实际上就是关注的是当元素不存在的时候,我们该如何返回相对应的地址。

这里具体介绍一下个人的想法(例子数组[1,3,5,6]):

  1. 首先分类:
    a. target在所有的数组之前,比如target = 0.
    b. target在数组之中,且等于其中的一个值。如target = 1.
    c. target在数组之中,且不等于其中任意一个值。如target =2.
    d. target在数组的之后,比如target = 7.
  2. 然后确定方法,明显二分查找。毕竟直接说是用二分查找的。hh
  3. 然后将上文所述的4中情况进行想一下,对应的答案应该如何用已知的变量进行描述。
    b. 明显不需要进行讨论,直接二分可得。
    a. 我们想象一下[1,3,5,6]进行二分走一遍变成[1,3]此时middle = 1;left = 0;right = 0;然后while循环还满足继续循环,此时middle = 0;left = 0;right =-1;又已知答案该是0,所以可以是left 或者right+1或者middle,见下一起进行统计判断。
    c. 而对于其中的值的话,第一次还是middle = 1;left = 0;right = 0然后此时while条件仍然满足,继续middle = 0;left = 1;right = 0;又已知答案该是1,所以可以是left或者right+1或者middle+1
    d.相同的方式分析,留给读者进行相对应的分析,可得
  4. 总结发现实际上大概的选择其实就是要么left或者right+1或者对应情况分析得到对应middle对应的return。

AC:

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        //采用【1,1】
        int left = 0, right = nums.size()-1, middle = 0;
        while(left <= right)
        {
            middle = left + (right - left) / 2;
            if(nums[middle] < target)
            {
                left = middle + 1;
            }
            else if(nums[middle] > target)
            {
                right = middle - 1;
            }
            else
                return middle;
        }
        return left;
    }
};

–写于2023/4/25

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

链接

实际上简单的思路很容易想出来,可以先处理完成后,再去分情况讨论。
AC:(值得关注的是传入【】空数组的时候的问题)

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int left = 0, right = nums.size() - 1, middle = 0;
        //采用[1,1]的写法
        while(left <= right)
        {
            middle = left + (right - left) / 2;
            if(nums[middle] < target)
                left = middle + 1;
            else if(nums[middle] > target)
                right = middle - 1;
            else
                break;
        }
        vector<int >vec{middle, middle};
        int ltag = middle,rtag = middle;
        if(nums.size() == 0 || nums[middle] != target)
        {
            vec[0] = -1;
            vec[1] = -1;
            return vec;
        }
        //进行再处理
        while((nums[vec[0]]==nums[middle] && vec[0]>0) || (nums[vec[1]]==nums[middle] && vec[1] < (nums.size() - 1)))
        {
            if(vec[0] > 0 && nums[vec[0] - 1] == nums[middle])
                vec[0] -= 1;
            if(vec[1]<nums.size()-1 && nums[vec[1] + 1] == nums[middle])
                vec[1] += 1;
            if (vec[0] == ltag && vec[1] == rtag)
                break;
            ltag = vec[0];
            rtag = vec[1];
        }
        return vec;
    }
};

但问题在于,有没有什么更好的方式去实现这一做法?
我们发现我们是先排序完之后,再次进行情况判断,得出最后的结果,那么,有没有一种情况可以,让我们边取数字,边得到最后的答案吗?
这里就可以去看其对应的题解了,讲的还是挺清楚的,这里就不赘叙了。

–写于2023-4-27

leetcode 69 x的平方根

链接

使用二分法进行查找,此题需要关注的是为什么需要使用unsigned,如果使用int会发生什么。希望读者进行思考一下。

class Solution {
public:
    int mySqrt(int x) {
        //采用二分法求取值,采用【1,1)的写法.由于上限是2的32次方,故右侧范围取不到2的16次方
        unsigned int left = 0, right = 65536, middle = 0;
        unsigned int num = 0;
        //值得关注的是此处不适用等于
        while (left < right)
        {
            //此处需要考虑的是溢出。
            middle = left + (right - left) / 2;
            if (middle * middle < x)
            {
                if (((middle + 1) * (middle + 1)) > x)
                    return middle;
                //经典判断[left,right)中,left需要加1,因为[实际上多一次判断。
                left = middle + 1;
            }
            else if ((middle * middle) > x)
                right = middle;
            else
                return middle;
        }
        return -1;
    }
};

答案:实际上就是在超过上限的时候,会转化为负数。
在这里插入图片描述
当然此题还有别的解法:

这边进行简述一下,官方题解一:实际上就是将计算问题变成数学问题,可以适当的去理解一下。官方题解三:就是在模拟逼近。

写于2023-5-3

leetcode 367 有效的完全平方数

链接

实际上和上一题有异曲同工之妙,实际上上一题理解之后,这一题就简单了。

class Solution {
public:
    bool isPerfectSquare(int num) {
        //此处和之前的那一题一样,为了防止int的溢出,需要使用unsigned 或者longlong
        unsigned int left = 0, right = 65536, middle = 0;
        //此处用【1,1】或者【1,1)都可以,这里采用比较少的【1,1),锻炼一下
        while (left < right)
        {
            middle = left + (right - left) / 2;
            if ((middle * middle) < num)
                left = middle + 1;
            else if ((middle * middle) > num)
                right = middle;
            else
                return true;
        }
        return false;
    }
};

在这里插入图片描述
写于 2023-5-4

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值