guodongxiaren / OJ

4 stars 3 forks source link

LeetCode 1095: 山脉数组中查找目标值 #26

Open guodongxiaren opened 4 years ago

guodongxiaren commented 4 years ago

https://leetcode-cn.com/problems/find-in-mountain-array/

(这是一个交互式问题) 给你一个 山脉数组 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
guodongxiaren commented 4 years ago

这道题和 搜索旋转排序数组 有点类似。都是二分法。 但也有不同,大体思路是:

一共是3次二分查找的过程。另外需要注意因为是最小下标优先,所以不洗左右山坡都查找完了再比较索引,先搜左边,找到的话就不搜右边了。

这个小技巧我一开始,竟然没意识到。所以说刷算法题,即使算法本身在工作中遇不到。在刷题过程中也能提高一些潜移默化的技巧,帮助你写业务逻辑代码的时候bug-free,或者更为简洁明快。

二分查找的小技巧

guodongxiaren commented 4 years ago
/**
 * // This is the MountainArray's API interface.
 * // You should not implement it, or speculate about its implementation
 * class MountainArray {
 *   public:
 *     int get(int index);
 *     int length();
 * };
 */

class Solution {
public:

    int findInMountainArray(int target, MountainArray &mountainArr) {
       int size = mountainArr.length();
        int top = findTop(mountainArr, 0, size - 1);
        int index = bsearch(mountainArr, 0, top, target, less<int>());
        if (index < 0) {
            index = bsearch(mountainArr, top, size - 1, target, greater<int>());
        }
        return index;
    }

    int findTop(MountainArray &mountainArr, int left, int right) {
        int mid = (left + right) / 2;
        if (left == right) {
            return -1;
        }
        // 左坡
        if (mountainArr.get(mid) < mountainArr.get(mid+1)) {
            return findTop(mountainArr, mid, right);
        } else { // 右坡
            if (mountainArr.get(mid) > mountainArr.get(mid-1)) {
                return mid;
            }
            return findTop(mountainArr, left, mid);
        }
    }

    template <typename CMP>
    int bsearch(MountainArray &m, int left, int right, int target, CMP cmp) {
        while (left <= right) {
            int mid = (left + right) / 2;
            if (target == m.get(mid)) {
                return mid;
            }

            if (cmp(target, m.get(mid))) {
                right = mid-1;
            } else {
                left = mid+1;
            }
        }
        return -1;
    }
};

本题我分别用递归和遍历实现了二分的过程。其实遍历的写法更好一些,也不易出错其实。

另外就是我用了greater和less的STL算法!