【二分查找】【中等】搜索二维矩阵/排序数组的第一位和最后位置/搜索旋转排序数组/旋转排序数组中的最小值

1.搜索二维矩阵

原题链接

  • 注意要求代码的时间复杂度为 O(log(m * n))
  • 对于整体的数组进行二分查找,在定位到数组中的位置
java 复制代码
public boolean searchMatrix(int[][] matrix, int target) {
    int m = matrix.length;
    int n = matrix[0].length;
    int left = 0;
    int right = m * n - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (matrix[mid / n][mid % n] == target) {
            return true;
        }
        if (matrix[mid / n][mid % n] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return false;
}

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

原题链接

  • 题目要求时间复杂度是 O(log n)
  • 进行两次二分查找:寻找两个边界
    • 第一次二分:找最左边的 target
    • 第二次二分:找最右边的 target
java 复制代码
public int[] searchRange(int[] nums, int target) {
    int start = findStart(nums, target);
    int end = findEnd(nums, target);
    return new int[]{start, end};
}
//寻找左端点
private int findStart(int[] nums, int target) {
    int ans = -1;
    int left = 0;
    int right = nums.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            ans = mid;
            right = mid - 1;
        }
        if (nums[mid] < target) {
            left = mid + 1;
        }
        if (nums[mid] > target) {
            right = mid - 1;
        }
    }
    return ans;
}
//寻找右端点
private int findEnd(int[] nums, int target){
    int ans = -1;
    int left = 0;
    int right = nums.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            ans = mid;
            left = mid + 1;
        }
        if (nums[mid] < target) {
            left = mid + 1;
        }
        if (nums[mid] > target) {
            right = mid - 1;
        }
    }
    return ans;
}

简化后的代码

java 复制代码
public int[] searchRange(int[] nums, int target) {
    int first = lowerBound(nums, target);
    // 没找到 target
    if (first == nums.length || nums[first] != target) {
        return new int[]{-1, -1};
    }
    int last = lowerBound(nums, target + 1) - 1;
    return new int[]{first, last};
}
// 找第一个 >= target 的位置
private int lowerBound(int[] nums, int target) {
    int left = 0;
    int right = nums.length;
    while (left < right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] >= target) {
            right = mid;
        } else {
            left = mid + 1;
        }
    }
    return left;
}

3.搜索旋转排序数组⭐

原题链接

  • 每次通过 mid 进行二分查找的时候,得到的结果要么 左半部分有序右半部分无序 或者 左半部分无序右半部分有序
  • nums 中的每个值都 独一无二,因此不存在重叠范围的问题
  • 可以通过 left、mid、right 的值判断左右有序的数组,如果 target 在有序数组中则直接二分查找,否则在递归进行判断
bash 复制代码
下标: 0 1 2 3 4 5 6
数组:[4,5,6,7,0,1,2]
            ↑
           mid
java 复制代码
public int search(int[] nums, int target) {
    int left = 0;
    int right = nums.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            return mid;
        }
        //判断是否左有序
        if (nums[left] <= nums[mid]) {
            //判断target是否在左有序区间
            if (target >= nums[left] && target <= nums[mid]) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        } else {
            //判断target是否在右有序区间
            if (target >= nums[mid] && target <= nums[right]) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
    }
    return -1;
}

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

原题链接

  • 最小值其实就是两个有序部分的分界点。
  • 同样二分查找中,min将数组分为左右两侧,左有序右无序或左五序右有序
  • 无序的一侧则继续递归
java 复制代码
public int findMin(int[] nums) {
    int left = 0;
    int right = nums.length - 1;
    while (left < right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] > nums[right]) {
            // 最小值一定在 mid 右边
            left = mid + 1;
        } else {
            // 最小值可能是 mid,也可能在 mid 左边
            right = mid;
        }
    }
    return nums[left];
}
相关推荐
特立独行的猫A17 小时前
用仓颉语言 cj-tauri 框架,五分钟给娃做了个鸿蒙形近字游戏
java·后端·python
I_belong_to_jesus21 小时前
C++关于分支预测的优化1
开发语言·c++·算法
budingxiaomoli1 天前
Spring统一功能处理
java·后端·spring
Flynn Liang1 天前
可观测性与成本治理:一次对话的 17 个观测点
java·langchain·可观测性·micrometer·langchain4j·token 成本·llm 监控
金銀銅鐵1 天前
带您看懂字节码指令(三):同一行加法,为什么有四种指令
java·jvm
Code_Solitude1 天前
C语言动态内存 + 柔性数组 + 数组名退化
c语言·算法·柔性数组
上海潭龙东海科技有限公司1 天前
客运站管控之动态孪生技术方案(新增一屏管控模块
数据仓库·人工智能·算法·重构·动态规划
文丑颜不良啊1 天前
LeetCode2821~2840题目解析
java·leetcode
apcipot_rain1 天前
C++转Java日记(10)
java·开发语言