LeetCode热题100——二分查找

文章目录

  • 1. 搜索插入位置
    • 1.1 题目链接
    • 1.2 题目描述
    • 1.3 解题代码
    • 1.4 解题思路
  • 2. 搜索二维矩阵
    • 2.1 题目链接
    • 2.2 题目描述
    • 2.3 解题代码
    • 2.4 解题思路
  • 3. 在排序数组中查找元素的第一个和最后一个位置
    • 3.1 题目链接
    • 3.2 题目描述
    • 3.3 解题代码
    • 3.4 解题思路
  • 4. 搜索旋转排序数组
    • 4.1 题目链接
    • 4.2 题目描述
    • 4.3 解题代码
    • 4.4 解题思路
  • 5. 寻找旋转排序数组中的最小值
    • 5.1 题目链接
    • 5.2 题目描述
    • 5.3 解题代码
    • 5.4 解题思路
  • 6. 寻找两个正序数组的中位数
    • 6.1 题目链接
    • 6.2 题目描述
    • 6.3 解题代码
    • 6.4 解题思路


1. 搜索插入位置

1.1 题目链接

点击跳转到题目位置

1.2 题目描述

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

提示:

  • 1 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • nums 为 无重复元素 的 升序 排列数组
  • -104 <= target <= 104

1.3 解题代码

class Solution {

    public int searchInsert(int[] nums, int target) {
        int ans = -1;
        int left = 0;
        int right = nums.length - 1;
        while(left <= right){
            int mid = ((left + right) >> 1);
            if(nums[mid] == target){
                ans = mid;
                break;
            } else if(nums[mid] < target){
                left = mid + 1;
            } else{
                ans = mid;
                right = mid - 1;
            }
        }
        if(ans == -1){
            ans = nums.length;
        }
        return ans;
    }
}

1.4 解题思路

1, 二分查找经典模板题目。

2. 搜索二维矩阵

2.1 题目链接

点击跳转到题目位置

2.2 题目描述

给你一个满足下述两条属性的 m x n 整数矩阵:

  • 每行中的整数从左到右按非严格递增顺序排列。
  • 每行的第一个整数大于前一行的最后一个整数。

给你一个整数 target ,如果 target 在矩阵中,返回 true ;否则,返回 false 。

提示:

  • m == matrix.length
  • n == matrix[i].length
  • 1 <= m, n <= 100
  • -104 <= matrix[i][j], target <= 104

2.3 解题代码

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length;
        int n = matrix[0].length;
        int left = 0;
        int right = m - 1;
        int row = -1;
        while(left <= right){
            int mid = ((left + right) >> 1);
            if(matrix[mid][0] == target){
                return true;
            } else if(matrix[mid][0] < target){
                row = mid;
                left = mid + 1;
            } else{
                right = mid - 1;
            }
        }
        if(row == -1){
            return false;
        }
        left = 0;
        right = n - 1;
        while(left <= right){
            int mid = ((left + right) >> 1);
            if(matrix[row][mid] == target){
                return true;
            } else if(matrix[row][mid] < target){
                left = mid + 1;
            } else{
                right = mid - 1;
            }
        }
        return false;
    }
}

2.4 解题思路

  1. 两次二分查找
  2. 第一次二分查找用来查找最左边一行不大于target的最大整数,确定行。
  3. 第二次二分查找用来再该行查找target。

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

3.1 题目链接

点击跳转到题目位置

3.2 题目描述

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

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

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

提示:

  • 0 <= nums.length <= 105
  • -109 <= nums[i] <= 109
  • nums 是一个非递减数组
  • -109 <= target <= 109

3.3 解题代码

class Solution {
    // 找到等于target的最小下标
    public int search1(int[] nums, int target){
        int n = nums.length;
        int left = 0;
        int right = n - 1;
        int ans = -1;
        while(left <= right){
            int mid = ((left + right) >> 1);
            if(nums[mid] > target){
                right = mid - 1;
            } else if(nums[mid] == target){
                right = mid - 1;
                ans = mid;
            } else{
                left = mid + 1;
            }
        }
        return ans;
    }
    // 找到等于target的最大下标
    public int search2(int[] nums, int target){
        int n = nums.length;
        int left = 0;
        int right = n - 1;
        int ans = -1;
        while(left <= right){
            int mid = ((left + right) >> 1);
            if(nums[mid] > target){
                right = mid - 1;
            } else if(nums[mid] == target){
                left = mid + 1;
                ans = mid;
            } else{
                left = mid + 1;
            }
        }
        return ans;
    }

    public int[] searchRange(int[] nums, int target) {
        int ans1 = search1(nums, target);
        int ans2 = search2(nums, target);

        return new int[]{ans1, ans2};
    }
}

3.4 解题思路

  1. 二分查找的经典题型。
  2. 两次二分查找,分别查找该值在数组中的最小下标和最大下标。

4. 搜索旋转排序数组

4.1 题目链接

点击跳转到题目位置

4.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 。

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

提示:

  • 1 <= nums.length <= 5000
  • -104 <= nums[i] <= 104
  • nums 中的每个值都 独一无二
  • 题目数据保证 nums 在预先未知的某个下标上进行了旋转
  • -104 <= target <= 104

