数组的查找--二分法查找

概念

二分查找(Binary Search)算法,也叫折半查找算法。

当要从一个序列中查找一个元素的时候,二分查找是一种非常快速的查找算法。

二分查找是针对有序数据集合的查找算法,如果是无序数据集合就遍历查找。

二分查找之所以快速,是因为它在匹配不成功的时候,每次都能排除剩余元素中一半的元素。因此可能包含目标元素的有效范围就收缩得很快,而不像顺序查找那样,每次仅能排除一个元素。

原理

比如有一个有序表数组[1,3,5,7,9,11,13,15,17,19,21],它是按照从小到大的顺序来进行排列的,现在需要在该有序表中查找元素19,步骤如下:

  1. 首先设置两个指针low和high,分别指向数据集合的第一个数据元素1(位序为0)和最后一个数据元素21(位序为10)。

然后把整个数据集合长度分成两半,并用一个指针指向它们的临界点,所以定义指针mid指向了中间元素11(位序5),也就是说mid=(high+low)/2,其中high和low都代表所指向的元素的位序,如下图:

  1. 接着,将mid所指向的元素(11)与待查找元素(19)进行比较。

因为19大于11,说明待查找的元素(19)一定位于mid和high之间。所以继续折半,则low = mid+1,而mid = (low+high)/2,结果如下图:

  1. 接着,又将mid所指向的元素(17)与待查找元素(19)进行比较,由于19大于17,所以继续折半,则low = mid+1,而mid = (low+high)/2,结果如下图:

  1. 最后,又将mid所指向的元素(19)与待查找元素(19)进行比较,结果相等,则查找成功,返回mid指针指向的元素的位序。

如果查找的元素值不是19,而是20,那么在最后一步之前还得继续折半查找,最后出现的情况如下图:

代码实现

public class Test01 {
    public static void main(String[] args) {
        //二分法查找
​
        int[] arr = {1,2,3,4,5,6,7,8,9,11,11,11,11,11,11};
​
        int index = binarySearch01(arr, 11);
        System.out.println("指定元素出现的下标位置:" + index);
​
        List<Integer> indexList = binarySearch02(arr, 11);
        System.out.println("指定元素出现的下标位置的集合:" + Arrays.toString(indexList.toArray()));
​
        index = recursionbinarySearch01(arr, 0, arr.length-1, 11);
        System.out.println("递归方式 - 指定元素出现的下标位置:" + index);
​
        indexList = recursionbinarySearch02(arr, 0, arr.length-1, 11);
        System.out.println("递归方式 - 指定元素出现的下标位置的集合:" + Arrays.toString(indexList.toArray()));
    }
​
    /**
     * 有序的数组中查找某个元素出现的下标位置
     * 不使用递归的二分查找
     * 返回出现的下标位置
     */
    public static int binarySearch01(int[] arr,int val){
​
        int low = 0;
        int high = arr.length-1;
​
        while(low <= high){
            int mid = (low + high)/2;
​
            if(val > arr[mid]){
                //目标在右侧
                low = mid+1;
            }else if(val < arr[mid]){
                //目标在左侧
                high = mid-1;
            }else{
                return mid;
            }
        }
        return -1;
    }
​
    /**
     * 有序的数组中查找某个元素首次出现的下标位置
     * 不使用递归的二分查找
     * 返回下标集合
     */
    public static List<Integer> binarySearch02(int[] arr,int val){
​
        int low = 0;
        int high = arr.length-1;
​
        while(low <= high){
            int mid = (low + high)/2;
​
            if(val > arr[mid]){
                //目标在右侧
                low = mid+1;
            }else if(val < arr[mid]){
                //目标在左侧
                high = mid-1;
            }else{
                // 定义放置索引下标的集合
                List<Integer> list = new ArrayList<>();
                // 将首次查找的位置放入集合
                list.add(mid);
​
                // 判断是否还有重复值
                int index = mid + 1;
                while(index < arr.length){
                    if(arr[index] == val){
                        list.add(index);
                    }else{
                        break;
                    }
                    index++;
                }
                index = mid-1;
                while(index >= 0){
                    if(arr[index] == val){
                        list.add(index);
                    }else{
                        break;
                    }
                    index--;
                }
                return list;
            }
        }
        return null;
    }
​
    /**
     * 有序的数组中查找某个元素出现的下标位置
     * 使用递归的二分查找
     * 返回出现的下标位置
     */
    public static int recursionbinarySearch01(int[] arr,int low,int high,int val){
​
        if(val < arr[low] || val > arr[high] || low > high){
            return -1;
        }
​
        int mid = (low + high)/2;
​
        if(val > arr[mid]){
            //目标在右侧
            return recursionbinarySearch01(arr, mid+1, high, val);
        }else if(val < arr[mid]){
            //目标在左侧
            return recursionbinarySearch01(arr, low, mid-1, val);
        }else{
            return mid;
        }
    }
    
    /**
     * 有序的数组中查找某个元素首次出现的下标位置
     * 使用递归的二分查找
     * 返回下标集合
     */
    public static List<Integer> recursionbinarySearch02(int[] arr,int low,int high,int val){
​
        if(val < arr[low] || val > arr[high] || low > high){
            return null;
        }
​
        int mid = (low + high)/2;
​
        if(val > arr[mid]){
            //目标在右侧
            return recursionbinarySearch02(arr, mid+1, high, val);
        }else if(val < arr[mid]){
            //目标在左侧
            return recursionbinarySearch02(arr, low, mid-1, val);
        }else{
            // 定义放置索引下标的集合
            List<Integer> list = new ArrayList<>();
            // 将首次查找的位置放入集合
            list.add(mid);
​
            // 判断是否还有重复值
            int index = mid + 1;
            while(index < arr.length){
                if(arr[index] == val){
                    list.add(index);
                }else{
                    break;
                }
                index++;
            }
            
            index = mid-1;
            while(index >= 0){
                if(arr[index] == val){
                    list.add(index);
                }else{
                    break;
                }
                index--;
            }
            return list;
        }
    }
}

优缺点

优点:速度快,不占空间,不开辟新空间

缺点:必须是有序的数组,数据量太小没有意义

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值