二分搜索相关题目汇总

二分

查找

由于后面的题都使用二分,就没有给代码写注释

/**
 * @Author xiangcheng
 * @Date 2021/8/17 15:58
 * @Version 1.0
 */
public class exer {
    public static void main(String[] args) {
        int[] arr = {2,4,5,8,11,32,34,53,67};
        System.out.println(mer1(arr,5,0,arr.length - 1));
    }
    private static int mer(int[] arr,int num){
        if (arr == null || arr.length == 0){
            return -1;
        }
        int re = -1;
        int l = 0,r = arr.length - 1;
        int mid;
        while (l <= r){
            mid = l + (r - l)/2;
            if (arr[mid] < num){
                l = mid + 1;
            }else if (arr[mid] > num){
                r = mid - 1;
            }else {
                re = mid;
                break;
            }
        }
        return re;
    }

    private static int mer1(int[] arr,int num,int l,int r){
        int re = -1,mid;
        if (l <= r){
            mid = l + (r - l)/2;
            if (arr[mid] > num){
                re = mer1(arr,num,l,mid - 1);
            }else if (arr[mid] < num){
                re = mer1(arr,num,mid + 1,r);
            }else {
                re = l;
            }
        }
        return re;
    }
}

局部最小

  • 给定一个无序数组arr,已知任意相邻的两个元素,值都不重复。请返回任意一个局部最小的位置。

  • 所谓局部最小的位置是指,如果arr[0]<arr[1],

  • 那么位置0就是一个局部最小的位置。

  • 如果arrN-1小于arr[N-2],

  • 那么位置N-1也是局部最小的位置。如果位置i既不是最左位置也不是最右位置。

  • 那么只要满足arr[i]同时小于它左右两侧的值即( arr[i-1]和arr[i+1]),

  • 那么位置i也是一个局部最小的位置。

package 二分.sousuo.exer_;

/**
 * @Author xiangcheng
 * @Date 2021/8/18 9:18
 * @Version 1.0
 */

/**
 * 给定一个无序数组arr,已知任意相邻的两个元素,值都不重复。请返回任意一个局部最小的位置。
 * 所谓局部最小的位置是指,如果arr[0]<arr[1],
 * 那么位置0就是一个局部最小的位置。
 * 如果arr[N-1](也就是arr最右的数)小于arr[N-2],
 * 那么位置N-1也是局部最小的位置。如果位置i既不是最左位置也不是最右位置。
 * 那么只要满足arr[i]同时小于它左右两侧的值即( arr[i-1]和arr[i+1]),
 * 那么位置i也是一个局部最小的位置。
 */
public class LocalMinimum {
    public static void main(String[] args) {
        int[] arr = {11,8,7,2,3,4,5,6,8,9,19};//测试用例
        System.out.println(findMin(arr));
    }
    private static int findMin(int[] arr){
        //当数据少于两个那么直接返回-1 ,因为不存在局部最小
        if (arr == null || arr.length <= 2){
            return -1;
        }
        //考虑最左位置
        if (arr[0] < arr[1]){
            return 0;
        }
        //考虑最右位置
        if (arr[arr.length - 1] < arr[arr.length -2]){
            return arr.length - 1;
        }
        //在中间位置查找
        return Find1(arr,0,arr.length - 1);
    }

    /**
     * 使用循环查找
     * @param arr   查找的数组
     * @return      如果返回-1那么就为没有局部最小
     */
    private static int Find(int[] arr){
        //上一个方法已经排除了特殊情况,那么直接查找即可
        //l标记数组头,r标记数组尾
        int l = 0,r = arr.length - 1;
        //mid标记中间,一开始可以不用赋初值。re作为放回值
        int mid = 0,re = -1;
        while (l < r){
            //这里做mid的赋值
            mid = l + ((r - l)>>1);
            //当中间这个数比左右任意一个数大,那么就在另一部分查找,大于哪边就去哪边查找
            if (arr[mid] > arr[mid + 1]){
                l = mid + 1;
            }else if (arr[mid] > arr[mid - 1]){
                r = mid - 1;
            }else{
                //当排除了前两种情况剩下的就为局部最小,也为re赋值,作为返回
                re = mid;
                break;
            }
        }
        return re;
    }

