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

二分算法深度解析

二分算法深度解析

    • 一、二分算法基础概念
      • 1.1 算法核心思想
      • 1.2 算法基本流程
      • 1.3 算法复杂度分析
      • 1.4 算法正确性证明
    • 二、二分算法基础实现
      • 2.1 基本二分查找(有序数组)
      • 2.2 二分查找的递归实现
      • 2.3 二分查找的常见错误
    • 三、二分算法变种问题
      • 3.1 查找第一个等于目标值的位置
      • 3.2 查找最后一个等于目标值的位置
      • 3.3 查找第一个大于等于目标值的位置
      • 3.4 查找最后一个小于等于目标值的位置
    • 四、二分算法在特殊数组中的应用
      • 4.1 在旋转有序数组中搜索
      • 4.2 寻找峰值
      • 4.3 在二维有序矩阵中搜索
    • 五、二分答案思想与应用
      • 5.1 二分答案的核心思想
      • 5.2 经典应用:分割数组的最大值
      • 5.3 经典应用:寻找最小的k满足条件
    • 六、二分算法的优化与技巧
      • 6.1 避免mid计算溢出
      • 6.2 浮点数二分
      • 6.3 三分查找(适用于单峰函数)
    • 七、二分算法实际应用场景
      • 7.1 数据搜索与查询
      • 7.2 算法优化
      • 7.3 科学计算与数值分析
      • 7.4 工程实践

二分算法(Binary Search Algorithm)是一种高效的搜索策略,思想简洁而强大,从基本的有序数组搜索到复杂的算法问题求解,以其对数级的时间复杂度成为算法设计中的核心技术之一。本文我将系统讲解二分算法的核心原理、常见变种、实现细节、应用及优化技巧,带你全面掌握这一基础而重要的算法。

一、二分算法基础概念

1.1 算法核心思想

二分算法,又称二分查找或折半查找,其核心思想是通过不断将搜索区间减半,逐步缩小目标元素的可能位置,从而在对数时间内完成搜索。该算法的基本前提是数据结构有序,通过比较中间元素与目标值的大小关系,决定下一步搜索的区间,直至找到目标元素或确定目标元素不存在。

1.2 算法基本流程

  1. 初始化:确定搜索区间的左右边界,通常为left=0right=n-1(假设数组长度为n)。
  2. 计算中间位置:计算区间中间位置mid = left + (right - left) / 2,避免溢出的写法为mid = left + ((right - left) >> 1)
  3. 比较与缩小区间
    • 若中间元素等于目标值,返回中间位置。
    • 若中间元素大于目标值,更新右边界为mid - 1,在左半区间继续搜索。
    • 若中间元素小于目标值,更新左边界为mid + 1,在右半区间继续搜索。
  4. 终止条件:当left > right时,搜索区间为空,目标元素不存在。

1.3 算法复杂度分析

  • 时间复杂度:二分算法的时间复杂度为 O ( log ⁡ n ) O(\log n) O(logn),其中n为搜索空间的大小。每次迭代将搜索区间减半,因此最多需要 log ⁡ 2 n \log_2 n log2n次迭代。
  • 空间复杂度:通常为 O ( 1 ) O(1) O(1),仅使用常数级别的额外空间。

1.4 算法正确性证明

二分算法的正确性可以通过数学归纳法证明:

  1. 当搜索区间长度为1时,算法正确判断元素是否存在。
  2. 假设搜索区间长度为k时算法正确,当长度为k+1时,通过比较中间元素,将问题分解为长度不超过k/2的子问题,由归纳假设知子问题正确,故原问题正确。

二、二分算法基础实现

2.1 基本二分查找(有序数组)

public class BinarySearch {/*** 在有序数组中查找目标值的索引* @param nums 有序数组* @param target 目标值* @return 目标值的索引,不存在返回-1*/public int search(int[] nums, int target) {int left = 0, right = nums.length - 1;while (left <= right) {int mid = left + ((right - left) >> 1); // 避免溢出的mid计算if (nums[mid] == target) {return mid;} else if (nums[mid] < target) {left = mid + 1;} else {right = mid - 1;}}return -1;}public static void main(String[] args) {BinarySearch bs = new BinarySearch();int[] nums = {1, 3, 5, 7, 9, 11, 13};System.out.println(bs.search(nums, 7));  // 输出3System.out.println(bs.search(nums, 8));  // 输出-1}
}

2.2 二分查找的递归实现

public class RecursiveBinarySearch {public int search(int[] nums, int target) {return recursiveSearch(nums, target, 0, nums.length - 1);}private int recursiveSearch(int[] nums, int target, int left, int right) {if (left > right) {return -1;}int mid = left + ((right - left) >> 1);if (nums[mid] == target) {return mid;} else if (nums[mid] < target) {return recursiveSearch(nums, target, mid + 1, right);} else {return recursiveSearch(nums, target, left, mid - 1);}}
}

2.3 二分查找的常见错误

