算法场景
二分查找作为一种高效的查找算法,时间复杂度为 O ( l o g n ) O(logn) O(logn)
,以下结合此文的「搜索插入位置」和「平方根求解」,来体验它的高效!


- 算法场景
-
- 一、搜索插入位置
-
- [1.1 题目链接](#1.1 题目链接)
- [1.2 题目描述](#1.2 题目描述)
- [1.3 题目示例](#1.3 题目示例)
- [1.4 算法思路](#1.4 算法思路)
- [1.5 核心代码](#1.5 核心代码)
- [1.6 示例测试(总代码)](#1.6 示例测试(总代码))
- [二、x 的平方根](#二、x 的平方根)
-
- [2.1 题目链接](#2.1 题目链接)
- [2.2 题目描述](#2.2 题目描述)
- [2.3 题目示例](#2.3 题目示例)
- [2.4 算法思路](#2.4 算法思路)
- [2.5 核心代码](#2.5 核心代码)
- [2.6 示例测试(总代码)](#2.6 示例测试(总代码))
- 总结
一、搜索插入位置
1.1 题目链接
1.2 题目描述
给定一个无重复元素的升序排列数组 nums 和一个目标值 target,在数组中找到目标值并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。要求必须使用时间复杂度为 O(log n) 的算法。
1.3 题目示例
- 示例 1:
输入: nums = [1,3,5,6], target = 5
输出: 2 - 示例 2:
输入: nums = [1,3,5,6], target = 2
输出: 1 - 示例 3:
输入: nums = [1,3,5,6], target = 7
输出: 4
1.4 算法思路
本题的核心是利用二分查找高效定位目标值的位置或插入点,关键在于把握「升序数组」的特性和边界条件的处理:
- 初始化搜索区间:左边界
left = 0,右边界right = nums.size() - 1,覆盖整个数组范围。 - 二分查找核心循环:当
left < right时,计算中间位置mid = left + (right - left) / 2(该写法可避免left + right溢出)。- 若
nums[mid] < target:说明目标值在mid右侧,将左边界更新为mid + 1。 - 否则:说明目标值在
mid左侧或等于nums[mid],将右边界更新为mid。
- 若
- 循环结束后,
left与right重合,此时需判断最终位置:- 若
nums[left] < target:说明目标值大于数组所有元素,应插入到数组末尾,返回right + 1。 - 否则:目标值的位置或插入点即为
right(与left相等)。
- 若
1.5 核心代码
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 right + 1;
return right;
}
};
1.6 示例测试(总代码)
cpp
#include <iostream>
#include <vector>
using namespace std;
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 right + 1;
return right;
}
};
int main() {
// 示例 1 测试
vector<int> nums1 = {1,3,5,6};
int target1 = 5;
Solution sol;
cout << "示例 1 输出:" << sol.searchInsert(nums1, target1) << endl; // 预期输出 2
// 示例 2 测试
vector<int> nums2 = {1,3,5,6};
int target2 = 2;
cout << "示例 2 输出:" << sol.searchInsert(nums2, target2) << endl; // 预期输出 1
// 示例 3 测试
vector<int> nums3 = {1,3,5,6};
int target3 = 7;
cout << "示例 3 输出:" << sol.searchInsert(nums3, target3) << endl; // 预期输出 4
return 0;
}
二、x 的平方根
2.1 题目链接
2.2 题目描述
给你一个非负整数 x,计算并返回 x 的算术平方根。由于返回类型是整数,结果只保留整数部分,小数部分将被舍去。注意:不允许使用任何内置指数函数和算符(如 pow(x, 0.5) 或 x ** 0.5)。
2.3 题目示例
- 示例 1:
输入:x = 4
输出:2 - 示例 2:
输入:x = 8
输出:2
解释:8 的算术平方根是 2.82842...,小数部分舍去后返回 2。
2.4 算法思路
本题本质是在「0 到 x」的整数区间内,查找最大的整数 mid 使得 mid * mid <= x,同样适用二分查找,需重点处理边界和溢出问题:
- 边界情况处理:当
x < 1时(即 x = 0),直接返回 0。 - 初始化搜索区间:左边界
left = 0,右边界right = x(因为对于非负整数 x,其平方根一定不大于 x)。 - 二分查找核心循环:当
left < right时,计算中间位置mid = left + (right - left + 1LL) / 2:- 加
1LL是为了避免「死循环」:当区间收缩到[left, left+1]时,确保能正确移动左边界。 - 用
long long类型存储mid:防止mid * mid超出 int 范围导致溢出。
- 加
- 区间更新逻辑:
- 若
mid * mid <= x:说明mid可能是答案或答案在mid右侧,将左边界更新为mid。 - 否则:说明答案在
mid左侧,将右边界更新为mid - 1。
- 若
- 循环结束后,
left与right重合,即为x算术平方根的整数部分。
2.5 核心代码
cpp
class Solution {
public:
int mySqrt(int x) {
// 处理边界情况
if(x < 1) return 0;
else
{
int left = 0;
int right = x;
while(left < right)
{
long long mid = left + (right - left + 1LL) / 2; // 溢出处理
if(mid * mid <= x) left = mid;
else right = mid - 1;
}
return left;
}
}
};
2.6 示例测试(总代码)
cpp
#include <iostream>
using namespace std;
class Solution {
public:
int mySqrt(int x) {
// 处理边界情况
if(x < 1) return 0;
else
{
int left = 0;
int right = x;
while(left < right)
{
long long mid = left + (right - left + 1LL) / 2; // 溢出处理
if(mid * mid <= x) left = mid;
else right = mid - 1;
}
return left;
}
}
};
int main() {
// 示例 1 测试
int x1 = 4;
Solution sol;
cout << "示例 1 输出:" << sol.mySqrt(x1) << endl; // 预期输出 2
// 示例 2 测试
int x2 = 8;
cout << "示例 2 输出:" << sol.mySqrt(x2) << endl; // 预期输出 2
// 额外测试用例
int x3 = 0;
cout << "x=0 输出:" << sol.mySqrt(x3) << endl; // 预期输出 0
int x4 = 5;
cout << "x=5 输出:" << sol.mySqrt(x4) << endl; // 预期输出 2
int x5 = 9;
cout << "x=9 输出:" << sol.mySqrt(x5) << endl; // 预期输出 3
return 0;
}
总结
在本篇文章中,我们通过「搜索插入位置」与「x 的平方根」两个经典题目,深入理解了 二分查找 的核心思想与应用方式。
二分查找的核心价值在于将线性搜索的 O(n) 时间复杂度优化为 O(log n),尤其适用于「有序区间」的查找问题。通过两道题的对比可以发现:
- 二分查找的关键在于「确定搜索区间」和「区间更新规则」,需根据题目目标灵活调整(如查找目标值 vs 查找最大满足条件值);
- 边界处理(如数组为空、x=0 等极端情况)和溢出防护(如用 long long 存储中间值)是二分查找实现的常见考点;
- 虽然两道题的场景不同,但都利用了「有序性」和「区间收缩」的核心逻辑,体现了二分查找的通用性。
掌握二分查找的本质后,面对有序数组查找、数值逼近等问题时,都可以快速构建解题思路,高效解决问题。