二分查找

点击查看代码
    //闭合区间二分查找
    public int Binary_Search_1(int[] num, int target){
        int left = 0;
        int right = num.length - 1;
        while(left <= right){
            int mid = left + (right - left)/2;
            if(num[mid] < target){
                left = mid + 1;
            }
            else{
                right = mid - 1;
            }
        }
        return left;//return right+1;
    }
    //左闭右开二分查找
    public int Binary_search_2(int[] num, int target){
        int left = 0;
        int right = num.length;
        while(left < right){
            int mid = left + (right - left)/2;
            if(num[mid] < target){
                left = mid + 1;
            }
            else{
                right = mid;
            }
        }
        return left;//return right;
    }
    //开区间二分查找
    public int Binary_Search_3(int[] num,int target){
        int left = -1;
        int right = num.length;
        while(left+1 < right){
            int mid = left + (right - left)/2;
            if(num[mid] < target){
                left = mid;
            }
            else{
                right = mid;
            }
        }
        return right;
    }

74.搜索二维矩阵

第一次提交index超出边界,直接使用index搜索数值是错误的。

点击查看代码
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length;
        int n = matrix[0].length;
        for(int i = 0;i < m ; i++){
            int index = Binary_Search(matrix[i],target);
            if(matrix[i][index] == target){//error,正确写法为if(index < n && matrix[i][index] == target)
                return true;
            }
        }
        return false;
    }
    public int Binary_Search(int[] nums,int target){
        int left = 0 ;
        int right = nums.length;
        while(left < right){
            int mid = left + (right - left)/2;
            if(nums[mid] < target){
                left = mid + 1 ;
            }
            else{
                right = mid;
            }
        }
        return left;
    }
}

第二次提交优化先判断是否需要使用二分查找

点击查看代码
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length;
        int n = matrix[0].length;
        for(int i = 0;i < m ; i++){
            if(matrix[i][n-1] < target){//免除不必要的二分查找。
                continue;
            }
            int index = Binary_Search(matrix[i],target);
            if(index < n && matrix[i][index] == target){
                return true;
            }
        }
        return false;
    }
    public int Binary_Search(int[] nums,int target){
        int left = 0 ;
        int right = nums.length;
        while(left < right){
            int mid = left + (right - left)/2;
            if(nums[mid] < target){
                left = mid + 1 ;
            }
            else{
                right = mid;
            }
        }
        return left;
    }
}

第三次提交,坐标映射,二维矩阵可以看作一维数组。

点击查看代码
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length;
        int n = matrix[0].length;
        int index = Binary_Search(matrix,target);
        int row = index/n;
        int col = index%n;
        if(index < m*n && matrix[row][col] == target){
            return true;
        }
        return false;
    }
    public int Binary_Search(int[][] nums,int target){                                                 
        int m = nums.length;
        int n = nums[0].length;
        int left = 0;
        int right = m*n;
        while(left < right){
            int mid = left + (right - left)/2;
            int row = mid/n;
            int col = mid%n;
            if(nums[row][col] < target){
                left = mid + 1;
            }
            else{
                right = mid;
            }
        }
        return left;
    }
}

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

两次二分查找,找target和target+1

点击查看代码
class Solution {
    public int[] searchRange(int[] nums, int target) {
        int[] res = new int[]{-1,-1};
        int start_index = Binary_Search(nums,target);
        int end_index = Binary_Search(nums,target + 1) - 1;
        if(start_index < nums.length){
            if(nums[start_index] == target){
                res[0] = start_index;
                res[1] = end_index;
                return res;
            }
        }
        return res;
    }
    public int Binary_Search(int[] nums, int target){
        int left = 0;
        int right = nums.length;
        while(left < right){
            int mid = left + (right - left)/2;
            if(nums[mid] < target){
                left = mid + 1;
            }
            else{
                right = mid;
            }
        }
        return left;
    }
}

33.搜索旋转排序数组

难题啊,思路:二分查找就是为了每次减少一半的数组,现在数组不是有序的了,我们无法直接通过去左半边还是右半边。我们现在只能先看对于当前的mid而言,左半边是有序的or右半边是有序的。ok如果左半边有序,左半边有序就是我们的依据,根据左半边有序看一下左半边是否包含目标值,包含就放到左区间,不包含就放到右区间。

我们只能通过相信一半的有序区间来确定到底怎么移动指针。