  1. 边界条件处理错误

    • 终止条件应为left <= right而非left < right,否则可能漏掉最后一个元素。
    • 更新边界时应为left = mid + 1right = mid - 1,避免陷入死循环。
  2. mid计算溢出

    • 错误写法:mid = (left + right) / 2,当leftright接近整数最大值时可能溢出。
    • 正确写法:mid = left + ((right - left) >> 1),使用减法和移位避免溢出。
  3. 返回条件错误

    • 找到目标值后应立即返回,避免继续无效搜索。

三、二分算法变种问题

3.1 查找第一个等于目标值的位置

public class FirstEqual {public int searchInsert(int[] nums, int target) {int left = 0, right = nums.length - 1;while (left <= right) {int mid = left + ((right - left) >> 1);if (nums[mid] >= target) {right = mid - 1;} else {left = mid + 1;}}return left;}public static void main(String[] args) {FirstEqual fe = new FirstEqual();int[] nums = {1, 3, 5, 7, 9};System.out.println(fe.searchInsert(nums, 6));  // 输出3}
}

3.2 查找最后一个等于目标值的位置

public class LastEqual {public int search(int[] nums, int target) {int left = 0, right = nums.length - 1;while (left <= right) {int mid = left + ((right - left) >> 1);if (nums[mid] <= target) {left = mid + 1;} else {right = mid - 1;}}return right;}
}

3.3 查找第一个大于等于目标值的位置

public class FirstGreaterEqual {public int search(int[] nums, int target) {int left = 0, right = nums.length - 1;while (left <= right) {int mid = left + ((right - left) >> 1);if (nums[mid] >= target) {right = mid - 1;} else {left = mid + 1;}}return left;}
}

3.4 查找最后一个小于等于目标值的位置

public class LastLessEqual {public int search(int[] nums, int target) {int left = 0, right = nums.length - 1;while (left <= right) {int mid = left + ((right - left) >> 1);if (nums[mid] <= target) {left = mid + 1;} else {right = mid - 1;}}return right;}
}

四、二分算法在特殊数组中的应用

4.1 在旋转有序数组中搜索

public class SearchInRotatedArray {public int search(int[] nums, int target) {if (nums == null || nums.length == 0) {return -1;}int left = 0, right = nums.length - 1;while (left <= right) {int mid = left + ((right - left) >> 1);if (nums[mid] == target) {return mid;}// 左半部分有序if (nums[left] <= nums[mid]) {if (nums[left] <= target && target < nums[mid]) {right = mid - 1;} else {left = mid + 1;}} else { // 右半部分有序if (nums[mid] < target && target <= nums[right]) {left = mid + 1;} else {right = mid - 1;}}}return -1;}
}

4.2 寻找峰值

public class FindPeakElement {public int findPeakElement(int[] nums) {int left = 0, right = nums.length - 1;while (left < right) {int mid = left + ((right - left) >> 1);if (nums[mid] > nums[mid + 1]) {// 峰值在左半部分right = mid;} else {// 峰值在右半部分left = mid + 1;}}return left;}
}

4.3 在二维有序矩阵中搜索

public class Search2DMatrix {public boolean searchMatrix(int[][] matrix, int target) {if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {return false;}int rows = matrix.length;int cols = matrix[0].length;int left = 0, right = rows * cols - 1;while (left <= right) {int mid = left + ((right - left) >> 1);int row = mid / cols;int col = mid % cols;if (matrix[row][col] == target) {return true;} else if (matrix[row][col] < target) {left = mid + 1;} else {right = mid - 1;}}return false;}
}

五、二分答案思想与应用

5.1 二分答案的核心思想

二分答案是二分算法的一种高级应用,适用于求解"最大化最小值"或"最小化最大值"类型的问题。其核心思想是:

