欢迎关注我的简书:https://www.jianshu.com/u/55a1bc4688c6
本文源自LeetCode二分查找章节,通过本章节的练习对二分查找的基础知识进行掌握,同时能够利用二分查找解决部分问题。二分查找是一个效率非常高的算法,它充分利用了元素间的次序关系,采用分治策略,可在最坏的情况下用O(log n)完成搜索任务。掌握二分查找对于提升代码效率很有帮助。
LeetCode二分查找地址:https://leetcode-cn.com/explore/learn/card/binary-search/208/background/832/
本篇文章将介绍3个二分模版,同时完成十个例题。在阅读过程中,如果对三个模板的区别难以理解,请务必完全阅读完,这是一个需要逐步思考的过程。
给个目录:
LeetCode704 二分查找
LeetCode69 x 的平方根
LeetCode374 猜数字大小
LeetCode33 搜索旋转排序数组
LeetCode278 第一个错误的版本
LeetCode75 寻找峰值
LeetCode159 寻找旋转排序数组中的最小值
LeetCode34 在排序数组中查找元素的第一个和最后一个位置
LeetCode658 找到 K 个最接近的元素
二分查找基础知识
什么是二分查找
二分查找是计算机科学中最基本、最有用的算法之一。 它描述了在有序集合中搜索特定值的过程。
二分查找中使用的术语:
目标 Target —— 你要查找的值
索引 Index —— 你要查找的当前位置
左、右指示符 Left,Right —— 我们用来维持查找空间的指标
中间指示符 Mid —— 我们用来应用条件来确定我们应该向左查找还是向右查找的索引
二分查找原理
二分查找是一种在每次比较之后将查找空间一分为二的算法。每次需要查找集合中的索引或元素时,都应该考虑二分查找。如果集合是无序的,我们可以总是在应用二分查找之前先对其进行排序。
二分查找一般由三个主要部分组成:
预处理 —— 如果集合未排序,则进行排序。
二分查找 —— 使用循环或递归在每次比较后将查找空间划分为两半。
后处理 —— 在剩余空间中确定可行的候选者。
二分查找是一个效率非常高的算法,它充分利用了元素间的次序关系,采用分治策略,可在最坏的情况下用O(log n)完成搜索任务。掌握二分查找对于提升代码效率很有帮助。
复杂度
二分查找的时间复杂度:O(log n),因为二分查找是通过对查找空间中间的值应用一个条件来操作的,并因此将查找空间折半,在更糟糕的情况下,我们将不得不进行 O(log n) 次比较,其中 n 是集合中元素的数目。
二分查找的空间复杂度:O(1),不需要任何其他额外空间。
LeetCode704 二分查找
我们先来一道最为基础的二分查找题目,理解一下二分查找。
本题为LeetCode704 二分查找,属于二分查找中最为基础的练习。二分查找,顾名思义,就是将数组一分为二,在左右两边查找,确定元素区间之后再次一分为二,直至确定元素。
题目
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例 1:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
示例 2:
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
提示:
你可以假设 nums
中的所有元素是不重复的。
n
将在 [1, 10000]
之间。
nums
的每个元素都将在 [-9999, 9999]
之间。
C++代码
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)/2; //计算计算中间值
if(nums[mid]==target) return mid; //如果找到了target,返回当前的索引
if(nums[mid]<target) left = mid+1; //如果nums[mid]<target,证明正确的值在当前位置的右边
if(nums[mid]>target) right = mid-1; //如果nums[mid]>target,证明正确的值在当前位置的左边
}
return -1;
}
};
体会
本题是二分查找最为基础的题目,对于理解二分查找非常重要。
二分查找 模版#1
模板 #1 是二分查找的最基础和最基本的形式。这是一个标准的二分查找模板,是非常基础简单的二分查找。模板 #1 用于查找可以通过访问数组中的单个索引来确定的元素或条件。模版#1 不需要后处理,因为每一步中,你都在检查是否找到了元素。如果到达末尾,则知道未找到该元素。
模版#1 对应的例题为:
LeetCode69 x 的平方根
LeetCode374 猜数字大小
LeetCode33 搜索旋转排序数组
模版#1 C++代码
int binarySearch( vector<int> & nums, int target )
{
if ( nums.size() == 0 )
return(-1);
int left = 0, right = nums.size() - 1;
while ( left <= right )
{
/* Prevent (left + right) overflow */
int mid = left + (right - left) / 2;
if (nums[mid] == target) return mid;
else if ( nums[mid] < target ) left = mid + 1;
else right = mid - 1;
}
/* End Condition: left > right */
return(-1);
}
语法关键
初始条件:left = 0, right = length-1
终止:left > right
向左查找:right = mid-1
向右查找:left = mid+1
LeetCode69 x 的平方根
题目
实现 int sqrt(int x) 函数。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
示例 1:
输入: 4
输出: 2
示例 2:
输入: 8
输出: 2
说明: 8 的平方根是 2.82842...,
由于返回类型是整数,小数部分将被舍去。
C++代码
class Solution {
public:
int mySqrt(int x){
int left = 0;
int right = x;
if (x <= 1) return x;
int ans= 0;
while(left<=right){
int mid = (right+left) /2; //计算中间值
if(x/mid >= mid ) {
left = mid+1