高效查找:二分查找算法解析

1.二分查找简介

二分查找算法(Binary Search)是一种高效的查找算法,适用于有序数组或序列。它的基本思想是通过逐步缩小查找范围,将查找区间一分为二,直到找到目标值或确定目标值不存在。

算法原理:在数组的中间位置选择一个元素作为"中间值 "。将目标值与"中间值"进行比较:如果目标值等于 中间值,查找成功,返回中间值的索引。如果目标值小于 中间值,则在左半部分继续查找。如果目标值大于中间值,则在右半部分继续查找。重复以上步骤,直到找到目标值或区间为空(即找不到目标值)。

时间复杂度:二分查找的时间复杂度为 (O(\log n)),其中 (n) 是数组的元素个数。每次查找将查找范围缩小一半,因而效率较高。

在二分查找中,针对特定需求,常用的有以下三种方式:

(1)朴素二分查找:查找任意一个等于目标值的位置。

朴素二分查找模版

cpp 复制代码
while (left <= right)//循环条件
{
    int mid = left + (right - left) / 2;//left + (right - left + 1) / 2也可
    if (...) 
        left = mid + 1;
    else if (...) 
        right = mid - 1;
    else
        return ...

}

(2)左端点二分查找:查找目标值的最左位置(即第一个出现的目标值)。

二分查找区间左端点

cpp 复制代码
while (left < right)//判断条件不能等于
{
    int mid = left + (right - left) / 2;//只能是left + (right - left) / 2
    if (...) left = mid + 1;
    else right = mid;
}

(3)右端点二分查找:查找目标值的最右位置(即最后一个出现的目标值)。

二分查找区间右端点

cpp 复制代码
        //找右端点
        while (left < right)//判断条件不能等于
        {
            int mid = left + (right - left + 1) / 2;//只能是left + (right - left + 1) / 2
            if (...) left = mid;
            else right = mid - 1;
        }

二分查找不仅适用于有序数组 ,还适用于具有二段性的情况。二段性指的是一个序列被划分成两段:在某个位置之前满足某个条件,而在该位置之后不满足(或反之)。利用二分查找,能够高效地找到这个转折点。

2.二分查找

  1. 题目简介
  1. 算法思想

暴力解法:遍历数组寻找目标值

二分查找:数组是升序的

(1)循环结束条件:left <= right

(2)中间值小于目标值,去右区间查找:mid < target -> left = mid + 1

中间值大于目标值,去左区间查找:mid > target -> right = mid - 1

中间值等于目标值:返回

  1. 代码实现
cpp 复制代码
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0, right = nums.size() - 1;
        while (left <= right)
        {
            int mid = left + (right - left)/2;//可防止溢出
            if (nums[mid] < target) left = mid + 1;
            else if (nums[mid] > target) right = mid - 1;
            else if (nums[mid] == target) return mid;
            
        }
        return -1;
    }
};

. - 力扣(LeetCode)

3.在排序数组中查找元素的第⼀个和最后⼀个位置

  1. 题目简介
  1. 算法思想

暴力解法:遍历数组,找到符合条件的区间

二分查找:有序数组,可以用二分查找,使用左右端点二分查找找到符合条件的区间的左右端点

(1)循环结束条件:left < right

(2)找左端点

中间值小于目标值,找大:mid < target -> left = mid + 1

中间值大于目标值,找小:mid >= target -> right = mid

(3)找右端点

中间值小于目标值,找大:mid <= target -> left = mid

中间值大于目标值,找小:mid > target -> right = mid - 1

  1. 代码实现
cpp 复制代码
class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        if (nums.size() == 0) return {-1, -1};
        int begin = 0;
        int left = 0, right = nums.size() - 1;
        //找左端点
        while (left < right)
        {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) left = mid + 1;
            else right = mid;
        }
        if (nums[right] != target) return {-1, -1};
        else begin = left;

        right = nums.size() - 1;  
        //找右端点
        while (left < right)
        {
            int mid = left + (right - left + 1) / 2;
            if (nums[mid] <= target) left = mid;
            else right = mid - 1;
        }
        return {begin, right};
    }
};

. - 力扣(LeetCode)

4. x的平安根

  1. 题目简介
  1. 算法思想

暴力解法:从1开始计算其平方,找到第一个平方大于target的数

二分查找:从1开始到x区间使用二分查找法,返回值左区间的元素平方小于等于x,右区间元素平方大于x。

(1)循环结束条件:left < right

(2)小于目标值,找大:mid * mid <= x -> left = mid

大于目标值,找小:mid * mid > x -> right = mid - 1

  1. 代码实现
cpp 复制代码
class Solution {
public:
    int mySqrt(int x) {
        if (x < 1) return 0;
        int left = 1, right = x;
        while (left < right)
        {
            long long mid = left + (right - left + 1) / 2;
            if (mid * mid <= x) left = mid;
            else right = mid - 1;
        }
        return left;
    }
};

. - 力扣(LeetCode)

5. 搜索插入位置

  1. 题目简介
  1. 算法思想

