Leetcode刷题:二分查找

本文详细介绍了二分查找算法的原理和实现过程,包括如何确定边界、二分条件以及区间收缩策略。通过LeetCode题目实例,阐述了二分查找在不同场景下的应用,如搜索插入位置、查找元素范围等。同时,讨论了处理重复数据和边界情况的技巧,并提供了多种问题的解决方案。此外,还提及了有效完全平方数判断和平方根计算等拓展问题,强调了在实际编程中如何优化二分查找算法。
摘要由CSDN通过智能技术生成

结合leetcode题目整理了学习二分查找的过程和理解。
​二分本质上是求第一个满足某条件的数。如果题目具有二分性质,也就是所谓的题目具有单调性质。

每次写之前要考虑:

  1. 二分的边界
  2. 二分条件怎么写
  3. left和right怎么收缩
  4. 多push一个inf会不会更好。(本质上来讲,就是考虑二分的边界内是不是一定存在答案,如按插入一个数时插入数大于数组中所有数字)

关于nums.push_back(inf):
如果数列后加一个inf,变成{1,2,3,3,4,inf},大于等于target的第一个就是4号位置,大于等于target+1第一个就是5号位置。一些题目中更简单
inf即题目中数据范围的最大值

cpp封装了一些现成的函数,在algorithm里,lower_bound和upper_bound分别是找大于等于第一个和大于第一个,小于最后一个和小于等于最后一个分别是lower_bound-1和upper_bound-1

要注意的问题

  1. 首先确定区间的边界条件。[left, right],右侧使用闭区间更不易出错
    while(left <= right) 的终止条件是 left == right + 1,写成区间的形式就是 [right + 1, right],这时候区间为空。
    移动方式:left = mid + 1; right = mid - 1;

  2. 确定要找大于等于某个值的最小值/小于等于某个值的最大值/…

  3. 注意有重复数据时的情况

二分框架

int left, right, ans;
while (left <= right) {
	int mid = (left + right) / 2;
	if () {
		left = mid + 1;
	} else {
		right = mid - 1;
		ans = mid;
	}
}
return ans;

704. 二分查找

704. 二分查找
在这里插入图片描述

寻找大于等于target的最小值/小于等于target的最大值。然后做判断,如果该值等于target,返回位置,否则返回-1
这里用的方法是小于等于target的最大值,把等号和ans放在left一边

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

35. 搜索插入位置

35. 搜索插入位置
在这里插入图片描述

二分查找时间复杂度为O(log n)
本题实际上是判断大于等于target的最小值的位置,所以等号放在right一边,ans也放在right一边
需要多判断是否超出原数组长度(也可以在最后插入inf)

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

以本题为例,说明为什么ans会放在right一侧:
落在右边分区的mid值要么满足nums[mid]大于target,要么满足nums[mid]等于target,所以ans一定在这个分支内;而左边部分只会有nums[mid] < left,答案一定不在其中。
如果求大于等于target的最小值,就把ans放在大于等于的条件下。
在这里插入图片描述
假如a到b这个区间内存在那个等于的答案x,所有的x到b都满足大于等于x,所有的a到x都满足小于等于x,而二分就是找到x这个位置,或者x的左边一个,或者x的右边一个。

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

34. 在排序数组中查找元素的第一个和最后一个位置
在这里插入图片描述

开始位置:大于等于target的最小值
结束位置:小于等于target的最大值/大于target的最小值-1

class Solution { 
public:
	int binarySearch(vector<int>& nums, int target, bool lower) {
		int left = 0;
        int right = nums.size() - 1;
        int ans = nums.size();
        nums.push_back(1e9);
		while (left <= right) {
   		int mid = (left + right) / 2;
   		if (nums[mid] > target || (lower && nums[mid] >= target)) {
   			right = mid - 1;   //右边界左移
   			ans = mid;
   		} else {
   			left = mid + 1;   //左边界右移
   		}
   	}
   	return ans;
   	}

   	vector<int> searchRange(vector<int>& nums, int target) {
   		int leftIndex = binarySearch(nums, target, true);
   		int rightIndex = binarySearch(nums, target, false) - 1; //求右边界时实际上求的是第一个大于target的值,故减一
   		if (leftIndex <= rightIndex && rightIndex < nums.size() && nums[leftIndex] == target &&nums[rightIndex] == target) {
   			return vector<int> {leftIndex, rightIndex};
   		}
   		return vector<int> {-1, -1};
   }
};

69. x 的平方根

69. x 的平方根
在这里插入图片描述

求小于等于sqrt(x)的最大值。
注意mid * mid可能超出int范围,强制转换为long long

class Solution {
public:
    int mySqrt(int x) {
        int left = 0;
        int right = x;
        int mid, ans;
        while (left <= right) {
            mid = left + (right - left) / 2;
            if ((long long)mid * mid  <= x) {
                left = mid + 1;
                ans = mid;
            } else {
                right = mid - 1;
            }
        }
        return ans;
    }
};

367. 有效的完全平方数

367. 有效的完全平方数
在这里插入图片描述

可以找大于等于的最小值,最后判断是否相等

class Solution {
public:
    bool isPerfectSquare(int num) {
        int left = 1;
        int right = num;
        int mid;
        int ans;
        while (left <= right) {
            mid = left + (right - left) / 2;
            if ((long long)mid * mid < num) {
                left = mid + 1;
            } else {
                right = mid - 1;
                ans = mid;
            }
        }
        return (long long)ans * ans == num? true: false;
    }
};

也可以:

class Solution {
public:
    bool isPerfectSquare(int num) {
        int l=1,r=num;
        while(l<=r)
        {
            int mid=l+(r-l)/2;
            if(1ll*mid*mid<num)l=mid+1;
            else
            {
                r=mid-1;
                if(1ll*mid*mid==num)return true;
            }
        }
        return false;
    }
};

1ll就是一个longlong类型的1,一个longlong类型乘int类型,会把int类型强转成longlong类型,然后再乘,所以不会溢出。

其他

  1. 给定一个按照升序排列的整数数组 nums,和一个目标值 target,找到大于target的最小值

  2. 给定一个按照升序排列的整数数组 nums,和一个目标值 target,找到小于等于target的最大值

  3. 给定一个按照降序排列的整数数组 nums,和一个目标值 target,找到大于等于target的最大值

  4. 给定一个按照降序排列的整数数组 nums,和一个目标值 target,找到小于等于target的最小值

12问:
在这里插入图片描述
34问不需要二分

补充知识

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值