二十天刷题计划--二分法(共三题)

137 阅读1分钟

「这是我参与2022首次更文挑战的第10天,活动详情查看:2022首次更文挑战」。

1.题目

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

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

进阶:

你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?  

示例 1:

输入:nums = [5,7,7,8,8,10], target = 8 输出:[3,4] 示例 2:

输入:nums = [5,7,7,8,8,10], target = 6 输出:[-1,-1] 示例 3:

输入:nums = [], target = 0 输出:[-1,-1]

思路

可能有多个目标值,所以先根据二分法缩小范围直到某个范围的中点值等于target,此时再向外扩张判断是否有周围是否有同样的target值。

代码

var searchRange = function(nums, target) {
  let left = 0;
  let right = nums.length - 1;
  while (left <= right) {
    let mid = Math.floor((left + right) / 2);
    if (nums[mid] === target) {
      left = mid - 1;
      right = mid + 1;
      while (true) {
        if (nums[left] === target) {
          left--;
        } else if (nums[right] === target) {
          right++;
        } else return [left + 1, right - 1];
      }
    }
    if (target < nums[mid]) {
      right = mid - 1;
    } else {
      left = mid + 1;
    }
  }

  return [-1, -1];
};

2.题目

搜索旋转排序数组

整数数组 nums 按升序排列,数组中的值 互不相同 。

在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。

 

示例 1:

输入:nums = [4,5,6,7,0,1,2], target = 0 输出:4

示例 2:

输入:nums = [4,5,6,7,0,1,2], target = 3 输出:-1

示例 3:

输入:nums = [1], target = 0 输出:-1

思路

由于题目说数字了无重复,举个例子:

1 2 3 4 5 6 7 可以大致分为两类,

第一类 2 3 4 5 6 7 1 这种,也就是 nums[start] <= nums[mid]。此例子中就是 2 <= 5。

这种情况下,前半部分有序。因此如果 nums[start] <=target<nums[mid],则在前半部分找,否则去后半部分找。

第二类 6 7 1 2 3 4 5 这种,也就是 nums[start] > nums[mid]。此例子中就是 6 > 2。

这种情况下,后半部分有序。因此如果 nums[mid] <target<=nums[end],则在后半部分找,否则去前半部分找。

总结:就始终在有序的范围内二分查找。

代码

let search = (nums, target) => {
    if(nums.length == 0) return -1;
    let l = 0, r = nums.length - 1;
    let mid;
    while(l <= r){
        mid = ~~((l + r) / 2);
        if(nums[mid] == target) return mid;
        
        if(nums[l] <= nums[mid]){ //这一半有序, 进一步判断 target是否在其中 
            if(target < nums[mid] && target >= nums[l]) r = mid - 1; // 在其中
            else l = mid + 1;
        }else{
            if(target > nums[mid] && target <= nums[r]) l = mid + 1;
            else r = mid - 1;
        }
    }
    return -1;
}

3.题目

搜索二维矩阵

编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:

每行中的整数从左到右按升序排列。 每行的第一个整数大于前一行的最后一个整数。

思路

先判断在哪一行之后在这一行内进行查找

代码

var searchMatrix = function(matrix, target) {
    let m = matrix.length;
    let n = matrix[0].length
    for(let i = 0; i < m; i++) { 
        if( matrix[i][n-1] >= target && target >= matrix[i][0] ) {
            console.log(i)
            for(let j = 0; j < n; j++) {
                if(matrix[i][j] === target) {
                    return true;
                }
            }
            return false
        }
    }
    return false
};