当前位置:   article > 正文

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

二分检索问题的线性结构表示

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;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

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;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

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;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

8.运行结果(优化后)

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/896614
推荐阅读
相关标签
  

闽ICP备14008679号