【数据结构与算法】一篇文章彻底搞懂二分查找(思路图解+代码优化)两种实现方式,递归与非递归

本文详细介绍了二分查找算法的工作原理、思路分析、代码实现(递归与非递归方式)以及在处理有重复关键字时的优化策略。通过示例展示了如何在有序数组中高效查找目标值,并提供了Java代码实现。最后,讨论了如何在存在重复关键字的情况下,返回所有匹配的索引。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.二分查找是什么?

二分查找也称折半查找,是一种效率较高的查找方式。但是,二分查找要求线性表为顺序存储结构且表按关键字有序排列。
时间复杂度O(log2n)

2.二分查找的思路分析

便于叙述,笔者以数组array[N] = [1,10,20,30,99,1020]为例。left、right分别表示数组的左界和右界。需要查找的值记为value。

  1. 首先,我们需要确定数组中间的下标(位置):mid = (left+right)/2;
  2. 将需要查找的数value与array[mid]比较:
    (1)若value > array[mid]:说明需要查找的数在mid的右边,需要更新left、mid值向右侧查找;
    (2)若value < array[mid]: 说明需要查找的数在mid的左边,需要更新right、mid值向左侧查找;
    (3)若value = array[mid]: 说明找到了需要查找的数,返回位置值即可。

3.思路图解

假设我们需要查找数值99,mid首先指向的位置应为20的位置。left指向1,right指向1020。判断array[mid] = 20 与 99 不相等,此时,left = mid + 1,右移;right 不变;mid更新为4。此时,array[mid] = 99, 查找成功,返回索引值4。
图解

4.代码实现(递归方式)

何时递归结束???

  • 找到value,返回mid;
  • 没有找到value:此时整个数组已经被递归搜索完成,left > right,结束递归,并且返回-1(认为没有匹配值)。
    具体Java代码实现如下:
public class BinarySearch {
    public static void main(String[] args) {
        int[] array = {1,10,20,30,99,1020};
        // 待查找值设置为99 、 0
        int value1 = 99;
        int value2 = 0;
        System.out.println(value1 + "'s index: " + bs(array,0,array.length-1,value1));
        System.out.println(value2 + "'s index: " + bs(array,0,array.length-1,value2));
    }

    // 二分查找递归实现
    /**
     *
     * @param array 数组
     * @param left 左端点
     * @param right 右端点
     * @param value 待查找的值
     * @return 查找到value则返回索引值;否则返回-1
     */
    public static int bs(int[] array, int left, int right, int value) {
        // 没有找到
        if(left > right){
            return -1;
        }
        int mid = (left + right) / 2;
        if(value > array[mid]){
            // 向右查找,更新left = mid + 1
             return bs(array, mid + 1, right, value);
        }else if(value < array[mid]){
            // 向左查找,更新right = mid - 1
            return bs(array,  left, mid - 1, value);
        }else {
            return mid;
        }
    }
}

5.代码实现(非递归方式)

非递归的方法体如下,具体见注释:

/**
     * 
     * @param array 数组
     * @param value 待查找的值
     * @return 查找到value则返回索引值;否则返回-1
     */
    public static int binarySearch(int[] array, int value){
        int left = 0;
        int right = array.length - 1;
        // 只要left <= right 就一直查找
        while (left <= right){
            int mid = (left + right) / 2; // mid放在循环内声明是因为mid也需要更新
            if(value > array[mid]){
                // 向右查找,更新left = mid + 1
                left = mid + 1;
            }else if(value < array[mid]){
                // 向左查找,更新right = mid - 1
                right = mid - 1;
            }else {
                return mid;
            }
        }
        // 出了循环说明没有找到,就返回-1
        return -1;
    }

6.运行结果

设待查找值设置为99 、 0,在array[N] = [1,10,20,30,99,1020]中查找。运行结果如下(99在数组内,故返回索引4;0不在数组中,故返回-1)
result1

7.代码优化(针对线性表中有重复关键字的情况)

这一部分,我们以递归方式实现的代码优化为例,思路如下:
取示例array[N] = {1,10,20,30,99, 99, 99, 1020},我们需要查找99

  1. 先找到mid,但是不要直接返回mid
  2. 以mid为起点向左侧扫描,将所有关键字为99的下标记录到ArrayList集合
  3. 再次以mid为起点向右侧扫描,将所有关键字为99的下标记录到ArrayList集合
  4. 注意mid只用记录一次
  5. 返回ArrayList,遍历ArrayList即可得到所有索引

优化代码如下:

import java.util.ArrayList;

public class BinarySearch {
    public static void main(String[] args) {
        int[] array = {1,10,20,30,99, 99, 99, 1020};
        // 待查找值设置为99 、 0
        int value1 = 99;
        int value2 = 0;
        System.out.println(value1 + "'s index: " + bs(array,0,array.length-1,value1));
        System.out.println(value2 + "'s index: " + bs(array,0,array.length-1,value2));
    }

    // 二分查找递归实现
    /**
     *
     * @param array 数组
     * @param left 左端点
     * @param right 右端点
     * @param value 待查找的值
     * @return 查找到value则返回索引值;否则返回空集合
     */
    public static ArrayList<Integer> bs(int[] array, int left, int right, int value) {
        // 没有找到
        if(left > right){
            return new ArrayList();
        }
        int mid = (left + right) / 2;
        if(value > array[mid]){
            // 向右查找,更新left = mid + 1
             return bs(array, mid + 1, right, value);
        }else if(value < array[mid]){
            // 向左查找,更新right = mid - 1
            return bs(array,  left, mid - 1, value);
        }else {
            ArrayList<Integer> res = new ArrayList<>();
            // 扫描左侧
            int index = mid - 1;
            while (true){
                if(index < 0 || array[index] != value){
                    break;
                }
                res.add(index);
                index--;
            }
            // mid记录一次
            res.add(mid);
            // 扫描右侧
            index = mid + 1;
            while (true){
                if(index > array.length - 1 || array[index] != value){
                    break;
                }
                res.add(index);
                index++;
            }
            return res;
        }
    }
}

8.运行结果(优化后)

取示例array[N] = {1,10,20,30,99, 99, 99, 1020},我们查找99、0,应该返回4、5、6和空。
end

— end
本文到此就结束啦,如果对你有帮助,顺手点个赞叭!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

兴趣使然黄小黄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值