算法知识02-二分查找

133 阅读1分钟

简单二分查找

function bindarySearch(arr,value){
    let low = 0;
    let hight = arr.length -1;
    while(low <= hight){
        let mid = (hight+low)/2;
        if(arr[mid] === value) {
            return mid;
        } else if(arr[mid] < value){
            low = mid+1;
        } else{
            hight = mid-1;
        }
    }
    return -1
}

三点需要注意的:

  1. while的执行条件:low<=hight;
  2. mid = (hight+low)/2,如果low和hight很大,可能导致溢出,这里可以改写为low+(hight-low)/2,再进行一次优化,low+((hight-low)>>2),计算机执行位运算比除法运算快
  3. low和hight的重新赋值:low = mid+1;hight = mid-1;如果直接写成 low=mid 或者 high=mid,就可能会发生死循环

递归写法

    function bindarySearch(arr,value,low,hight){
        if(low<hight) return -1;
        let mid = low + ((hight+low)>>1);// 注意位运算优先级
        if(arr[mid] === value) return mid;
        if(arr[mid] < value){
            return bindarySearch(arr,value,mid+1,hight);
        } else {
            return bindarySearch(arr,value,low,mid-1);
        }
    }
    function search(arr,value){
        let low = 0;
        let hight = arr.length -1;
        return bindarySearch(arr,value,low,hight);
    }

二分查找应用场景的局限性

二分查找的时间复杂度是 O(logn),查找数据的效率非常高。不过,并不是什么情况下都可以用二分查找,它的应用场景是有很大局限性的

  • 首先,二分查找依赖的是顺序表结构,简单点说就是数组,不适用于链表,
  • 其次,二分查找针对的是有序数据。如果我们针对的是一组静态的数据,没有频繁地插入、删除,我们可以进行一次排序,多次二分查找。这样排序的成本可被均摊,二分查找的边际成本就会比较低。但是,如果我们的数据集合有频繁的插入和删除操作,要想用二分查找,要么每次插入、删除操作之后保证数据仍然有序,要么在每次二分查找之前都先进行排序。针对这种动态数据集合,无论哪种方法,维护有序的成本都是很高的。
  • 再次,数据量太小不适合二分查找。
  • 最后,数据量太大也不适合二分查找。二分查找的底层需要依赖数组这种数据结构,而数组为了支持随机访问的特性,要求内存空间连续,对内存的要求比较苛刻。比如,我们有 1GB 大小的数据,如果希望用数组来存储,那就需要 1GB 的连续内存空间。

二分查找的变形

1. 变体一:查找第一个值等于给定值的元素

function bindarySearch(arr,value){
    let low = 0;
    let hight = arr.length -1;
    while(low <= hight){
        let mid = hight+((hight-low)>>1);
        if(arr[mid] < value){
            low = mid+1;
        } else if{
            hight = mid-1;
        } else {
        // arr[mid] === value;
        // 当mid 为0,查找的为第一个,当mid前面一个元素的值 === value说明mid的值不是第一个,继续往前查找
            if((mid === 0) || arr[mid-1] !==value){
                return mid;
            }
            hight = mid-1;
        }
    }
    return -1
}

变体二:查找最后一个值等于给定值的元素

function bindarySearch(arr,value){
    let low = 0;
    let hight = arr.length -1;
    while(low <= hight){
        let mid = hight+((hight-low)>>1);
        if(arr[mid] < value){
            low = mid+1;
        } else if{
            hight = mid-1;
        } else {
        // arr[mid] === value;
        // 当mid 为0,查找的为第一个,当mid前面一个元素的值 === value说明mid的值不是第一个,继续往前查找
            if((mid === hight) || arr[mid+1] !==value){
                return mid;
            }
            low = mid+1;
        }
    }
    return -1
}

变体三:查找第一个大于等于给定值的元素

function bindarySearch(arr,value){
    let low = 0;
    let hight = arr.length -1;
    while(low <= hight){
        let mid = hight+((hight-low)>>1);
        if(arr[mid] >= value){
            if ((mid == 0) || (a[mid - 1] < value)) return mid;
            high = mid - 1;
        } else if{
            low = mid+1;
        }
    }
    return -1
}

变体四:查找最后一个小于等于给定值的元素

function bindarySearch(arr,value){
    let low = 0;
    let hight = arr.length -1;
    while(low <= hight){
        let mid = hight+((hight-low)>>1);
        if(arr[mid] > value){
            high = mid - 1;
        } else if{
            if ((mid == arr.length-1) || (a[mid - 1] > value)) return mid;
            low = mid+1;
        }
    }
    return -1
}