在一个循环有序的数组里查找特定值

本文介绍了一种在循环有序数组中查找特定值的方法。通过一种改进的二分查找算法实现高效搜索,该算法能够处理数组中最小值位置不确定的情况。

在一个循环有序的数组里查找特定值。(循环有序就是指数组里的各项都是有序的,但是最小值不一定是数组的第 0 项,而可能是其中任意一项,然后逐项递增,到数组尾的时候再回到第 0 项)

在这样的数组中查找就不能直接使用二分法了。可以使用一个二分法的变形,除了判断中心值之外,还要判断两端的值,以此确定循环开始点在中点的哪一边,并判断所查找的值是否在循环起始点的哪边,并采用不同的处理方式。

 

  1. #include <stdio.h>
  2. int mysearch(int arr[], int s, int e, int value)
  3. {
  4.     int m = (s + e) / 2;
  5.     for (; s < e; m = (s + e) / 2)
  6.     {
  7.         if (arr[m] == value)
  8.         {
  9.             return m;
  10.         }
  11.         /*
  12.          * s m e
  13.          * 1 2 3
  14.          */
  15.         if (arr[s] <= arr[m] && arr[m] <= arr[e])
  16.         {
  17.             if (value < arr[m])
  18.             {
  19.                 e = m - 1;
  20.             }
  21.             else
  22.             {
  23.                 s = m + 1;
  24.             }
  25.         }
  26.         /*
  27.          * s m e
  28.          * 2 3 1
  29.          */
  30.         else if (arr[s] <= arr[m] && arr[m] >= arr[e])
  31.         {
  32.             if (value < arr[m] && value >= arr[s])
  33.             {
  34.                 e = m - 1;
  35.             }
  36.             else
  37.             {
  38.                 s = m + 1;
  39.             }
  40.         }
  41.         /*
  42.          * s m e
  43.          * 3 1 2
  44.          */
  45.         else
  46.         {
  47.             if (value > arr[m] && value <= arr[e])
  48.             {
  49.                 s = m + 1;
  50.             }
  51.             else
  52.             {
  53.                 e = m - 1;
  54.             }
  55.         }
  56.     }
  57.     if (arr[s] == value)
  58.     {
  59.         return s;
  60.     }
  61.     return -1;
  62. }
  63. int main(int argc, char * argv[])
  64. {
  65.     int i;
  66.     int arr[] = {70, 80, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  67.     int len = sizeof(arr) / sizeof(arr[0]);
  68.     for (i = 0; i < len; ++i)
  69.     {
  70.         printf("%d/n", mysearch(arr, 0, len - 1, arr[i]));
  71.     }
  72.     return 0;
  73. }
<think>首先,用户要求手动编写一个适用于有序数组的二分查找算法。我需要基于提供的引用来构建一个完整的实现。 关键点来自引用: - 引用[1]:展示了创建有序数组和输入值的C代码。 - 引用[2]:定义了二分查找的术语,如target、l、r、mid。 - 引用[3]:描述了二分查找的逻辑:如果key等于arr[mid],则找到;如果key小于arr[mid],向左查找;如果key大于arr[mid],向右查找。 - 引用[4]:给出了一个C++实现,用于找到最接近的值。 - 引用[5]:展示了Java中的二分查找,处理插入位置。 系统指令: - 使用$...$格式表示行内数学表达式。 - 独立公式使用$$...$$格式。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 - 结构清晰,帮助用户逐步解决问题。 我的回答结构: 1. **介绍**:解释二分查找的基本概念和适用条件。 2. **算法原理**:简要描述二分查找的工作原理。 3. **手动实现**:提供一个完整的代码实现,用中文注释,确保代码清晰。 4. **步骤解释**:逐步解释代码逻辑。 5. **时间复杂度分析**:使用行内数学表达式表示时间复杂度。 6. **生成相关问题**:基于主题生成几个问题。 7. **引用标识**:在适当的地方添加引用标识。 代码实现:我需要选择一个适当的语言。用户提到C语言在引用[1]中,所以我可以使用C语言实现,但引用[5]是Java,引用[4]是C++。既然用户说“手动编写”,我可以使用伪代码或C语言来保持通用。用户上一次的问题是关于二分查找的,所以我应该使用类似的语言。或者提供一个通用伪代码并用中文注释。 引用[1]是C语言,所以我可以用C语言实现,并用中文注释。 算法逻辑: - 输入:有序数组arr,大小n,目标值target。 - 输出:如果找到,返回索引;否则,返回-1。 - 初始化左指针l=0,右指针r=n-1。 - 当l <= r时循环: - 计算mid = l + (r - l)/2 // 防止溢出 - 如果arr[mid] == target,返回mid。 - 如果arr[mid] < target,设置l = mid + 1。 - 否则,设置r = mid - 1。 - 循环结束时,未找到,返回-1。 这与引用[3]和[5]一致。 添加注释:用中文写注释。 生成相关问题: - 基于二分查找的主题,如: 1. 二分查找的时间复杂度是多少? 2. 二分查找在无序数组上可行吗? 3. 如何优化二分查找的实现? 4. 二分查找的应用场景有哪些? 确保回答真实可靠。 完整回答大纲: 1. 引言:二分查找是一种高效的搜索算法,基于有序数组。 2. 算法步骤描述。 3. 代码实现(C语言)。 4. 逐步解释。 5. 时间复杂度:$O(\log n)$。 6. 相关问题。 添加引用标识:例如,在描述算法时,提到“根据引用[3],二分查找的核心逻辑包括...”。 </think>### 手动实现有序数组的二分查找算法 二分查找(Binary Search)是一种高效的搜索算法,适用于有序数组(升序或降序)。它通过不断缩小搜索范围来快速定位目标值,时间复杂度为 $O(\log n)$,远优于线性搜索的 $O(n)$ [^2]。算法核心是使用三个指针:左指针 `l`、右指针 `r` 和中点指针 `mid`。每次比较中点值与目标值,根据大小关系调整搜索区间,直到找到目标或区间为空。下面我将一步步指导您手动实现一个完整的二分查找算法(使用 C 语言),确保代码清晰可靠。 #### 算法原理 二分查找的工作原理基于有序数组性: 1. **初始化**:设置左指针 `l = 0`(数组起始索引)和右指针 `r = n - 1`(数组结束索引)。 2. **循环迭代**:当 `l <= r` 时,计算中点索引 `mid = l + (r - l)/2`(防止整数溢出)。比较 `arr[mid]` 与目标值 `target`: - 如果 `arr[mid] == target`,返回 `mid`(找到目标)。 - 如果 `arr[mid] < target`,说明目标可能在右半侧,更新 `l = mid + 1`。 - 如果 `arr[mid] > target`,说明目标可能在左半侧,更新 `r = mid - 1`。 3. **结束条件**:如果循环结束仍无匹配,返回 `-1`(表示未找到)[^3]。 算法的时间复杂度为 $O(\log n)$,因为每次迭代将搜索范围减半,空间复杂度为 $O(1)$[^5]。 #### 手动实现代码(C 语言) 以下是完整的 C 语言实现代码,包了详细的注释。代码假设数组是升序排序的,并处理了边界情况(如数组为空或目标超出范围)。 ```c #include <stdio.h> // 二分查找函数:输入有序数组 arr,数组长度 n,目标值 target;返回目标索引或 -1 int binarySearch(int arr[], int n, int target) { // 处理空数组或无效输入 if (arr == NULL || n <= 0) { return -1; // 数组无效,直接返回未找到 } int l = 0; // 左指针,初始化为数组起始索引 int r = n - 1; // 右指针,初始化为数组结束索引 // 循环迭代,直到搜索区间为空 while (l <= r) { int mid = l + (r - l) / 2; // 计算中点(防止溢出,优于 (l+r)/2) if (arr[mid] == target) { return mid; // 找到目标,返回索引[^3] } else if (arr[mid] < target) { l = mid + 1; // 目标在右半侧,缩小搜索区间到 [mid+1, r][^2] } else { r = mid - 1; // 目标在左半侧,缩小搜索区间到 [l, mid-1][^2] } } return -1; // 循环结束未找到,返回 -1[^5] } int main() { // 示例用法:创建有序数组并测试二分查找 int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // 有序升序数组(必须有序)[^1] int n = sizeof(arr) / sizeof(arr[0]); // 计算数组长度 int target; printf("请输入要查找的目标值: "); scanf("%d", &target); // 用户输入目标值[^1] int result = binarySearch(arr, n, target); // 调用二分查找函数 if (result != -1) { printf("目标值 %d 找到于索引 %d。\n", target, result); } else { printf("目标值 %d 未找到。\n", target); } return 0; } ``` #### 代码逐步解释 1. **初始化和边界处理**:函数首先检查数组是否有效(如非空或长度大于0),避免运行时错误。这是必要的健壮性设计。 2. **指针初始化**:`l` 和 `r` 定义了当前搜索区间 `[l, r]`。初始时覆盖整个数组。 3. **中点计算**:使用 `mid = l + (r - l)/2` 而非 `(l + r)/2`,防止大数组时整数溢出(例如,当 `l` 和 `r` 都大于 $2^{31}-1$ 时)[^5]。 4. **比较逻辑**: - 如果 `arr[mid] == target`,立即返回索引,算法结束。 - 如果 `arr[mid] < target`,说明目标在右侧,移动 `l` 以缩小区间(因为数组有序)[^3]。 - 如果 `arr[mid] > target`,说明目标在左侧,移动 `r` 以缩小区间。 5. **循环结束**:当 `l > r` 时,区间为空,表示目标不存在,返回 `-1`。 6. **主函数演示**:`main()` 函数创建了一个示例数组,提示用户输入目标值,并调用 `binarySearch` 打印结果。测试时,输入如 `5` 会输出索引 `4`。 #### 时间复杂度分析 二分查找的时间复杂度为 $O(\log n)$,因为每次迭代将搜索范围减半。数学上,最坏情况下迭代次数满足 $k = \log_2 n$,其中 $n$ 是数组大小[^2]。空间复杂度为 $O(1)$,仅使用常数额外空间。 #### 注意事项 - **数组必须有序**:如果数组无序,需先排序(但排序时间复杂度可能较高,如 $O(n \log n)$)[^2]。 - **适用场景**:适合静态或较少变动的数据集,如数据库索引或查找表中元素[^4]。 - **变体扩展**:上述代码是基础版本。可扩展为查找插入位置(如引用[5]),或找到最接近的值(如引用[4])。 如果您需要其他语言的实现(如 Python 或 Java),或想测试特定边界用例,请告知!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值