数据结构和算法之二分查找

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/jun5753/article/details/90447095

1.二分法查找定义

二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。 它是对一组有序的数字中进行查找,传递相应的数据,进行比较查找到与原数据相同的数据,查找到了返回对应的数组下标,没有找到返回-1;

优点: 比较次数少,查找速度快,平均性能好 ,占用系统内存较少

缺点: 待查表为有序表,插入删除困难

适用: 不经常变动而查找频繁的有序列表

2.二分查找的思想

生活中二分查找的思想无处不在。一个最常见的就是猜数游戏,我随机写一个 0 到 99 的数,然后你来猜我写的是什么。猜的过程中,我会告诉你每次是猜大了还是猜小了,直到猜中为止。假如我写的数是 23,猜数过程如下所示。

次数 猜测范围 中间数 对比大小
第1次 0-99 49 49>23
第2次 0-48 24 24>23
第3次 0-23 11 11<23
第4次 12-23 17 17<23
第5次 18-23 20 20<23
第6次 21-23 22 22<23
第7次 23

最多只需要 7 次就猜出来了,这个过程是很快的。同理,要查找某个数据是否在给定的数组中,我们同样也可以利用这个思想。

二分查找针对的是一个有序的数据集合,查找思想有点类似于分治,每次都通过和中间元素进行比较,将待查找区间缩小为之前的一半,直到找到要查找的元素或者区间缩小为 0 为止。

3.简单二分查找的算法实现

  • kotlin
//第一种:
/**
 * @param array 有序数组
 * @param left 数组低地址下标
 * @param right 数组高地址下标
 * @param key  查找元素
 */
fun binarySearch(data: IntArray, left: Int, right: Int, key: Int): Int {
    var low = left
    var high = right

    while (low <= high) {
        val mid = (low + high) / 2
        when {
            key == data[mid] -> return mid
            key > data[mid] -> low = mid + 1
            else -> high = mid - 1
        }
    }
    return -1
}

//第二种:递归法:

/**
 * @param array 有序数组
 * @param left 数组低地址下标
 * @param right 数组高地址下标
 * @param key  查找元素
 */
fun binarySearch(data: IntArray, left: Int, right: Int, key: Int): Int {

    while (left <= right) {
        val mid = (left + right) / 2
        return  when {
            key == data[mid] ->  mid
            
            key > data[mid] ->  binarySearch(data, mid + 1, right, key)// left = mid + 1
            
            else ->  binarySearch(data, left, mid - 1, key) //right = mid - 1
        }
    }
    return -1
}

注意事项

  • 循环退出条件 left <= right

  • mid = left + ((right-left) >> 1),用移位运算优化计算性能

  • left 和 right 的更新分别是 mid+1 和 mid-1

  • Java版

public static void main(String[] args) {
        int srcArray[] = {3, 5, 11, 17, 21, 23, 28, 30, 32, 50, 64, 78, 81, 95, 101};
        System.out.println(binSearch(srcArray, 0, srcArray.length - 1, 111));
        System.out.println(binSearch(srcArray, 78));
    }


    /**
     * 二分查找普通实现。
     *
     * @param srcArray 有序数组
     * @param key      查找元素
     * @return 不存在返回-1
     */
    private static int binSearch(int srcArray[], int key) {
        int mid;
        int start = 0;
        int end = srcArray.length - 1;
        while (start <= end) {
            mid = (end - start) / 2 + start;
            if (key < srcArray[mid]) {
                end = mid - 1;
            } else if (key > srcArray[mid]) {
                start = mid + 1;
            } else {
                return mid;
            }
        }
        return -1;
    }

    /**
     * 二分查找递归实现。
     *
     * @param srcArray 有序数组
     * @param start    数组低地址下标
     * @param end      数组高地址下标
     * @param key      查找元素
     * @return 查找元素不存在返回-1
     */
    private static int binSearch(int srcArray[], int start, int end, int key) {
        int mid = (end - start) / 2 + start;
        if (srcArray[mid] == key) {
            return mid;
        }
        if (start >= end) {
            return -1;
        } else if (key > srcArray[mid]) {
            return binSearch(srcArray, mid + 1, end, key);
        } else if (key < srcArray[mid]) {
            return binSearch(srcArray, start, mid - 1, key);
        }
        return -1;
    }

4.二分查找的应用场景

二分查 找依赖的是顺序表结构,也就是数组,需要能够按照下标随机访问元素。

二分查找针对的是有序数据,如果数据无序,需要先进行排序。而如果有频繁的插入、删除操作,则每次查找前都需要再次排序,这时候,二分查找将不再适用。

数据量太小可以直接遍历查找,没有必要用二分查找。但如果数据之间的比较操作非常耗时,比如数据为长度超过 300 的字符串,则不管数据量大小,都推荐使用二分查找。

数据量太大不适合用二分查找。二分查找底层依赖数组这种数据结构,而数组为了支持随机访问的特性,要求内存空间连续,对内存的要求比较苛刻。比如,我们有 1 G B 1GB 大小的数据,如果希望使用数组来存储,那就需要 1 G B 1GB 的连续内存空间来存储。

参考资料:

1.程杰 .大话数据结构. 北京:清华大学出版社, 2011

猜你喜欢

转载自blog.csdn.net/jun5753/article/details/90447095
今日推荐