Leetcode刷题:二分查找

结合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

文章目录

    • 要注意的问题
    • 二分框架
    • 704. 二分查找
    • 35. 搜索插入位置
    • ⭐34. 在排序数组中查找元素的第一个和最后一个位置
    • 69. x 的平方根
    • 367. 有效的完全平方数
    • 其他
    • 补充知识

要注意的问题

  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. 二分查找
Leetcode刷题:二分查找_第1张图片

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

class Solution {
public:
    int search(vector& 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. 搜索插入位置
Leetcode刷题:二分查找_第2张图片

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

class Solution {
public:
    int searchInsert(vector& 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放在大于等于的条件下。
Leetcode刷题:二分查找_第3张图片
假如a到b这个区间内存在那个等于的答案x,所有的x到b都满足大于等于x,所有的a到x都满足小于等于x,而二分就是找到x这个位置,或者x的左边一个,或者x的右边一个。

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

34. 在排序数组中查找元素的第一个和最后一个位置
Leetcode刷题:二分查找_第4张图片

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

class Solution { 
public:
	int binarySearch(vector& 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 searchRange(vector& 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 {leftIndex, rightIndex};
   		}
   		return vector {-1, -1};
   }
};

69. x 的平方根

69. x 的平方根
Leetcode刷题:二分查找_第5张图片

求小于等于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. 有效的完全平方数
Leetcode刷题:二分查找_第6张图片

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

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

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

其他

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

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

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

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

12问:
Leetcode刷题:二分查找_第7张图片
34问不需要二分

补充知识

Leetcode刷题:二分查找_第8张图片
Leetcode刷题:二分查找_第9张图片
Leetcode刷题:二分查找_第10张图片
Leetcode刷题:二分查找_第11张图片

你可能感兴趣的:(c++,leetcode,算法,c++)