当前位置: 首页 > news >正文

力扣刷题——二分查找总结

我们可以总结出二分查找的通用做法和常见变种。二分查找是一种在有序数组中高效查找元素的算法,时间复杂度为 O (log n)。

二分查找的通用模板

二分查找的核心思想是将搜索范围不断缩小一半,直到找到目标元素或确定其不存在。以下是通用模板:

cpp

int binarySearch(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) {return mid;  // 找到目标} else if (nums[mid] < target) {left = mid + 1;  // 目标在右半部分} else {right = mid - 1;  // 目标在左半部分}}return -1;  // 未找到目标
}

常见变种及处理技巧

1. 查找插入位置(第一个大于等于目标的位置)
  • 特点:返回第一个大于等于target的索引,若所有元素都小于target,则返回数组长度。
  • 关键:循环结束时,left指向第一个大于等于target的位置。

cpp

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 - 1;}}return left;  // 最终left即为插入位置
}
2. 二维矩阵查找
  • 特点:将二维矩阵展开为一维有序数组。
  • 关键:计算一维索引对应的二维坐标。

cpp

bool searchMatrix(vector<vector<int>>& matrix, int target) {int m = matrix.size(), n = matrix[0].size();int left = 0, right = m * n - 1;while (left <= right) {int mid = left + (right - left) / 2;int row = mid / n, col = mid % n;  // 转换为二维坐标if (matrix[row][col] == target) {return true;} else if (matrix[row][col] < target) {left = mid + 1;} else {right = mid - 1;}}return false;
}
3. 查找元素的第一个和最后一个位置
  • 特点:需要两次二分查找,分别找到左边界和右边界。
  • 关键
    • 左边界:第一个大于等于target的位置。
    • 右边界:第一个大于target的位置减 1。

cpp

vector<int> searchRange(vector<int>& nums, int target) {auto lower = [&](int t) {int left = 0, right = nums.size() - 1;while (left <= right) {int mid = left + (right - left) / 2;if (nums[mid] < t) {left = mid + 1;} else {right = mid - 1;}}return left;};int first = lower(target);if (first >= nums.size() || nums[first] != target) {return {-1, -1};}int last = lower(target + 1) - 1;  // 右边界return {first, last};
}
4. 旋转排序数组中的查找
  • 特点:数组被旋转后,左右半部分仍有序,但整体无序。
  • 关键:通过比较nums[mid]nums[0]的关系,确定哪半部分有序,再根据有序部分缩小范围。

cpp

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) {return mid;}if (nums[0] <= nums[mid]) {  // 左半部分有序if (target >= nums[0] && target < nums[mid]) {right = mid - 1;  // 目标在左半部分} else {left = mid + 1;  // 目标在右半部分}} else {  // 右半部分有序if (target > nums[mid] && target <= nums.back()) {left = mid + 1;  // 目标在右半部分} else {right = mid - 1;  // 目标在左半部分}}}return -1;
}
5. 寻找旋转排序数组中的最小值
  • 特点:最小值是旋转点,左侧元素都大于右侧元素。
  • 关键:比较nums[mid]与最右侧元素的大小,确定旋转点位置。

cpp

int findMin(vector<int>& nums) {int left = 0, right = nums.size() - 1;while (left <= right) {int mid = left + (right - left) / 2;if (nums[mid] <= nums[right]) {  // 最小值在左侧right = mid - 1;} else {  // 最小值在右侧left = mid + 1;}}return nums[left];  // 最终left指向最小值
}

二分查找的关键点总结

  1. 循环条件:通常为left <= right,确保不漏掉最后一个元素。
  2. 中间索引计算:使用mid = left + (right - left) / 2防止整数溢出。
  3. 边界调整
    • 若目标在右侧,left = mid + 1
    • 若目标在左侧,right = mid - 1
  4. 变种处理
    • 查找左边界:找到第一个大于等于target的位置。
    • 查找右边界:找到第一个大于target的位置减 1。
    • 旋转数组:通过比较nums[mid]与边界元素,确定有序部分。

掌握这些技巧后,可以灵活应对各种二分查找的变种问题。

相关文章:

  • 三、Hive DDL数据库操作
  • [工具]B站缓存工具箱 (By 郭逍遥)
  • 【LeetCode Hot100 | 每日刷题】排序数组
  • Python中,async和with结合使用,有什么好处?
  • Unity 红点系统
  • 06.three官方示例+编辑器+AI快速学习webgl_animation_skinning_additive_blending
  • C++23 views::repeat (P2474R2) 深入解析
  • Redis BigKey 问题是什么
  • Android Canvas API 详细说明与示例
  • Android Native 函数 Hook 技术介绍
  • 飞蛾扑火算法matlab实现
  • 【leetcode】《BFS扫荡术:如何用广度优搜索征服岛屿问题》
  • 左右括号的最小处理次数
  • 深入了解linux系统—— 自定义shell
  • 《智能网联汽车 自动驾驶功能道路试验方法及要求》 GB/T 44719-2024——解读
  • ES常识5:主分词器、子字段分词器
  • Nodejs核心机制
  • 支持selenium的chrome driver更新到136.0.7103.92
  • 【Java EE初阶 --- 多线程(初阶)】线程安全问题
  • 百度AI战略解析:文心一言与自动驾驶的双轮驱动
  • 中国-拉共体论坛第四届部长级会议北京宣言
  • 北京今日白天超30℃晚间下冰雹,市民称“没见过这么大颗的”
  • 熊出没!我驻日本札幌总领馆提示中国公民注意人身安全
  • 特朗普开启第二任期首次外访:中东行主打做生意,不去以色列
  • 英媒:英国首相住所起火,目前无人伤亡
  • 深一度|在亚马尔的天才面前,姆巴佩戴上“帽子”又如何