力扣数组之二分法解题

前言:本博客用于更新力扣的数组模块,关于二分法的题解。

1.数组的关键点

连续的地址空间,所以两要素必不可少:首地址+大小,任何时候都要注意此两要素,首地址+大小。

力扣704. 二分查找 - 力扣(LeetCode)

二分查找顾名思义就是采用二分法来查找有序数组,说人话就是一半一半的查找,其基本思维就是四个大字:不重不漏!!!即通过正确划分边界,来确保二分查找的时候不重不漏!

解题思路:二分法步骤

①while循环当中每次求解下标为middle的值与target进行大小对比;

②根据middle的值和target的对比,更新left,right和middle;

③其中更新right的时候,就需要注意边界问题,也就是左闭右闭还是左闭右开

C语言实现

//二分法两种确定边界的方法:左闭右闭,左闭右开。
//左闭右闭
int search(int* nums, int numsSize, int target) //[left,right]
{
    //二分法定义边界left,right,是左闭右闭[left,right],还是左闭右开[left,right),取决于right包括不包括numsSize
    int left = 0;
    int right = numsSize - 1;
    int middle = 0;
    //二分法的循环条件定义,左闭右闭,还是左闭右开来定义while循环体的条件
    while (left <= right) 
    {
        //二分法的具体实现
        middle = (left + right) / 2;
        if (target < nums[middle]) 
        {
            right = middle - 1;
        } 
        else if (target > nums[middle]) 
        {
            left = middle + 1;
        } 
        else
            return middle;
    }
    return -1;
}

C++实现

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

       return -1;
    }
};

C和C++对比

都是常规思路,没有引入容器

在C++的实现过程中需要注意的是数组的求大小公式:nums.size()

力扣:69. x 的平方根 - 力扣(LeetCode)

审题:求解x的算术平方根,要求不能使用内置函数和**运算符来求解。首先写出数学公式:ans * ans = x,现在的要求是求解ans,有一种数学思想是,(无限逼近 ≈ 等于),其实无限逼近就可以看作是等于,那么我们之前在一个有序的数组当中查找一个元素的时候,是不是也是通过移动左右边界,然后求解这个无限逼近的值就是最终的值呢?所以这个题同样可以采用二分法来求解,主要思维就是:二分法的另一核心思想之(无限逼近 ≈ 等于)

解题思路:二分法步骤

和上述的二分法一模一样,直接看代码注释就ok

C语言实现

int mySqrt(int x) 
{
    int left = 0;
    int right = x;
    int ans = -1;
    while(left <= right)
    {
        int middle = left + (right - left) / 2; //防止下标溢出
        if((long long)middle * middle <= x) //long long 是防止middle的平方溢出int类型
        {
            ans = middle; //求取middle无限逼近middle * middle = x
            left = middle + 1; //移动left使其找见无限逼近最终值的middle
        }
        else
        {
            right = middle - 1;
        }
    }
    return ans;
}

C++实现

同C

力扣35. 搜索插入位置 - 力扣(LeetCode)

审题:在一个有序数组里面,查找是否存在target,如果存在返回下标;如果不存在,返回其插入这个有序数组当中的位置(下标)。

解题思路:二分法

首先看到题目,第一反应是遍历这个数组,在其中找是否存在这样的target,然后返回相应的下标,但是当看到如下两点的时候,就可以考虑使用二分法:在一个有序数组当中;查找一个目标值,注意是一个

二分法步骤回顾:

①定义三个变量:头部head,中部middle,尾部tail;、

②while循环(head <= tail)左闭右闭,(head < tail)左闭右开;

③通过比较middle和target的值,进行head和tail的二分收缩。

细节注意:左闭右闭和左闭右开两种对应的while和tail的更新不同,因为要做到不重不漏!

C语言实现

int searchInsert(int* nums, int numsSize, int target) 
{
    //二分法三个变量:head,middle,tail;然后判断middle是否就是target;
    int head = 0;
    int tail = numsSize - 1; //左闭右闭(因为tail是数组当中的最后一个元素)
    int middle = 0;
    while(head <= tail)
    {
        middle = (head + tail) / 2;
        if(nums[middle] > target)
        {
            tail = middle - 1; //因为tail包含在里面,是右闭模型,所以不能再次包含
        }
        else if(nums[middle] < target)
        {
            head = middle + 1; //同上
        }
        else
        {
            return middle;
        }
    }

    return head; //while当中是head <= tail的时候,按理来说是head + 1,但是力扣上面head可以过,有点疑惑
//因为head的值更新是head = middle + 1;所以此时当head = tail 的时候,head已经是+1了,所以直接return head,而不是head + 1;
}

C++实现

完全同C,只是numsSize = nums.size()

 力扣34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)

审题:在一个递增数组当中,寻找与目标值target相同的开始位置和结束位置,然后用数组的形式返回这两个位置。

解题思路:二分法

在一个有序的数组当中查找元素,有序和查找,满足二分法的条件,但是这个题有个特点是查找多个元素,那么查找多个元素怎么处理呢,因为之前接触的都是查找一个元素?

这个题其实细究的话是查找两个元素一个最左边的,一个最右边的,所以基本思路是二分法,然后一个一个的查找,具体思路如下:

