[leetcode日记]1095.在山脉中寻找目标

自己给自己想的方法取了名字,叫做“盲人爬山”hhhhh

题目

(这是一个 交互式问题 )

给你一个 山脉数组 mountainArr,请你返回能够使得 mountainArr.get(index) 等于 target 最小 的下标
index 值。

如果不存在这样的下标 index,就请返回 -1。

何为山脉数组?如果数组 A 是一个山脉数组的话,那它满足如下条件:

首先,A.length >= 3

其次,在 0 < i < A.length - 1 条件下,存在 i 使得:

A[0] < A[1] < … A[i-1] < A[i] A[i] > A[i+1] > … > A[A.length - 1]

你将 不能直接访问该山脉数组,必须通过 MountainArray 接口来获取数据:

MountainArray.get(k) - 会返回数组中索引为k 的元素(下标从 0 开始) MountainArray.length()

  • 会返回该数组的长度

注意:

对 MountainArray.get 发起超过 100
次调用的提交将被视为错误答案。此外,任何试图规避判题系统的解决方案都将会导致比赛资格被取消。

为了帮助大家更好地理解交互式问题,我们准备了一个样例
“答案”:https://leetcode-cn.com/playground/RKhe3ave,请注意这 不是一个正确答案。

示例 1:

输入:array = [1,2,3,4,5,3,1], target = 3 输出:2 解释:3 在数组中出现了两次,下标分别为 2 和
5,我们返回最小的下标 2。 示例 2:

输入:array = [0,1,2,4,2,1], target = 3 输出:-1 解释:3 在数组中没有出现,返回 -1。

提示:

3 <= mountain_arr.length() <= 10000 0 <= target <= 10^9 0 <=
mountain_arr.get(index) <= 10^9

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-in-mountain-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

分析:

看完题目,感觉和前些天另外一道题(搜索旋转排列数组)相当类似,这是传送门:我的另外一篇博文
不同之处就在于旋转排序数组切一刀以后是一个顺序数组和一个旋转排序数组;这题的山脉数组切一道以后是一个顺序数组(可能递增可能递减)和一个山脉数组。
善于学习归纳的我,当然是想着能不能用一样的办法来解决这个问题啦~
当时有两种方法可以解决问题:

  1. 先依次遍历找出旋转数组转折点
  2. 根据切一刀以后的性质来解决问题。

这道题目也是类似,因为之前实验结果表明,法一效率上优于法二,所以这题我先实验的可能效果会更差的法二(这样我就能督促我写全两种办法了)

法二代码

这种方法我在写的时候,结合这道题目的语境,想了一个方法名字(叫做盲人爬山)。我每次选取数组中一个元素的时候,因为我是一个在山中的盲人,不知道怎么向上,也不知道怎么向下,那么最合适的方法就是

取一个相邻的点,判断出这个点的梯度方向,从而判断出在山中的位置

叫做盲人爬山是不是非常地贴切。如果觉得贴切就请在评论区刷一下“真是个小机灵鬼”

#define MAX_SIZE 10000

int find_target(MountainArray *mountainArr , int flag , int left ,int right, int target){
     int mid,num;
//     printf("Begin to find!\n");
     if (flag == 1){
         while (left < right){
             mid = (left+right)/2;
             num = get(mountainArr,mid);
             if (num == target){
                 return mid;
             } else if (num > target){
                 right = mid; 
             } else {
                 left = mid+1; 
             }
         }
     } else if (flag == -1){
         while (left < right){
             mid = (left+right+1)/2;
             num = get(mountainArr,mid);
             if (num == target){
                 return mid;
             } else if (num > target){
                 left = mid; 
             } else {
                 right = mid-1; 
             }
         }
     }
     num = get(mountainArr,right);
     if (num == target) return right;
     return -1;
 }

int findInMountainArray(int target, MountainArray* mountainArr) {
	int len = length(mountainArr);
    int index = -1,result = MAX_SIZE; 	//result用于暂存结果(因为题目中要求数字必须是第一个出现的)
    int left = 0,right = len - 1; 

    while (left < right && left < result){	//考虑剪枝
//    	printf("%d\t%d\n",left,right); 
        int num1 = get (mountainArr , (left+right+1)/2); 
        if (num1 == target){
        	result = (left+right+1)/2 ;
		}
        int num2 = get (mountainArr , (left+right-1)/2); 
        if (num2 == target)
        	result = (left+right-1)/2 ; 

        if (num1 > num2){
            if (num1 >= target){
                index = find_target(mountainArr , 1 , left , (left+right-1)/2 ,target) ;   
                if (index != -1 ){
                    return index;  
                }
                left = (left+right+1)/2 ; 
                continue ;
            } else if (num1 < target){
                left = (left+right+1)/2 + 1;
                continue ; 
            }
        } else if (num1 < num2){
            if (num1 > target){
                index = find_target(mountainArr, -1 , (left+right+1)/2 + 1, right,target) ;  
                if (index != -1 && index < result){
                    if (result == MAX_SIZE){
                    	result = index;
                    	right = index;
					}
                    else return result;    
                }
                right =  (left+right-1)/2;             
            } else if (num1 <= target){
                right = (left+right-1)/2 ; 
            }
        }
    }
    if (result == MAX_SIZE)
        return -1;
    else return result; 
}

