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

C# 二分查找:从原理到实现(新学者思考)

1. 什么是二分查找

二分查找(Binary Search)是一种在有序数组中查找特定元素的高效算法。它的核心思想是“分而治之”:每次将搜索范围缩小一半,直到找到目标值或范围为空。时间复杂度为 O(log n),比线性查找的 O(n) 快得多,尤其适合大规模数据。

2. 算法原理

二分查找的基本步骤如下:

  1. 设定左右边界 left 和 right,初始分别为数组的第一个和最后一个索引。
  2. 计算中间索引 mid = left + (right - left) / 2,这样可以防止大数相加时溢出。
  3. 将中间元素与目标值比较:
    • 若相等,返回 mid;
    • 若中间元素小于目标值,说明目标在右半部分,将 left 更新为 mid + 1;
    • 若中间元素大于目标值,说明目标在左半部分,将 right 更新为 mid - 1。
  4. 重复步骤 2-3,直到 left 大于 right,表示未找到目标值。

3. 边界条件与常见坑点

在实际编码中,二分查找的边界处理非常容易出错,重点关注:

  • 循环条件:使用 while (left <= right),当 left 和 right 相等时仍需判断该位置。
  • 中间索引计算:优先使用 left + (right - left) / 2,避免 (left + right) / 2 的溢出风险。
  • 左右边界更新:left = mid + 1 或 right = mid - 1,否则可能陷入死循环。
  • 返回值:未找到时通常返回 -1 或插入位置的按位取反值。

4. C# 标准实现(迭代版)

下面给出一个经典的迭代二分查找方法,适用于 int 数组:

/// <summary> /// 在有序整数数组中执行二分查找,返回目标值的索引,未找到返回 -1。 /// </summary> public static int BinarySearch(int[] arr, int target) { if (arr == null || arr.Length == 0) return -1; int left = 0; int right = arr.Length - 1; while (left <= right) { // 安全计算中间索引,避免溢出 int mid = left + (right - left) / 2; if (arr[mid] == target) { return mid; } else if (arr[mid] < target) { left = mid + 1; // 目标在右半区 } else { right = mid - 1; // 目标在左半区 } } return -1; // 未找到 }

5. C# 递归实现

二分查找也可以写成递归形式,逻辑更直观,但需要注意递归深度和栈空间消耗:

/// <summary> /// 二分查找的递归版本,返回目标索引,未找到返回 -1。 /// </summary> public static int BinarySearchRecursive(int[] arr, int target) { return BinarySearchRecursive(arr, target, 0, arr.Length - 1); } private static int BinarySearchRecursive(int[] arr, int target, int left, int right) { if (left > right) return -1; int mid = left + (right - left) / 2; if (arr[mid] == target) return mid; else if (arr[mid] < target) return BinarySearchRecursive(arr, target, mid + 1, right); else return BinarySearchRecursive(arr, target, left, mid - 1); }

6. 查找第一个或最后一个目标值(变种)

如果数组中有重复元素,可能需要找到第一次出现的位置或最后一次出现的位置。这是二分查找的常见变种,在 C# 中也很容易实现:

/// <summary> /// 查找目标值在数组中第一次出现的索引。 /// </summary> public static int FindFirst(int[] arr, int target) { int left = 0, right = arr.Length - 1; int result = -1; while (left <= right) { int mid = left + (right - left) / 2; if (arr[mid] == target) { result = mid; // 记录当前找到的位置 right = mid - 1; // 继续向左搜索更早的匹配 } else if (arr[mid] < target) left = mid + 1; else right = mid - 1; } return result; } /// <summary> /// 查找目标值在数组中最后一次出现的索引。 /// </summary> public static int FindLast(int[] arr, int target) { int left = 0, right = arr.Length - 1; int result = -1; while (left <= right) { int mid = left + (right - left) / 2; if (arr[mid] == target) { result = mid; // 记录当前找到的位置 left = mid + 1; // 继续向右搜索更晚的匹配 } else if (arr[mid] < target) left = mid + 1; else right = mid - 1; } return result; }

