二分查找(红绿标记法)

1.线性查找

线性查找(Linear Search)是一种简单直观的搜索算法,用于在数组中查找特定值的位置。它的基本思想是逐个检查数组中的每个元素,直到找到目标元素或者遍历完整个数组。
下面是线性查找的基本实现步骤:

  1. 遍历数组:从数组的第一个元素开始,逐个检查每个元素。
  2. 比较目标值:将当前元素与目标值进行比较。
  3. 找到目标:如果找到目标值,返回当前元素的索引。
  4. 未找到目标:如果遍历完整个数组都没有找到目标值,返回一个指定的表示未找到的值(例如 -1)。
    下面是一个简单的 C 语言实现示例,展示了如何使用线性查找在数组中查找特定的目标值:
#include <stdio.h>
// 线性查找函数,返回目标值在数组中的索引,如果找不到则返回 -1
int linearSearch(int arr[], int n, int target) {
    for (int i = 0; i < n; i++) {
        if (arr[i] == target) {
            return i;  // 找到目标值,返回索引 i
        }
    }
    return -1;  // 没有找到目标值,返回 -1
}
int main() {
    int arr[] = {23, 4, 16, 42, 8, 15};
    int n = sizeof(arr) / sizeof(arr[0]);
    int target = 42;
    
    int result = linearSearch(arr, n, target);
    
    if (result != -1) {
        printf("目标值 %d 找到在数组中的索引为 %d\n", target, result);
    } else {
        printf("目标值 %d 在数组中未找到\n", target);
    } 
    return 0;
}

解释代码细节

  • linearSearch 函数接受三个参数:arr(待查找的数组),n(数组的大小),target(要查找的目标值)。
  • 函数使用 for 循环遍历数组 arr 中的每个元素。
  • 在循环体中,使用 if 条件语句检查当前元素 arr[i] 是否等于目标值 target
  • 如果找到目标值,则立即返回当前元素的索引 i
  • 如果遍历完整个数组都没有找到目标值,则返回 -1 表示未找到。

2.二分查找

2.1一般的二分查找

二分查找(Binary Search)是一种高效的搜索算法,适用于已经排好序的数组。它的基本思想是通过将目标值与数组中间元素进行比较,从而排除一半的数据,从而快速缩小搜索范围。
二分查找的步骤

  1. 初始化:确定数组的左右边界,通常左边界为 left = 0,右边界为 right = n - 1,其中 n 是数组的长度。
  2. 循环条件:只要 left 小于等于 right,就继续查找。
  3. 中间元素:计算中间元素的索引 mid,一般为 mid = (left + right) / 2
  4. 比较目标值
    • 如果目标值等于 arr[mid],则找到目标,返回 mid
    • 如果目标值小于 arr[mid],则在左半部分继续查找,更新 right = mid - 1
    • 如果目标值大于 arr[mid],则在右半部分继续查找,更新 left = mid + 1
  5. 未找到目标:如果循环结束时仍未找到目标值,则表示数组中不存在该值,返回 -1 或者其他指定的未找到标志。

2.2特殊的二分查找——红绿标记法

但是,在这里,我打算介绍一种新型的二分查找方法——红绿标记法

#include <stdio.h>

// 函数声明
int findMinGreenIndex(int nums[], int n, int target);
int isGreen(int num, int target);

// 寻找第一个大于等于 target 的元素的索引
int findMinGreenIndex(int nums[], int n, int target) {
    int red = -1;
    int green = n;
    int mid = 0;
    while (red + 1 < green) {
        mid = (red + green) / 2;
        if (isGreen(nums[mid], target)) {
            green = mid;
        } else {
            red = mid;
        }
    }
    return green;
}

// 判断是否大于等于 target
int isGreen(int num, int target) {
    return num >= target;
}

int main(void) {
    int nums[5] = {1, 2, 3, 4, 5};
    int number = findMinGreenIndex(nums, 5, 3);
    printf("%d\n", number); // 输出应该是 2
    return 0;
}

2.3 原理解释:

  1. 定义变量

    • red:初始为-1,表示小于目标值的范围的右边界。
    • green:初始为n,表示大于等于目标值的范围的左边界。
    • mid:中间位置的索引。
  2. 循环条件:

    • 当 red + 1 < green 时,表示范围内还有元素未处理。
  3. 计算中间位置 mid

    • mid = (red + green) / 2,计算中间索引位置。
  4. 判断条件:

    • 调用 isGreen(nums[mid], target) 函数,判断 nums[mid] 是否大于等于 target
    • 如果是,则将 green 缩小到 mid,因为我们要找的是第一个大于等于 target 的元素,可能还有更小的索引满足条件,所以缩小搜索范围到左侧。
    • 如果不是,则将 red 扩展到 mid,因为 nums[mid] 小于 target,所以将搜索范围缩小到右侧。
  5. 返回结果:

    • 当循环结束时,返回 green,即找到的第一个大于等于 target 的元素的索引。

2.4 代码中的应用:

  • 在 main 函数中,数组 nums 中有序地存储了 {1, 2, 3, 4, 5}
  • 调用 findMinGreenIndex(nums, 5, 3),期望找到大于等于 3 的第一个元素的索引。
  • 在这个例子中,3 在数组中的索引为 2,因此输出结果应为 2
    这种红绿标记法的优点在于,它通过修改搜索范围的两个边界 redgreen,在每次迭代中都将搜索范围缩小一半,因此时间复杂度为 O(log n),非常高效地找到符合条件的索引。
posted @ 2024-08-17 20:04  写代码的大学生  阅读(4)  评论(0编辑  收藏  举报  来源