    /**
     * 使用递归做法
     * @param arr   查找的数组
     * @param l     左边的边界值
     * @param r     右边的边界值
     * @return      如果返回-1即为没有局部最小
     */
    private static int Find1(int[] arr,int l,int r){
        //mid标记中间
        int mid = l + ((r - l)>>1);
        //re作为返回值
        int re = -1;
        //因为局部最小不可能用一个数来比较,所以只需要递归到 l < r
        if (l < r){
            //当中间这个数比左右任意一个数大,那么就在另一部分查找,大于哪边就去哪边查找
            if (arr[mid] > arr[mid - 1]){
                re = Find1(arr,l,mid - 1);
            }else if (arr[mid] > arr[mid + 1]){
                re = Find1(arr,mid + 1,r);
            }else {
                re = mid;
            }
        }
        return re;
    }
}

查找一个数出现在数组中最左的位置

(最右与这个思路相同)

/**
 * @Author xiangcheng
 * @Date 2021/8/17 16:28
 * @Version 1.0
 */
/*
查找一个数出现在数组中最左的位置
(最右与这个思路相同)
 */
public class Test01 {
    public static void main(String[] args) {
        int[] arr = {1,2,4,4,4,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,8,11,32,34,53,67};
//        System.out.println(mer1(arr,5,0,arr.length - 1));
        System.out.println(mer2(arr,5));
    }
    //定义一个全局变量来记录查找数出现的位置,每查到一次就更新
    private static int re = -1;

    /**
     * 使用递归
     * @param arr   查找的数组
     * @param num   查找的数
     * @param l     标记最左边的位置
     * @param r     编辑最右边的位置
     * @return      最后一次查找到的位置,-1即为没找到
     */
    private static int mer1(int[] arr,int num,int l,int r){
        //定义mid标记中间位置
        int mid;
        //此时需要查到每个数上所以需要加=
        if (l <= r){
            mid = l + (r - l)/2;
            if (arr[mid] > num){
                mer1(arr,num,l,mid - 1);
            }else if (arr[mid] < num){
                mer1(arr,num,mid + 1,r);
            }else {
                //只有查找到才给mid更新位置
                re = mid;
                //继续向左边查找
                mer1(arr,num,l,r - 1);
            }
        }
        return re;
    }

    /**
     * 使用循环实现
     * @param arr   待查找的数组
     * @param num   查找的数
     * @return
     */
    private static int mer2(int[] arr,int num){
        //排除边界值
        if (arr == null|| arr.length == 0){
            return -1;
        }
        //l标记数组最左,r标记数组最右
        int l = 0,r = arr.length - 1;
        //mid标记中间
        int mid;
        //与递归的思路相同
        while (l <= r){
            mid = l + ((r - l)>>1);
            if (arr[mid] > num){
                r = mid - 1;
            }else if (arr[mid] < num){
                l = mid + 1;
            }else {
                re = mid;
                r = mid - 1;
            }
        }
        return re;
    }
}

旋转数组

给定一个有序循环数组arr,返回arr中的最小值。
有序循环数组是指,有序数组左边任意长度的部分放到右边去,右边的部分拿到左边来。
比如数组[1,2,3,3,4],是有序循环数组,[4,1,2,3,3]也是。

emmm使用递归的方法我写出来的有问题,有待后续补充

/**
 * @Author xiangcheng
 * @Date 2021/8/18 10:22
 * @Version 1.0
 */
/*
给定一个有序循环数组arr,返回arr中的最小值。
有序循环数组是指,有序数组左边任意长度的部分放到右边去,右边的部分拿到左边来。
比如数组[1,2,3,3,4],是有序循环数组,[4,1,2,3,3]也是。
 */
public class LoopArray {
    public static void main(String[] args) {
        int[] arr = {3,4,5,1,2,2,2,2,2,2};
        System.out.println(FindMin(arr));
    }

