整数二分

123 阅读4分钟

介绍

二分的本质就是找边界的,至于什么二分查找,二分答案什么的,名字花里胡哨的,但他们的实质都是找边界,所以没有必要分要把他们分开,也不必把他们理解的多么高深。

二分模板

以下模板是他人总结的,该模板避免了众多细节。关于l + r >> 1l + r + 1 >> 1的区别是在出现mid - 1后,如果出现此则使用l + r + 1 >> 1,以免造成边界问题。

bool check(int x) {/* ... */} // 检查x是否满足某种性质

// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;    // check()判断mid是否满足性质
        else l = mid + 1;
    }
    return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

题目

1.704. 二分查找 - 力扣(LeetCode) (难度:简单)

描述:

  • 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1

示例:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

代码:

int search(int* nums, int numsSize, int target) {
    int l = 0,r = numsSize-1;
    while(l<r)
    {
        int mid = (l+r+1)/2;
        if(nums[mid]<= target) l = mid;
        else r = mid-1;
    }
    if(nums[l]!=target) return -1;
    else return l;
}

2.35. 搜索插入位置 - 力扣(LeetCode) (难度:简单)

描述:

  • 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。(请必须使用时间复杂度为 O(log n) 的算法。)

示例:

输入: nums = [1,3,5,6], target = 5
输出: 2

代码:

int searchInsert(int* nums, int numsSize, int target) {
    int l = 0, r = numsSize - 1;
    while(l < r)
    {
        int mid = l + r >> 1;
        if(nums[mid] >= target ) r = mid;
        else l = mid + 1;
    }
    if(nums[l] < target) return l+1;
    else return l;   
}

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

描述:

  • 给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target,返回 [-1, -1]。你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

示例:

示例 1:

输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]

示例 2:

输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]

示例 3:

输入: nums = [], target = 0
输出: [-1,-1]

代码:

int* searchRange(int* nums, int numsSize, int target, int* returnSize) {
    int* ans = (int*)malloc(sizeof(int)*2);
    *returnSize = 2;
    if(numsSize == 0) 
    {
        ans[0] = -1;
        ans[1] = -1;
        return ans;
    }
    int l1 = 0, r1 = numsSize-1;
    while(l1 < r1 )
    {
        int mid = l1 + r1  >> 1;
        if(nums[mid] >= target) r1 = mid;
        else l1 = mid + 1;
    }
    if(nums[l1]== target) ans[0] = l1;
    else ans[0] = -1;
    int l2 = 0, r2 = numsSize - 1;
    while(l2 < r2)
    {
        int mid = l2 + r2 + 1 >> 1;
        if(nums[mid] <= target) l2 = mid;
        else r2 = mid - 1;
    }
    if(nums[l2] == target) ans[1] = l2;
    else ans[1] = -1;
    return ans; 
}

4.69. x 的平方根 - 力扣(LeetCode)(难度:简单)

描述:

  • 给你一个非负整数 x ,计算并返回 x 的 算术平方根 。 由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。 注意: 不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。 示例:

示例 1:

输入: x = 4
输出: 2

示例 2:

输入: x = 8
输出: 2
解释: 8 的算术平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。

代码:

int mySqrt(int x) {
    long long  l = 0, r = x;
    while(l < r)
    {
        long long  mid = (l + r + 1 ) >> 1;
        if(mid * mid <= x ) l = mid;
        else r = mid - 1; 
    }
    return (int)l;
}

注意:

1.int不可以,要用long long。

5.367. 有效的完全平方数 - 力扣(LeetCode)(难度:简单)

描述:

  • 给你一个正整数 num 。如果 num 是一个完全平方数,则返回 true ,否则返回 false 。完全平方数 是一个可以写成某个整数的平方的整数。换句话说,它可以写成某个整数和自身的乘积。 不能使用任何内置的库函数,如  sqrt 。

示例:

示例 1:

输入: num = 16
输出: true
解释: 返回 true ,因为 4 * 4 = 164 是一个整数。

示例 2:

输入: num = 14
输出: false
解释: 返回 false ,因为 3.742 * 3.742 = 143.742 不是一个整数。

代码:

bool isPerfectSquare(int num) {
    long long l = 0, r = num;
    while(l < r)
    {
        long long mid = (l + r + 1) >> 1;
        if(mid * mid <= num) l = mid;
        else r = mid - 1; 
    }
    if( l * l == num) return true;
    else return false;
}