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

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

1.二分查找是什么?

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

时间复杂度:O(log2n)

2.二分查找的思路分析

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

首先,我们需要确定数组中间的下标(位置):mid = (left+right)/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)

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

这一部分,我们以递归方式实现的代码优化为例,思路如下:

取示例array[N] = {1,10,20,30,99, 99, 99, 1020},我们需要查找99

先找到mid,但是不要直接返回mid

以mid为起点向左侧扫描,将所有关键字为99的下标记录到ArrayList集合

再次以mid为起点向右侧扫描,将所有关键字为99的下标记录到ArrayList集合

注意mid只用记录一次

返回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和空。

相关文章
|
1月前
|
存储 算法 索引
【优选算法】—— 二分查找
【优选算法】—— 二分查找
|
1月前
|
机器学习/深度学习 数据采集 监控
机器学习-特征选择:如何使用递归特征消除算法自动筛选出最优特征?
机器学习-特征选择:如何使用递归特征消除算法自动筛选出最优特征?
72 0
|
15天前
|
算法
算法系列--递归(一)--与链表有关(上)
算法系列--递归(一)--与链表有关
27 0
|
1天前
|
算法
数据结构与算法-递归思想
数据结构与算法-递归思想
6 0
|
18天前
|
算法 测试技术 Serverless
【二分查找】【C++算法】378. 有序矩阵中第 K 小的元素
【二分查找】【C++算法】378. 有序矩阵中第 K 小的元素
|
19天前
|
存储 算法 搜索推荐
【数据结构与算法】归并排序(详解:递归与非递归的归并排序 | 赠:冒泡排序和选择排序)
【数据结构与算法】归并排序(详解:递归与非递归的归并排序 | 赠:冒泡排序和选择排序)
|
1月前
|
存储 编解码 自然语言处理
【软件设计师备考 专题 】深入理解数据压缩、递归和图的相关算法
【软件设计师备考 专题 】深入理解数据压缩、递归和图的相关算法
64 0
|
1月前
|
算法 索引
算法思想总结:二分查找算法
算法思想总结:二分查找算法
|
1月前
【手撕数据结构】二分查找(好多细节)
【手撕数据结构】二分查找(好多细节)
|
1月前
|
传感器 算法 计算机视觉
基于肤色模型和中值滤波的手部检测算法FPGA实现,包括tb测试文件和MATLAB辅助验证
该内容是关于一个基于肤色模型和中值滤波的手部检测算法的描述,包括算法的运行效果图和所使用的软件版本(matlab2022a, vivado2019.2)。算法分为肤色分割和中值滤波两步,其中肤色模型在YCbCr色彩空间定义,中值滤波用于去除噪声。提供了一段核心程序代码,用于处理图像数据并在FPGA上实现。最终,检测结果输出到&quot;hand.txt&quot;文件。