    /**
     * 对数组进行校验
     * @param arr   待查找的数组
     * @return      返回这个最小值
     */
    private static int FindMin(int[] arr){
        if (arr == null || arr.length == 0){
            return -1;
        }
        if (arr.length == 1){
            return 0;
        }
        return Find(arr);

    }

    /**
     * 使用循环进行查找
     * @param arr   待查找的数组
     * @return      最小值所在位置
     */
    private static int Find(int[] arr){
        int l = 0,r = arr.length - 1;
        int mid,re = -1;
        while (l <= r){
            mid = l + ((r - l)>>1);
            //当左边小于最右边,那么就说明在l到r这一段为有序
            if (arr[l] < arr[r]){
                re = l;
                break;
            }
            //当l与r之间距离为1,并且 arr[l] > arr[r] 就说明arr[r]就是这个最小值
            if (r - l== 1 && arr[l] > arr[r]){
                re = r;
                break;
            }
            //排除前两种情况,剩下就是经过旋转过的
            //当arr[mid] > arr[r]时,即中间位置的值大于末尾处的值,说明在mid-r中间经过了旋转
            if (arr[mid] > arr[r]){
                l = mid;
            }else if (arr[mid] < arr[l]){
                r = mid;
            } else {//排除了前两种情况,就说明左右两边的值与中间的值相等,那么最小值在两边出现的可能性都有,就只能循环查找
                re = arr[l];
                for (int i = l;i < r;i++){
                    re = Math.min(re,arr[i]);
                }
                break;
            }
        }
        return re;
    }
}

找arr[]==i

给定一个有序数组arr,其中不含有重复元素,请找到满足arr[]==i条件的最左的位置。如果所有位置上的数都不满足条件,返回-1。

/**
 * @Author xiangcheng
 * @Date 2021/8/18 11:22
 * @Version 1.0
 */
/*
给定一个有序数组arr,其中不含有重复元素,
请找到满足arr[]==i条件的最左的位置。
如果所有位置上的数都不满足条件,返回-1。
 */
public class FindOf {
    public static void main(String[] args) {
        int[] arr = {-1,0,2,4,5,6,7};
//        int[] arr = {0,1,2,4,5,6,7};
        System.out.println(FindTo(arr));
    }
    /**
     * 对数组进行校验
     * @param arr   待查找的数组
     * @return      返回arr[i]==i
     */
    private static int FindTo(int[] arr){
        if (arr == null || arr.length == 0){
            return -1;
        }
        if (arr.length == 1){
            return 0;
        }
//        return Find0(arr);
        return Find1(arr,0,arr.length - 1);
    }

    //记录最后一次出现满足条件的位置,用于返回
    private static int m = -1;

    /**
     * 使用循环进行查找
     * @param arr   待查找的数组
     * @return      arr[i] = i 所在位置
     */
    private static int Find0(int[] arr){
        //l标记数组头,r标记数组尾
        int l = 0,r = arr.length - 1;
        //mid标记中间,一开始可以不用赋初值
        int mid;
        while (l <= r){
            //这里做mid的赋值
            mid = l + ((r - l)>>1);
            //当中间这个值大于他的索引,就说明左边可能存在
            if (arr[mid] > mid){
                r = mid;
            //当中间这个值小于他的索引,就说明右边可能存在
            }else if (arr[mid] < mid){
                l = mid;
            }else {
                //剩下就是满足条件的
                m = mid;
                //再进行循环找到最左的位置
                r = mid - 1;
            }
        }
        return m;
    }

    private static int Find1(int[] arr,int l,int r){
        if (l <= r){
            int mid = l + ((r - l)>>1);
            //当中间这个值大于他的索引,就说明左边可能存在
            if (arr[mid] > mid){
                Find1(arr,l,mid - 1);
            //当中间这个值小于他的索引,就说明右边可能存在
            }else if (arr[mid] < mid){
                Find1(arr,mid + 1,r);
            }else {
                //剩下就是满足条件的
                m = mid;
                //再进行循环找到最左的位置
                Find1(arr,l,r - 1);
            }
        }
        return m;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值