zoukankan      html  css  js  c++  java
  • 有关于二分搜索的常见问题(java实现)

    前言:

    二分搜索是一个非常常见的面试题目,它具有非常广泛的用途。熟练的掌握二分搜索的基本形式和他的变式是非常重要的。接下来我们将使用java实现一些常见的有关二分搜索的问题。

    具体内容:

    1.二分搜索的基本形式:在一个有序的数组中查找k,如果k存在的话就返回k的下标,否则就返回-1

    基本的二分搜索要注意以下几个问题:

    (1)边界条件:如果k比array[0]小,或者k比array[length-1]大的话,那就说明k是不存在的;如果数组的长度为0那么也说明k是不存在的

    (2)middle的求法:middle = low+(high-low)/2

    以下是具体代码:

    //在一个有序无重复数组中找到和k相等的数的位置,找不到就返回-1
        public static  int binarySearchUnique(int[] array, int k){
            int length = array.length;
            int low = 0, high = length-1;
            if(low>high || k<array[low]||k>array[high]){
                return -1;
            }
            while(low<=high){
                int middle = low+(high-low)/2;
                if(array[middle] == k){
                    return middle;
                }else{
                    if(array[middle]>k){
                        low = middle +1;
                    }else{
                        high = middle -1;
                    }
                    
                }
            }
            return -1;
        }
        

    2.二分搜索进阶版:在一个有序有重复的数组中找到大于等于k的最小下标,如果不存在就返回-1

    思想:(1)将k和array[middle]的值进行对比,如何前者更大的话,那么说明low=middle+1;否则说明high=middle;这里为什么不是high=middle-1,因为有可能array[middle-1]比k要小,那么这个时候high就不是大于等于k的元素的最小下标所在的上限。

    (2)外层循环条件由low<=high;改成了low<high;因为如果是low等于high的话,由于high=middle,那么有可能会陷入死循环

    (3)跳出循环的条件是low等于high。此时返回的low就是所求的值。

    注意的点:(1)边界情况的判断:数组的长度要大于0

    (2)循环的跳出条件是low<high

    (3)最后的low就是所求的值

        //在一个有序有重复的数组中找到和k相等的数的最小下标,找不到就返回-1
        //search the position for the number which is the first number of <=k
        public static int binarySearchMany(int[] array, int k){
            int high = array.length -1;
            int low = 0;
            if(low>high||k>array[high]){
                return -1;
            }
            while(low<high){
                int middle = low+(high-low)/2;
                if(array[middle]<k){
                    low = middle + 1;
                }else{
                    high = middle;
                }
            }
            return low;
        }
        

    3.二分搜索进阶版:在一个有序的无重复的前后轮转的数组中找到k的位置,如果k不存在的话返回-1
    思想:将k值和array[middle],array[high]以及array[low]分别比较:

    当 k==array[middle]时,说明找到了

    当array[middle]<k<=array[high],说明low=middle+1;

    当array[middle]<k&&array[high]<k,我们只能知道high = high -1;

    当array[low]<=k<array[middle], 说明high = middle -1;

    当array[low]>k&&array[middle]>k,我们只能知道low = low+1;

    //给定一个有序递增数组,不含有重复元素,但是这个有序数组发生了rotate,用二分查找找到k的位置,k不存在的话返回1
        public static int binarySearchRotate(int[] array, int k){
            int length = array.length -1;
            int low = 0, high = length;
            if(low>high){
                return -1;
            }
            while(low<=high){
                int middle = low+(high - low)/2;
                if(array[middle]==k){
                    return middle;
                }else{
                    if(array[low]<=k&&array[middle]>k){
                        high = middle -1;
                    }else if(array[low]>k&&array[middle]>k){
                        low += 1;
                    }else if(array[high]>=k&&array[middle]<k){
                        low = middle+1;
                    }else{
                        high = high-1;
                    }
                    
                }
            }    
            return -1;
            
        }

    4.二分搜索进阶版:在一个有序的无重复的数组中找到满足array[i]=i的最小下标

    思想:

    (1)array[middle]>=middle,那么说明下标大于middle的那部分数据都是不可能的

    (2)array[middle]<=middle, 那么说明下标小于middle的那部分数据都输不可能的

    边界:如果array[0]>0的话,那么是不可能找到的,同理array[length-1]也不能小于length;数组的长度要大于0

        //给定一个有序递增数组arr,其中不含有重复元素,请找到满足arr[i]==i条件的最左的位置。如果所有位置上的数都不满足条件,返回-1
        public static int binarySearchIndex(int[] array){
            int low = 0, high = array.length-1;
            if(low>high||array[low]>0||array[high]<high){
                return -1;
            }
            while(low<high){
                int middle = low +(high-low)/2;
                if(array[middle]>=middle){
                    high = middle - 1;
                }else if(array[middle]<middle){
                    low = middle + 1;
                }
            }
            if(array[low]==low){
                return low;
            }
            return -1;
        }

    测试:
    对以上的代码进行测试:

    1.先写出朴素的搜索函数:

        
        // the common method to find the k
        public static int findk(int[] array, int k){
            for(int i = 0; i<array.length; i++){
                if(array[i]==k){
                    return i;
                }
            }
            return -1;
        }
        // the common method to find the first value which is >=k
        public static int findFirstValue(int[] array, int k){
            for(int i = 0; i<array.length; i++){
                if(array[i]>=k){
                    return i;
                }
            }
            return -1;
        }
        //the common method to find first number which satisfy array[i] = i
        public static int findFirstIndex(int[] array){
            for(int i=0; i<array.length; i++){
                if(array[i]==i){
                    return i;
                }
            }
            return -1;
        }
        

    2.进行测试

        public static void main(String args[]){
            int N = 1000000;
            Random rand = new Random();
            double same = 0.0;
            while(N>0){
                int n = rand.nextInt(50)+1;
                int k = rand.nextInt();
                int[] array = new int[n];
                for(int i = 0; i<n; i++){
                    array[i] = rand.nextInt();
                }
                Arrays.sort(array);
                int position;
                position = binarySearchMany(array, k);
                int position2 = findFirstValue(array, k);
                if(position == position2){
                    same++;
                }else{
                    System.out.println(k);
                    for(int i=0; i<n; i++){
                        System.out.print(array[i]+" ");
                    }
                    System.out.println();
                    System.out.println(position);
                    System.out.println(position2);
                }
                N--;
            }
            double ratio = same/1000000;
            System.out.println(ratio);
        }

    3.经过测试以上代码皆为正确代码

  • 相关阅读:
    OpenSSH免密码登录SSH2
    mysql_init调用卡住原因分析
    磁盘文件读性能测试
    madvise、fadvise、posix_madvise和posix_fadvise函数的使用
    进程间传递文件描述符fd
    Orace开源的异步IO编程库,特点是接口非常简单
    爱奇艺视频窗口显示不出来解决办法
    brk/sbrk和mmap行为分析程序
    编译boost,去掉不使用的组件
    第24课 经典问题解析二
  • 原文地址:https://www.cnblogs.com/whatyouknow123/p/9016665.html
Copyright © 2011-2022 走看看