暴力解法:遍历数组找到第一个比目标值大的元素的下标

二分查找:数组是升序的,我们要找的返回值是大于等于target的,因此将数组分为左右两个区间,左边区间小于target,右边区间大于等于target,,所以我们要找到右区间的左端点。

(1)循环结束条件:left < right

(2)中间值小于目标值,则应该找大:mid < target -> left = mid + 1

中间值大于目标值,找小:mid > target -> right = mid

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

. - 力扣(LeetCode)

6. 山脉数组的封顶索引

  1. 题目简介

暴力解法:遍历数组寻找峰值

二分查找:数组具有二段性,arr[i] < arr[i + 1],arr[i] > arr[i+ 1]

(1)循环结束条件:left < right

(2)处于升序部分:arr[mid] >= arr[mid - 1] left = mid;

处于降序部分:arr[mid] < arr[mid - 1] right = mid - 1;

  1. 代码实现
cpp 复制代码
class Solution {
public:
    int peakIndexInMountainArray(vector<int>& arr) {
        int left = 1, right = arr.size() - 2;
        while (left < right)
        {
            int mid = left + (right - left + 1) / 2;
            if (arr[mid] >= arr[mid - 1]) left = mid;
            else right = mid - 1;
        }
        return left;
    }
};

. - 力扣(LeetCode)

7. 寻找峰值

  1. 题目简介
  1. 算法思想

  2. 代码实现

暴力解法:遍历数组,找到一个大于左右相邻值的元素

二分查找算法:找二段性,nums[-1] = nums[n] = -∞,arr[i] > arr[i + 1] 则左边一定存在峰值,

arr[i] < arr[i + 1] 则右边一定存在峰值。

(1)循环结束条件: left < right

(2)处于递减序列中:nums[mid] > nums[mid + 1] -> right = mid;

处于递增序列中:nums[mid] <= nums[mid + 1] -> left = mid + 1

cpp 复制代码
class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        int left = 0, right = nums.size() - 1;
        while (left < right)
        {
            long long mid = left + (right - left) / 2;
            if (nums[mid] > nums[mid + 1]) right = mid;
            else left = mid + 1;
        }
        return right;
    }
};

8.搜索旋转排序数组中的最小值

  1. 题目简介
  1. 算法思想

暴力解法:遍历数组找到最小值

二分查找:找二段性,AB之间大于数组最后一个元素,CD之间小于等于D元素

(1)循环条件:left < right

(2)中间值大于D:left = mid + 1

中间值小于等于D:right = mid

  1. 代码实现
cpp 复制代码
class Solution {
public:
    int findMin(vector<int>& nums) {
        int n = nums.size();
        int left = 0, right = n - 1;
        while (left < right)
        {
            int mid = left + (right - left) / 2;
            if (nums[mid] > nums[n - 1]) left = mid + 1;
            else right = mid;
        }
        return nums[left];
    }
};

. - 力扣(LeetCode)

9. 0〜n-1 中缺失的数字

  1. 题目简介
  1. 算法思想

暴力解法:遍历数组,时间复杂度为O(n)

二分查找:找二段性,目标元素左边区间的元素和它们的下标相等,右边区间的元素比他们的下标大1

(1)循环结束条件:left < right

(2) 元素和下标相等:records[mid] == mid -> left = mid + 1;

不相等:right = mid;

  1. 代码实现
cpp 复制代码
class Solution {
public:
    int takeAttendance(vector<int>& records) {
        int left = 0, right = records.size() - 1;
        while (left < right)
        {
            int mid = left + (right - left) / 2;
            if (records[mid] == mid) left = mid + 1;
            else right = mid;
        }
        if (records[left] == left) return left + 1;
        else return left;
    }
};

. - 力扣(LeetCode)

相关推荐
qystca几秒前
【16届蓝桥杯寒假刷题营】第2期DAY4
数据结构·c++·算法·蓝桥杯·哈希
JNU freshman2 分钟前
线段树 算法
算法·蓝桥杯
英国翰思教育38 分钟前
留学毕业论文如何利用不同问题设计问卷
人工智能·深度学习·学习·算法·学习方法·论文笔记
人类群星闪耀时1 小时前
寻找两个正序数组的中位数:分治法与二分查找的结合
算法·leetcode
এ旧栎1 小时前
蓝桥与力扣刷题(240 搜索二维矩阵||)
算法·leetcode·矩阵·学习方法
Xzh04231 小时前
c语言网 1127 尼科彻斯定理
数据结构·c++·算法
gentle_ice2 小时前
leetcode——删除链表的倒数第N个节点(java)
java·leetcode·链表
qystca3 小时前
【16届蓝桥杯寒假刷题营】第2期DAY5
c++·算法·蓝桥杯·贡献度
这是我583 小时前
链表的介绍
数据结构·c++·其他·链表·visual studio·介绍·图文结合
艺杯羹4 小时前
C语言二级题解:查找字母以及其他字符个数、数字字符串转双精度值、二维数组上下三角区域数据对调
c语言·开发语言·数据结构