  1. 将求解问题转化为判断问题。
  2. 确定答案的可能范围(左边界和右边界)。
  3. 对可能的答案进行二分搜索,每次判断该答案是否可行。
  4. 根据判断结果调整搜索范围,最终得到最优解。

5.2 经典应用:分割数组的最大值

public class SplitArray {public int splitArray(int[] nums, int m) {int left = 0, right = 0;for (int num : nums) {left = Math.max(left, num); // 左边界为数组中的最大值right += num; // 右边界为数组总和}while (left < right) {int mid = left + ((right - left) >> 1);if (isValid(nums, m, mid)) {right = mid;} else {left = mid + 1;}}return left;}private boolean isValid(int[] nums, int m, int maxSum) {int count = 1;int currentSum = 0;for (int num : nums) {if (currentSum + num > maxSum) {count++;currentSum = num;if (count > m) {return false;}} else {currentSum += num;}}return true;}
}

5.3 经典应用:寻找最小的k满足条件

public class FindMinK {public int findMinK(int[] nums, int target) {int left = 0, right = nums.length - 1;int result = -1;while (left <= right) {int mid = left + ((right - left) >> 1);if (isSatisfied(nums, mid, target)) {result = mid;right = mid - 1; // 寻找更小的k} else {left = mid + 1;}}return result;}private boolean isSatisfied(int[] nums, int k, int target) {// 判断k是否满足条件// 具体逻辑根据问题而定return nums[k] >= target;}
}

六、二分算法的优化与技巧

6.1 避免mid计算溢出

// 错误写法(可能溢出)
int mid = (left + right) / 2;// 正确写法(推荐)
int mid = left + ((right - left) >> 1);// 另一种正确写法
int mid = left + (right - left) / 2;

6.2 浮点数二分

public class FloatingBinarySearch {public double findRoot(double n) {double left = 0, right = n;// 浮点数二分需要设置精度final double EPS = 1e-10;while (right - left > EPS) {double mid = left + (right - left) / 2;if (mid * mid < n) {left = mid;} else {right = mid;}}return left;}
}

6.3 三分查找(适用于单峰函数)

public class TernarySearch {public double findMax(double[] nums) {double left = 0, right = nums.length - 1;final double EPS = 1e-10;while (right - left > EPS) {double mid1 = left + (right - left) / 3;double mid2 = right - (right - left) / 3;if (nums[(int)mid1] < nums[(int)mid2]) {left = mid1;} else {right = mid2;}}return nums[(int)left];}
}

七、二分算法实际应用场景

7.1 数据搜索与查询

  • 数据库索引查询:B树和B+树的查找过程本质上是多路二分搜索。
  • 有序数组中的快速查找:如Java的Arrays.binarySearch()方法。

7.2 算法优化

  • 快速排序中的分区优化:通过二分思想优化基准值的选择。
  • 动态规划中的状态优化:如使用二分查找减少状态转移的时间。

7.3 科学计算与数值分析

  • 求解方程近似解:如二分法求解非线性方程的根。
  • 数值积分与优化:结合二分思想进行数值计算。

7.4 工程实践

  • 网络协议中的滑动窗口大小调整。
  • 系统资源分配中的阈值确定。
  • 机器学习中的超参数优化。

That’s all, thanks for reading!
觉得有用就点个赞、收进收藏夹吧!关注我,获取更多干货~

相关文章:

  • AI大模型初识(一):AI大模型的底层原理与技术演进
  • 【Linux驱动开发 ---- 2.1_深入理解 Linux 内核架构】
  • 生成模型_条件编码器
  • 【BTC】密码学原理
  • FPGA基础 -- Verilog表达式之操作数:常数
  • _mm_aeskeygenassist_si128 硬件指令执行的操作
  • SpringCloud + Zookeeper + Feign整合及Feign原理
  • 43-旋转图像
  • 网络安全之任意文件读取利用
  • nt!CcGetDirtyPages函数分析之Scan to the end of the Bcb list--重要
  • 设计模式笔记_创建型_工厂模式
  • C++ vector(2)
  • 【学习笔记】NLP 基础概念
  • 微软因安全漏洞禁用黑暗环境下的Windows Hello面部识别功能
  • langChain4j-流式输出
  • 前端压缩图片的免费软件
  • C# winform教程(二)----ComboBox
  • Pycharm(二十)神经网络入门
  • 【技术】记一次 Docker 中的 ES 数据迁移,使用 Reindex API
  • 运行ollama V0.9.1 异常 GLIBC_2.27 not found
  • h5case什么网站/怎么让百度搜索靠前
  • 购物网网站建设开题报告/互联网推广项目
  • 三国群英传私服网站怎么做/站长之家怎么找网址
  • 北京做网站哪家公司好/火星时代教育培训机构怎么样
  • 深圳专业做网站较好的公司/微信营销平台
  • 政府为什么要建设网站/小说关键词提取软件