二分查找无序数组arr中的局部最小值的位置

二分查找无序数组arr中的局部最小值的位置

提示:二分法系列文章
最基础的二分法:
二分法查找有序数组中的k所在的位置
二分法查找有序数组arr中,大于等于k的最左侧、最右侧的位置


题目

无数数组arr,一定保证任意位置i和i+1不相等
若[0]<[1],0位置算是局部最小
若[N-1]<[N-2],N-1位置算是局部最小
【注意,在今后的文章中,arr[i]=[i],我们用一个中括号简写即可,最开始你不习惯,但是你要习惯,慢慢就熟悉了】
其他的,所[i-1]>[i]且[i]<[i+1],则i位置是局部最小值。

请你找到病返回1个arr中的最小值。


一、审题

示例:arr=0 1 2 1 2 3 4 1 2;
显然,0位置就是局部最小值
212中间1那个位置也是局部最小值
只需要返回其中1个即可。


二、解题

这个题目,如果o(n)遍历,病按照条件判断也是可以的,但是就像前面找k一样,你是要费o(n)的时间的,n太大的话就失效了。

显然,查找这种事,就要瞬间想到二分查找,因为,二分真的真的很牛,能将算法复杂度降到o(log(n)),为啥不敏感地想到呢?

例如arr=3 1 2 1 2 3 4 1 2
(1)首先看0 1位置,显然3>1,3不会是局部最小
(2)再看N-2 N-1位置,显然,1<2,2不会是局部最小
(3)在L=1,R=N-2中二分查找
mid=0+8/2=4,mid处的2并不满足[i-1]>[i]且[i]<[i+1],故mid不是局部最小
——此时,随意判断,因为我们刚刚(1)看到了有一个[0]>[1],故如果此时[mid-1]<[mid]的话,令R=mid-1,这样子的话L–之间必然有一个是局部最小,大不了就是1位置。
——同理,刚刚我们看到了(2)[N-2]<[N-1],故此时再有[mid]<[mid+1]的话,令L=mid+1,这样子的话,L–R之间也必然有一个是局部最小,大不了就是N-2位置。
(4)一旦找到一个位置是局部最小,break即可,不找了。返回mid;
你仔细品,拿下面那个数组瞅瞅是不是这个道理!
在这里插入图片描述
手撕代码很简单:

//复习手撕代码:优化方法:二分法
    public static int findLocalSmallest2Review(int[] arr){
        if (arr == null || arr.length < 2) return -1;
        int N = arr.length;
        //看边界
        if (arr[0] < arr[1]) return 0;
        if (arr[N - 1] < arr[N - 2]) return N - 1;

        int L = 1;//0和N-1不是不管了
        int R = N - 2;
        int index = -1;//结果,找不到就是-1
        while (L <= R){
            int mid = L + ((R - L) >> 1);
            if (arr[mid - 1] < arr[mid]){
                //必然mid不是局部最小值,条件都不满足,那左边也必然有一个最小值
                R = mid - 1;
            }else if (arr[mid] > arr[mid + 1]){
                //也不然mid不是局部最小,右边必然,有一个是局部最小值
                L = mid + 1;
            }else {
                //上面俩条件都不满足,必然是arr[mid - 1] > arr[mid]且arr[mid] < arr[mid + 1]
                //这可不就是局部最小的定义吗
                index = mid;
                break;//找到了出去返回结果
            }
        }

        return index;
    }

自然有一个暴力解,但是,运气好的话,很快找到,如果运气不好,这个局部最小在R边上呢,N又很大,那就不行了
因此还是要二分法好。

//暴力解法,用于验证可靠性
    public static int findLocalSmallest1(int[] arr){
        //3种情况,第一种:0位置比1位置小,0位置是局部最小
        if(arr[0] < arr[1]) return 0;
        //第二种,N-1位置比N-2位置小,N-1位置是局部最小
        if(arr[arr.length - 1] < arr[arr.length -2]) return arr.length - 1;
        //从左往右遍历
        //第三种,i-1位置比i位置大,i+1位置比i位置大,i位置是局部最小
        int index = -1;
        for (int i = 1; i < arr.length-2; i++) {
            if(arr[i-1] > arr[i] && arr[i] < arr[i+1]){
                index = i;
                break;//找到就退出
            }
        }
        return index;
    }

测试代码:

//寻找一个局部最小的位置
//3种情况,第一种:0位置比1位置小,0位置是局部最小
//第二种,N-1位置比N-2位置小,N-1位置是局部最小
//第三种,i-1位置比i位置大,i+1位置比i位置大,i位置是局部最小
public class findLocalSmallest {
    public static void main(String[] args) {
        int[] arr = {2,1,3,1,2,1,5};//N=7
        int pos = findLocalSmallest1(arr);
        System.out.println(pos);
        int pos1 = findLocalSmallest2(arr);
        System.out.println(pos1);
        int pos3 = findLocalSmallest2Review(arr);
        System.out.println(pos3);
        //上面俩方法都是对的,只要返回其一即可,不一定,但是二分法的计算效率很高,所以很棒。
    }

总结

提示:重要经验:

1)理解清楚局部最小值的定义,则可以很快利用二分查找无序数组arr中的局部最小值的位置
2)二分查找的系列文章,这里暂时有3篇,前面开头那有2篇,一定好好理解,今后手撕不是问题,非常容易的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰露可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值