写完以后明显感觉有些糟糕,一些边界处理的效率太低了;虽然感觉边界距离完美还有很大的差距,但是在数组元素较多的时候并不会对效率产生很大影响。不过因为已经写了蛮久,决定还是先跑通过以后尝试一下另外一种方法。

法二运行结果:

法二
结果果然不乐观,接下来试试法一。也就是二分法找到山脉的peak以后再分左右二分查找。这样子效率会稳定很多,而且边界处理会容易很多。

法一代码

代码是参考评论区写的,不完全一样,但是大致思路是相同的,都用到了将函数当作参数进行传递。这种方法我不太熟练。
貌似这道题评论区都是清一色的方法一……要知道之前的那道题的评论区可是清一色的方法二啊……

/**
 * *********************************************************************
 * // This is the MountainArray's API interface.
 * // You should not implement it, or speculate about its implementation
 * *********************************************************************
 *
 * int get(MountainArray *, int index);
 * int length(MountainArray *);
 */
typedef int (*func)(int);
int binarySearch(int target, int left, int right, func key, MountainArray* mountainArr) {
        target = key(target);
        while (left <= right) {
            int middle = (left + right) / 2;
            int current = key(get(mountainArr, middle));
            if (current == target)
                return middle;
            else if (current < target)
                left = middle + 1;
            else
                right = middle - 1;
        }
        return -1;
}

int func1(int x) {
    return x;
}

int func2(int x) {
    return -x;
} 

int findInMountainArray(int target, MountainArray* mountainArr) {
	    int left = 0, right = length(mountainArr) - 1;
        while (left < right) {
            int middle = (left + right) / 2;
            if (get(mountainArr, middle) < get(mountainArr, middle + 1)) {
                left = middle + 1;
            } else {
                right = middle;
            }
        }
        
        int peak = left;
        int index = binarySearch(target, 0, peak, func1, mountainArr);
        if (index != -1) {
            return index;
        }
        return binarySearch(target, peak + 1, length(mountainArr) - 1, func2, mountainArr);
}

法一运行结果

法一
明显比法二要好一些,和预期的一样。那能不能再进行一些优化呢?
熟悉我的小伙伴肯定已经猜到,我都这么问了肯定能优化啊……
那是我再优化其实也只是在法一的基础上进行了自己的重写,不能算是方法三,因为思路相同与方法一:

代码

#define MIN(a, b) (a) < (b) ? (a) : (b);
int FindLeftTarget(int target, MountainArray* mountainArr, int begin, int end)
{
    int left = begin;
    int right = end;
    int mid = (left + right) / 2;
    int val = 0;
    if (get(mountainArr, left) == target) {
        return left;
    }
    if (get(mountainArr, right) == target) {
        return right;
    }
    while (left != mid) {
        val = get(mountainArr, mid);
        if (val < target) {
            left = mid;
        }else if (val > target) {
            right = mid;
        }else {
            return mid;
        }
        mid = (left + right) / 2;
    }
    return -1;
}
int FindRightTarget(int target, MountainArray* mountainArr, int begin, int end)
{
    int left = begin;
    int right = end;
    int mid = (left + right) / 2;
    int val = 0;
    if (get(mountainArr, left) == target) {
        return left;
    }
    if (get(mountainArr, right) == target) {
        return right;
    }
    while (left != mid) {
        val = get(mountainArr, mid);
        if (val < target) {
            right = mid;
        }else if (val > target) {
            left = mid;
        }else {
            return mid;
        }
        mid = (left + right) / 2;
    }
    return -1;
}

int FindTop(MountainArray* mountainArr)
{
    int len = length(mountainArr);
    int left = 0;
    int right = len - 1;
    int mid = (left + right) / 2;
    int leftVal = 0;
    int midVal = 0;
    int rightVal = 0;    
    while (left != mid) {
        leftVal = get(mountainArr, mid - 1);
        midVal = get(mountainArr, mid);
        rightVal = get(mountainArr, mid + 1);
        if ((leftVal < midVal) && (midVal < rightVal)) {
            left = mid;
        }else if ((leftVal > midVal) && (midVal > rightVal)) {
            right = mid;
        } else {
            return mid;
        }
        mid = (left + right) / 2;
    }
    return -1;
}

int findInMountainArray(int target, MountainArray* mountainArr){
	int top = 0;
    int len = length(mountainArr);
    int index = len;
    int temp = 0;
    top = FindTop(mountainArr);
    temp = FindLeftTarget(target, mountainArr, 0, top);
    if (temp != -1) {
        index = MIN(temp, index);
    }
    temp = FindRightTarget(target, mountainArr, top, len - 1);
    if (temp != -1) {
        index = MIN(temp, index);
    }
    if (index == len) {
        return -1;
    }
    return index;
}

运行结果

法3
就结果上而言确实没有多大的提升了,不过我自己感觉有提升就好!
加油!明天会更好!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

邵政道

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

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

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

打赏作者

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

抵扣说明:

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

余额充值