4.3 解题代码

class Solution {
    public int search(int[] nums, int target) {
        int n = nums.length;
        int left = 0;
        int right = n - 1;
        int ans = -1;
        while(left <= right){
            int mid = ((left + right) >> 1);
            if(nums[mid] == target){
                ans = mid;
                break;
            } 
            // 下面确定哪一片区域是有序的
            if(nums[mid] < nums[0]){ // 右侧是有序的
                if(target < nums[0] && target >= nums[mid]){
                    left = mid + 1;
                } else{
                    right = mid - 1;
                }
            } else{ // 左侧是有序的
                if(target >= nums[0] && target < nums[mid]){
                    right = mid - 1;
                } else{
                    left = mid + 1;
                }
            }
        }
        return ans;
    }
}

4.4 解题思路

  1. 二分查找题。
  2. 值得注意的是,该题目有序的部分要么在mid左侧,要么在mid右侧,这个时候需要针对target是否在有序的一侧进行判断,如果不在有序的一侧则在另一侧进行查找,否则的话就在有序的一侧进行查找。

5. 寻找旋转排序数组中的最小值

5.1 题目链接

点击跳转到题目位置

5.2 题目描述

已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:

  • 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
  • 若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]
    注意,数组 [a[0], a[1], a[2], …, a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], …, a[n-2]] 。

给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素

提示:

  • n == nums.length
  • 1 <= n <= 5000
  • -5000 <= nums[i] <= 5000
  • nums 中的所有整数 互不相同
  • nums 原来是一个升序排序的数组,并进行了 1 至 n 次旋转

5.3 解题代码

class Solution {
    public int findMin(int[] nums) {
        int n = nums.length;
        int left = 0;
        int right = n - 1;
        int ans = nums[0];
        while(left <= right){
            int mid = ((left + right) >> 1);
            if(nums[mid] >= nums[0]){ // 往右侧找即可(因为最小的数初始化就是最左边的数)
                left = mid + 1;
            } else{
                ans = Math.min(ans, nums[mid]);
                right = mid - 1;
            }
        }
        return ans;
    }
}

5.4 解题思路

  1. 思路与第4题一致,主要是通过判定条件知晓去搜索哪一个区间。

6. 寻找两个正序数组的中位数

6.1 题目链接

点击跳转到题目位置

6.2 题目描述

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数

算法的时间复杂度应该为 O(log (m+n)) 。

提示:

  • nums1.length == m
  • nums2.length == n
  • 0 <= m <= 1000
  • 0 <= n <= 1000
  • 1 <= m + n <= 2000
  • -106 <= nums1[i], nums2[i] <= 106

6.3 解题代码

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int m = nums1.length;
        int n = nums2.length;
        int len = m + n;
        if((len & 1) == 1){ 
            int idx = len / 2;
            return getKthElement(nums1, nums2, idx + 1); // 获取第idx + 1小的元素
        } else{
            int idx = len / 2;
            return (getKthElement(nums1, nums2, idx) + getKthElement(nums1, nums2, idx + 1)) / 2.0;
        }
    }

    public int getKthElement(int[] nums1, int[] nums2, int k){// 获取第k小的元素
        int m = nums1.length;
        int n = nums2.length;
        int idx1 = 0;
        int idx2 = 0;

        while(true){
            if(idx1 == m){
                return nums2[idx2 + k - 1];
            }
            if(idx2 == n){
                return nums1[idx1 + k - 1];
            }
            if(k == 1){
                return Math.min(nums1[idx1], nums2[idx2]);
            }

            int half = k / 2;
            int newIdx1 = Math.min(idx1 + half, m) - 1; 
            int newIdx2 = Math.min(idx2 + half, n) - 1;
            int num1 = nums1[newIdx1];
            int num2 = nums2[newIdx2];
            if(num1 <= num2){
                k -= (newIdx1 - idx1 + 1);
                idx1 = newIdx1 + 1;
            } else{
                k -= (newIdx2 - idx2 + 1);
                idx2 = newIdx2 + 1;
            }
        }
    }
}

6.4 解题思路

  1. 对于求中位数,设两个数组的长度总和为len,如果len为奇数,则是求两个数组第len / 2 + 1小的数,如果len为偶数,则是求两个数组第len / 2 + 1小的数和第len / 2小的数的平均值。
  2. 所以问题转换成求两个数组的第k小的数,nums1的范围是从下标idx1 ~ m - 1,nums2的范围是从下标idx2 ~ n - 1。如果idx1已经越界了,则第k小的元素则是从nums2的可选范围内选取第k小的,对于nums2的越界情况也是如此。如果k等于1的时候,则是从nums1[idx1]和nums2[idx2]中选取一个最小值。
  3. 求解k的一半,记为half,则对于nums1,获取其范围内第k / 2小的元素,如果不存在则为 m - 1,对于nums2,获取其范围内第k / 2小的元素,如果不存在则为 n - 1。之后比较两个元素的大小,较小者的移动,并且去除这之间的所有元素(数量记为n),k 减去 n,下一轮即在新的范围内求解第k - n小的元素。

你可能感兴趣的:(LeetCode热题100,leetcode,算法,职场和发展)