LeetCode 1095. 山脉数组中查找目标值/二分搜索

此题来自LeetCode1095. 山脉数组中查找目标值
题目说的很露骨了,无论是从两半有序,还是从不多于100次的索引+数据量都可以看出这是个二分搜索。

通过方法1可以看出,我们利用数组不完全一致有序的性质使用搜索,不断的排除掉不可能的区域,不断地缩小搜索区域,这也是二分搜索。

方法1:

  • 先用二分找到山顶;
  • 在用二分在0~山顶的元素里面找,找不到进行3;
  • 用二分在山顶~end的元素里找。
  • 这里找到山顶使用的二分可以使用减治的二分法,当mid,山峰肯定在mid右边,否则在左边(或者是其本身),且这个时候写成减治的二分法的时候要注意几点:1.在这要取mid+1,所以范围必须是[L,R] 2.循环设置成while(left < right),循环终止时left==right,但这个数并没有检查是不是,所以最后要判断下 3.尽量不要让mid放在右边,即不要把范围分割成[L,M-1][M,R]有可能会进入死循环,因为L=M的话就会出不断循环的问题。

AC代码:

#include 
using namespace std;
class Solution {
public:
    int topPos, target;
    // [L..R] 
    int findMountainTop(int L, int R, MountainArray &mountainArr) {
        while (L < R) {
            // 这个写法是在循环体内部不断排除元素,最后剩下的 L 或者 R 就是山顶的下标
            int M = L + (R - L) / 2;
            // 左中位数
            if (mountainArr.get(M) < mountainArr.get(M + 1)) {
                // 下一轮搜索区间在 [M + 1, R]
                L = M + 1;
            } else {
                // 下一轮搜索区间在 [L, M]
                R = M;
            }
        }
        return R;
    }

    // [L..R]
    int SearchTargetLeft(int L, int R, MountainArray &mountainArr) {
        while (L < R) {
            // 这种写法是在循环体内部就找目标元素,所以先写等于的情况,不满足的时候向左边找或者向右边找
            // M 的写法最好要统一
            int M = (L + R) / 2;
            int mid = mountainArr.get(M);
            if (mid == target) {
                return M;
            } else if (mid > target) {
                // 下一轮搜索区间在 [L, M - 1]
                R = M;
            } else {
                // 下一轮搜索区间在 [M + 1, R]
                L = M + 1;
            }
        }
        return -1;
    }
    // [L..R]
    int SearchTargetRight(int L, int R, MountainArray &mountainArr) {
        while (L < R) {
            // M 的写法最好要统一
            int M = (L + R) / 2;
            // 下面这两行代码没有用
            // int left = mountainArr.get(L);
            // int right = mountainArr.get(R - 1);
            int mid = mountainArr.get(M);
            if (mid == target) {
                return M;
            } else if (mid < target) {
                // 下一轮搜索区间在 [L, M - 1]
                R = M;
            } else {
                // 下一轮搜索区间在 [M + 1, R]
                L = M + 1;
            }
        }
        return -1;
    }

    int findInMountainArray(int target, MountainArray &mountainArr) {
        int n = mountainArr.length();
        this->target = target;

        topPos = findMountainTop(0, n - 1, mountainArr);
        // cout << topPos;
        int t = SearchTargetLeft(0, topPos + 1, mountainArr);
        if (t != -1) {
            return t;
        } else {
            return SearchTargetRight(topPos + 1, n, mountainArr);
        }
    }
};

方法2:

  • 其实这题困扰的是山峰的位置,也就是说山峰和搜索区间的相对位置,从而影响了我的区间缩小选择,那么很简单,将所有情况列出来依次讨论就可以了,关于二分分类的另一道简单题:https://blog.csdn.net/weixin_43510267/article/details/105802177
  • 如果mid == target,将下标push到小顶堆里,但不能急着返回,因为还有可能有另一个相同的数在山的另一边。
  • 如果left < mid < right或者left > mid > right,分别讨论target和mid的关系就可以了(因为这时候山峰并不会影响),但是target是有可能等于mid的,因为可能target有个相同的值在另一个山峰,所以缩小的区间的时候一定要考虑。`
  • 如果left < mid > right,这是最麻烦的情况,因为怎么讨论都没用,山峰的位置并不确定,无论target与mid关系如何,两边都有可能存在target,那么两边都搜呗!!!

方法2AC代码:

class Solution {
public:
    int target;
    priority_queue<int,vector<int>,greater<int>> ans;
    int findInMountainArray(int target, MountainArray &mountainArr) { 
        //binary search
        int n = mountainArr.length();
        if(n == 0) return -1;
        this->target = target;
        binary_search(0,n,mountainArr);  
        return ans.empty() ? -1 : ans.top();                                                            
    }

    void binary_search(int L,int R,MountainArray &mountainArr){
        if(L >= R) return;
        int M = (L + R) / 2; //cout << M << " ";
        int left = mountainArr.get(L);
        int right = mountainArr.get(R - 1);//注意搜索范围为[L,R)
        int mid = mountainArr.get(M);
        if(mid == target){
            ans.push(M);  //不能直接返回 因为序列可能还有一个一模一样的数 在另一半
        } 
        if(left <= mid  && mid <= right){
            if(target > mid) binary_search(M + 1,R,mountainArr);
            else binary_search(L,M,mountainArr);
        }
        else if(left >= mid && mid >= right){
            //还要考虑相等
            if(target >= mid) binary_search(L,M,mountainArr);
            else binary_search(M + 1,R,mountainArr);
        }
        else{ //left < mid > right 无论target和mid的关系是怎样的,两边都有可能,因为山峰的位置并不确定 
            binary_search(L,M,mountainArr);
            binary_search(M + 1,R,mountainArr); 
        }
        return;
    }
};

你可能感兴趣的:(Binary,二分法,leetcode,c++,算法)