• 二分查找


    前几天,在论坛看到有统计说90%的程序员不能写对简单的二分法。二分法不是很简单的吗?

    其实,二分法真的不是那么简单,尤其是二分法的各个变种。最最简单的二分法,就是从一个排好序的数组之查找一个key值。如下面程序

    /**
    * 二分查找 找到该值在数组中的下标 否则为-1
    */
    static int binarySearch(int [] array, int key)
    {
        int left = 0;
        int right = array.length - 1;
        
        while(left <= right)
        {
            int mid = (left + right) / 2;
            if(array[mid] == key)
            {
                return mid;
            }
            else if(array[mid] < key)
            {
                left = mid + 1;
            }else
            {
                right = mid - 1;
            }
        } 
        return -1;
    }
    

    这个程序,相信只要是一个合格的程序员应该都会写。稍微注意点,每次移动left和right 指针的时候, 需要在mid的基础上 + 1 或者 - 1, 防止出现死循环, 程序也就也能够正确运行。

    但如果条件稍微变化一下, 你还会写吗?如,数组之中的数据可能可以重复,要求返回匹配的数据的最小(或最大)的下标;更近一步, 需要找出数组中第一个大于key的元素(也就是最小的大于key的元素的)下标,等等。这些,虽然只有一点点的变化,实现的时候确实要更加的细心。下面列出了这些二分检索变种的实现。

    1. 找出第一个与Key相等元素
    // 查找第一个相等的元素
    static int findFirstEqual(int [] array, int key)
    {
        int left = 0;
        int right = array.length - 1;
        
        while (left <= right )
        {
            int mid = (left + right) / 2;
            if ( array[mid] >= key)
            {
                right = mid - 1;
            }else{
                left = mid + 1;
            }
        }
        if(left < array.length && array[left] == key){
            return left;
        }
        return -1;
    }
    
    1. 找出最后一个与key相等的元素
    // 查找最后一个相等的元素
    static int findLastEqual(int[] array, int key) {
        int left = 0;
        int right = array.length - 1;
    
        // 这里必须是 <=
        while (left <= right) {
            int mid = (left + right) / 2;
            if (array[mid] <= key) {
                left = mid + 1;
            }
            else {
                right = mid - 1;
            }
        }
        if (right >= 0 && array[right] == key) {
            return right;
        }
    
        return -1;
    }
    

    3.查找第一个等于或者大于Key的元素

    // 查找第一个等于或者大于key的元素
    static int findFirstEqualLarger(int[] array, int key) {
        int left = 0;
        int right = array.length - 1;
    
        // 这里必须是 <=
        while (left <= right) {
            int mid = (left + right) / 2;
            if (array[mid] >= key) {
                right = mid - 1;
            }
            else {
                left = mid + 1;
            }
        }
        return left;
    }
    
    1. 查找第一个大于key的元素
    
    // 查找第一个大于key的元素
    static int findFirstLarger(int[] array, int key) {
        int left = 0;
        int right = array.length - 1;
    
        // 这里必须是 <=
        while (left <= right) {
            int mid = (left + right) / 2;
            if (array[mid] > key) {
                right = mid - 1;
            }
            else {
                left = mid + 1;
            }
        }
        return left;
    }
    
    
    1. 查找最后一个等于或者小于key的元素
    // 查找最后一个等于或者小于key的元素
    static int findLastEqualSmaller(int[] array, int key) {
        int left = 0;
        int right = array.length - 1;
    
        // 这里必须是 <=
        while (left <= right) {
            int mid = (left + right) / 2;
            if (array[mid] > key) {
                right = mid - 1;
            }
            else {
                left = mid + 1;
            }
        }
        return right;
    }
    
    
    1. 查找最后一个小于key的元素
    
    // 查找最后一个小于key的元素
    static int findLastSmaller(int[] array, int key) {
        int left = 0;
        int right = array.length - 1;
    
        // 这里必须是 <=
        while (left <= right) {
            int mid = (left + right) / 2;
            if (array[mid] >= key) {
                right = mid - 1;
            }
            else {
                left = mid + 1;
            }
        }
        return right;
    }
    
    

    接下来,大家可以对这四种变种算法进行相应的测试。
    很多的时候,应用二分检索的地方都不是直接的查找和key相等的元素,而是使用上面提到的二分检索的各个变种,熟练掌握了这些变种,当你再次使用二分检索的检索的时候就会感觉的更加的得心应手了。这里,我留给大家一个问题,这种 mid = (left + right) / 2 的写法有什么不足,该怎么改进呢?

  • 相关阅读:
    Goahead在linux环境下安装部署
    vim卡住怎么办
    Clickhouse 实现 row number功能
    JavaScript ES6 模块化
    MySQL012事务的四个基本特征是什么
    MySQL015简述mysql中索引类型有哪些,以及对数据库的性能的影响
    MySQL010MySQL执行计划怎么看
    JavaScript ES6 Promise
    MySQL011如何处理MySQL的慢查询
    MySQL009MySQL为什么需要主从复制和读写分离
  • 原文地址:https://www.cnblogs.com/Hijack-you/p/11614463.html
Copyright © 2020-2023  润新知