leetcode每日一题(2020-4-29):1095. 山脉数组中查找目标值

题目描述:(这是一个#交互式问题#)

给你一个 山脉数组 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.二分查找
2.在排序数组中查找元素的第一个和最后一个位置
3.位运算两数中点

1.二分查找:

二分查找要求:
1.必须采用顺序存储结构。
2.必须按关键字大小有序排列。

二分查找时间复杂度:
while循环的次数,即log(n)。

leetCode.704题(升序数组的二分查找)
非递归【参考了作者:oppeuro】:

var search = function(nums, target) {
  var start = 0;
  var end = nums.length;
  if(target < nums[start] || target > nums[end]){
  //因为升序,target只要超过两个边界即是不存在
    return -1
  }
  else{
  //执行二分查找函数
    return research(start, end-1, target, nums);
  }
  function research(l, r, target, nums){
  //m为l和r的中点,为向下取整,因为向右移会减一
    var m = (l + r) >> 1;
    //中点和终点重合结束循环,判断此时中/终点的值是否为target
    while(m < r){
      if(target == nums[m]){
        return m;
      }
      if(target > nums[m]){
        l = m + 1;
      }else if(target < nums[m]){
        r = m;
      }
      m =(l + r) >> 1;
    }
    return nums[m] == target ? l : -1;
  }
};

递归【参考百科】:

var search = function(nums, target) {
  var start = 0;
  var end = nums.length;
  return binary(start, end-1, target, nums);
  function binary(l, r, target, nums) {
    if (l <= r) {
        if (nums[l] == target) {
            return l;
        }
        if (nums[r] == target) {
            return r;
        }
        var m = (l + r) >> 1;
        if (nums[m] == target) {
            return m;
        } else if (nums[m] > target) {
            return binary(l, m-1, target, nums);
        } else {
            return binary(m+1, r, target, nums);
        }
    }
    return -1;
  }
};

2.在排序数组中查找元素的第一个和最后一个位置

leetCode.34题(在排序数组中查找元素的第一个和最后一个位置)
利用二分查找,若找到目标值则继续向前/后遍历是否还存在目标值。

var searchRange = function(nums, target) {
    let low =0
    let high = nums.length-1;
    let list = []
    while(low<high){
        let mid = (low + high) >> 1;
        if(nums[mid]>target){
            high = mid-1
        }else if(nums[mid]<target){
            low = mid+1
        }else{
            list[0] = mid;
            list[1] = mid;
            while(nums[mid-1]==target){
                list[0] = mid-1
                mid--
            }
            mid = list[1]
            while(nums[mid+1]==target){
                list[1] = mid+1;
                mid++
            }
            return list;
        }
    }
    return (nums[low]==target?[low,low]:[-1,-1])
};

3.1095山脉数组查找目标值

【参考作者:oppeuro】

/*
    第一次查 分3种情况:查到正中间,查到左边,查到右边
    1. 查到正中间(最简单的)左边递增右边递减,做两次二分即可。
    2. 查到左侧分情况 如果查的数小于当前值,(左侧 -> 当前做二分)-(当前值 -> 右侧 看成一个新的山脉数组)
    3. 查到右侧分情况 如果查的数大于当前值,(左侧 -> 当前值 看成一个新山脉数组)- (当前值 -> 右侧做二分)
*/
var findInMountainArray = function(target, mountainArr, start = 0, end = mountainArr.length()) {
    // 递归出口,只有两值的时候直接求解即可
    if(start > end) return -1;
    if(end - start == 2){
        if(mountainArr.get(start) == target) return start;
        if(mountainArr.get(end - 1) == target) return end - 1;
        return -1;
    }
    // 查找数组的范围, 中间值的前一值,中间值,中间值的后一值;通过这个3值确定当前位置。
    let l = start, r = end, m = (l + r) >> 1,
        last = mountainArr.get(m - 1), now = mountainArr.get(m), next = mountainArr.get(m + 1);
    // 正中间
    if(last < now && next < now){
        if(target > now) return -1;
        if(target == now) return m;
        let left = seachLowToHeight(l, m, target, mountainArr);
        return left == -1 ? seachHeightToLow(m + 1, r, target, mountainArr) : left;
    }
    // 左边
    if(last < now && now < next){
        if(target == now) return m;
        if(target < now){
            let left = seachLowToHeight(l, m, target, mountainArr)
            return left == -1 ? findInMountainArray(target, mountainArr, m + 1, r) : left;
        }
        if(target > now){
            return findInMountainArray(target, mountainArr, m + 1, r);
        }
    }
    // 右边
    if(last > now && now > next){
        let left = findInMountainArray(target, mountainArr, l, m);
        if(target == now){
            return left == -1 ? m : left;
        }
        if(target < now){
            return left == -1 ? seachHeightToLow(m + 1, r, target, mountainArr) : left;
        }
        if(target > now){
            return left == -1 ? seachHeightToLow(l, m, target, mountainArr) : left;
        }
    }
    return -1;
};
// 两个二分辅助函数,一个是小到大二分,一个是大到小二分
function seachLowToHeight(l, r, target, mountainArr){
    let m = (l + r) >> 1, val = mountainArr.get(m);
    while(l < r){
        if(val == target){
            return m;
        }
        if(val < target){
            l = m + 1;
        }else if(val > target){
            r = m;
        }
        m = (l + r) >> 1;
        val = mountainArr.get(m);
    }
    return val == target ? l : -1;
}
function seachHeightToLow(l, r, target, mountainArr){
    let m = (l + r) >> 1, val = mountainArr.get(m);
    while(l < r){
        if(val == target){
            return m;
        }
        if(val < target){
            r = m;
        }else if(val > target){
            l = m + 1;
        }
        m = (l + r) >> 1;
        val = mountainArr.get(m);
    }
    return val == target ? l : -1;
}

猜你喜欢

转载自www.cnblogs.com/autumn-starrysky/p/12805132.html