7. 使用 C# 内置的 Array.BinarySearch

.NET 框架本身就提供了二分查找方法Array.BinarySearch,它返回的是找到的索引;如果未找到,则返回一个负数,其按位取反后就是目标应插入的位置。使用起来非常方便:

int[] sortedArray = { 1, 3, 5, 7, 9 }; int index = Array.BinarySearch(sortedArray, 5); // 返回 2 int notFound = Array.BinarySearch(sortedArray, 4); // 返回负数 if (notFound < 0) { // ~notFound 就是 4 应该插入的位置,以保持数组有序 int insertPos = ~notFound; Console.WriteLine($"4 应插入在索引 {insertPos} 处"); }

8. 常见面试题与应用场景

  • 在旋转有序数组中查找目标值(LeetCode 33)。
  • 寻找峰值元素(LeetCode 162)。
  • 计算平方根(LeetCode 69)。
  • 搜索二维矩阵(LeetCode 74)。
  • 在有序数据集合中快速定位、范围查询等。

这些题目本质上都是二分查找思想的延伸,掌握了基础实现后,可以灵活运用边界调整来适应不同需求。

9. 总结

实现时牢牢记住三点:有序数组正确的中间点计算合适的边界收缩。平时也可以直接使用Array.BinarySearch减少重复造轮子,但理解其背后的原理才能让你在复杂问题中游刃有余。

新手强烈推荐看b站up蓝不过海的演示视频

http://www.jsqmd.com/news/1245477/

相关文章:

  • Oracle数据库High Version Count问题诊断与优化
  • 深入解析SCI/LIN寄存器:从数据发送到错误注入的嵌入式实战
  • OpenAI广告业务探索:大模型商业化与对话式广告的未来
  • python代码分析nginx访问日志
  • 企业版配套软件介绍 ---- USB TO SPI 3.0-Slave
  • 头文件、条件编译、编译工具、make工具的使用
  • 知识城全屋定制哪家好:派福装饰空间焕颜 - MXyuyu
  • C++实战:构建高性能古诗词学习平台的数据结构与算法设计
  • 从零部署阿拉德源码:Linux服务端与Unity客户端联调实战
  • 分布式定时任务架构设计与实践指南
  • 嵌入式低功耗设计:时钟门控技术原理与Tiva™ MCU实战
  • 想住环境舒适酒店?快来看看金华婺城区的这些宝藏之选!
  • 2026年7月最新欧米茄扬州吾悦广场维修保养服务电话 - 欧米茄官方服务中心
  • 基于TM4C1294NCPDT的CAN总线底层驱动开发与寄存器级配置详解
  • 欧米茄更换原装表带价格查询|完整维修地址及售后电话权威信息公告(2026年7月最新) - 欧米茄服务中心
  • 鸿蒙三方库 | harmony-utils之LocationUtil位置获取与订阅详解
  • Unity像素化插件深度解析:从原理到实战的风格化渲染方案
  • Openwrt软路由在Vmware环境的搭建
  • [Android] 全球网测 v4.4.8 -综合测速工具
  • Godot引擎Shell Fur毛发插件:原理、实战与性能优化指南
  • 2026年工业船型开关生产商,选这3家才靠谱
  • Godot语法主题开发实战:从核心原理到避坑指南
  • 2026年7月最新泰格豪雅苏州平江万达广场维修保养服务电话 - 亨得利钟表维修中心
  • 打标机行业布局豆包 AI 搜索新机遇,企优托王成成解读制造业 GEO 增长路径
  • 计算机毕业设计之基于springboot的日常用药检索小指南
  • Qwen-Audio-3.0-TTS:16种语言20种方言的多语言语音合成技术解析
  • Claude提示词优化:从冗长到高效的AI协作实践
  • MySQL“读已提交“并非万能药:深度解析RC隔离级别的盲区与适用边界
  • 近期量化工具推荐,AI检查要跟着核心问题走
  • 2026年7月工厂吸尘器厂家Top3:品牌优缺点排行 - 工业清洁测评社