点击查看代码
class Solution {
    public int search(int[] nums, int target) {
        int left = 0;
        int right = nums.length-1;

        while(left <= right){
            int mid = left + (right - left)/2;
            //1.找到target直接返回
            if(nums[mid] == target){
                return mid;
            }
            //2.先看左边有序还是右边有序
                //2.1左边有序区间!       
            if(nums[mid] >= nums[left]){
                    //如果左侧有序区间包含target,移动右指针到左侧有序空间。
                    //左侧有序区间不包含target,移动左指针到右侧无序区间。
                if(nums[left] <= target && target < nums[mid]){
                    right = mid - 1;
                }
                else{
                    left = mid + 1;
                }
            }
                //2.2右边有序区间!
            else{
                    //如果右侧有序区间包含target,移动左指针到右侧有序区间。
                    //右侧有序区间不包含target,移动右指针到左侧无序区间。
                if(nums[mid] < target && target <= nums[right]){
                    left = mid + 1;
                }
                else{
                    right = mid - 1;
                }
            }
        }
        return -1;
    }
}

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

思路:ok依旧旋转排序数组,看着就不会做。狐假虎威!其实非常简单,只需要明白旋转点=断裂点=最小值的点。当前mid的值小于最右边的值,那最小值只有可能是在左边区间或者mid这个值上,移动右指针到左区间。当前mid的值大于最右边的值,那断裂点肯定在右区间,移动指针到右区间并跳过当前值。

153不是一个标准的“找精确值”的问题,而是一个“找边界”或“找结构断点”的问题,必须使用保留性,也就是所谓的开闭区间。当二分查找的目的是寻找一个边界、最小值或插入位置时,我们倾向于使用 while (left < right) 配合 right = mid 的保留性逻辑,以确保答案不会被跳过。这是由问题本身的结构需求所决定的。

初始化只是定义了闭区间,实际上依然是左闭右开的区间选择。实际上如何定义区间取决于我们的目的,我们究竟要得到什么?

点击查看代码
class Solution {
    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]){
                left = mid + 1;
            }
            else{
                right = mid;
            }   
        }
        return nums[left];
    }
}

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

思路:没有模板可言,依旧二分查找。

主函数通过辅助函数求的K小的值返回最终结果。
辅助函数寻找两个数组中第K小的数:二分查找在于每次扔一半也就是K的二分之1,看是nums1的一半还是nums2的一半,比较每个数组的前二分之K个的数,然后删除那个结尾最小的数组的一部分。循环删除,直到终止条件满足

点击查看代码
class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int n = nums1.length;
        int m = nums2.length;
        int total = n + m;
        if(total%2 == 1){
            // 奇数
            return find_k_min(nums1,nums2,total/2 + 1);
        }
        else{
            // 偶数
            return (find_k_min(nums1,nums2,total/2)+find_k_min(nums1,nums2,total/2+1)) / 2.0;
        }
    }
    
    public int find_k_min(int[] nums1, int[] nums2, int K){
        int index1 = 0;
        int index2 = 0;
        while(true){
            
            //1. 终止条件

            // 1.1 【优先判断】数组耗尽:如果 index 已经走到数组末尾
            if(index1 == nums1.length){
                return nums2[index2 + K - 1]; 
            }
            if(index2 == nums2.length){
                return nums1[index1 + K - 1];
            }
                
            // 1.2 【其次判断】K=1:两个数组都还有元素,直接比较起点
            if(K == 1){
                return Math.min(nums1[index1], nums2[index2]);
            }

            //2. 分治思路

            int half = K/2;
                
            // 确定要比较的索引 p1 和 p2
            // 使用 Math.min(index + half, length) - 1 来确保索引不会越界
            int p1 = Math.min(index1 + half, nums1.length) - 1; 
            int p2 = Math.min(index2 + half, nums2.length) - 1;
                
            // 比较删除谁的数并更改索引
            if(nums1[p1] < nums2[p2]){
                int eliminatedCount = (p1 - index1 + 1); 
                    
                K = K - eliminatedCount;
                index1 = p1 + 1; 
            }
            else{
                int eliminatedCount = (p2 - index2 + 1);
                    
                K = K - eliminatedCount;
                index2 = p2 + 1;
            }
        }
    }
}
posted @ 2025-11-23 17:38  柳成荫y  阅读(5)  评论(0)    收藏  举报