①二分法分两步进行,也就是定义两个函数,先找重复元素最左边的下标;

②再找重复元素最右边的下标;

③最后将两个下标存进数组当中返回。

重点:当找见第一个左边界之后,tail开始收缩至middle -1处,继续用二分法逻辑找下一个左边界;当找见一个右边界之后,head开始收缩至middle +1处,继续用二分法找下一个有边界;同时记录每次等于target的边界值

C语言实现

//难点:之前接触的二分法都是找一个元素,这个是找多个元素,二分法如何找多个元素呢?采用两个二分法,一个二分法找重复target最左边的,一个找target最右边的。
//二分法找左边界
int searchLeft(int* nums, int numsSize, int target)
{
    //二分基本步骤,头,中,尾
    int head = 0;
    int tail = numsSize - 1; //右闭
    //定义最左边的初始值是-1
    int leftBorder = -1;
    while(head <= tail)
    {
        int middle = (head + tail) / 2;
        //当找到一个后,移动尾部tail,继续收缩,知道找见数组最左边的target对应的下标
        if(nums[middle] == target)
        {
            //先记录leftBorder
            leftBorder = middle;
            //收缩大数组的尾部继续寻找
            tail = middle - 1;
        }
        else if(nums[middle] < target)
        {
            head = middle + 1;
        }
        else
        {
            tail = middle - 1;
        }
    }
    return leftBorder;
}
//右边界同理:找见第一个右边的之后,head开始收缩,继续找,直到找见最后一个,没找见就是-1
int searchRight(int* nums, int numsSize, int target)
{
    //二分基本步骤,头,中,尾
    int head = 0;
    int tail = numsSize - 1; //右闭
    //定义最右边的初始值是-1
    int rightBorder = -1;
    while(head <= tail)
    {
        int middle = (head + tail) / 2;
        //当找到一个后,移动头部head,继续收缩,知道找见数组最右边的target对应的下标
        if(nums[middle] == target)
        {
            //先记录rightBorder
            rightBorder = middle;
            //收缩大数组的头部继续寻找
            head = middle + 1;
        }
        else if(nums[middle] < target)
        {
            head = middle + 1;
        }
        else
        {
            tail = middle - 1;
        }
    }

    return rightBorder;
}


int* searchRange(int* nums, int numsSize, int target, int* returnSize) 
{
    int leftBorder = searchLeft(nums, numsSize, target);
    int rightBorder = searchRight(nums, numsSize, target);

    //开辟存放左右边界的数组,大小为2;
    *returnSize = 2;
    int * returnArr = (int*)malloc(2 * sizeof(int));
    returnArr[0] = leftBorder;
    returnArr[1] = rightBorder;

    return returnArr;
}

C++

打完球来写

### 关于 LeetCode 上与数组长度相关的函数或解法 在 LeetCode 平台上处理数组问题时,`length` 属性通常用于获取数组的大小。以下是具体使用方法以及一些常见场景下的代码示例。 #### 获取数组长度的方法 在 Python 中,可以通过 `len()` 方法来获取数组(列表)的长度[^3]。而在 Java 中,则通过 `.length` 属性访问数组的长度[^2]。 --- #### 示例 1: 删除数组中的重复项并返回新长度 这是一个经典的题目,在有序数组中删除重复项,并返回去重后的有效长度。下面分别给出 Python 和 Java 的实现: ##### Python 实现 ```python class Solution: def removeDuplicates(self, nums): if not nums: return 0 slow = 1 for fast in range(1, len(nums)): if nums[fast] != nums[fast - 1]: nums[slow] = nums[fast] slow += 1 return slow ``` 上述代码利用双指针技术完成操作,其中 `len(nums)` 被用来计算原始数组的长度。 ##### Java 实现 ```java public int removeDuplicates(int[] nums) { if (nums.length == 0) return 0; int slow = 1; for (int fast = 1; fast < nums.length; fast++) { if (nums[fast] != nums[fast - 1]) { nums[slow] = nums[fast]; slow++; } } return slow; } ``` 这里同样依赖於 `nums.length` 来判断数组的实际尺寸。 --- #### 示例 2: 移除指定值并调整数组长度 另一个常见的问题是移除数组中的某个特定值,并返回新的有效长度。下面是对应的解决方案: ##### Python 实现 ```python class Solution: def removeElement(self, nums, val): count = 0 for i in range(len(nums)): if nums[i] != val: nums[count] = nums[i] count += 1 return count ``` 此代码片段展示了如何通过计数器变量 `count` 记录保留下来的元素数量,而 `len(nums)` 则提供了初始数组的总长度。 ##### Java 实现 ```java public int removeElement(int[] nums, int val) { int count = 0; for (int i = 0; i < nums.length; i++) { if (nums[i] != val) { nums[count++] = nums[i]; } } return count; } ``` 在这里也运用到了 `nums.length` 进行循环控制。 --- #### 注意事项 - **原地修改**: 需要注意的是,LeetCode 的某些题目要求对输入数组进行「原地」修改,这意味着不允许额外分配空间存储结果数组[^4]。 - **时间复杂度**: 大多数情况下,这类问题的时间复杂度应保持在线性级别 O(n),因此需谨慎设计算法逻辑[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值