算法题(二分查找)

一、题目

1、寻找旋转排序数组中的最小值(LC 153)

2、寻找两个正序数字的中位数(LC 4)

二、题解

1、寻找旋转排序数组中的最小值(LC 153)

(1)分析

这道题的数组是经过旋转的有序数组,整体不再完全有序,但仍然保持了两段各自有序的特性,所以不能直接遍历,可以使用二分查找。利用二分不断缩小范围,最终定位到最小值的位置。

采用闭区间二分写法,先定义好左右两个指针,然后在循环中不断计算中间位置,核心判断逻辑是把中间元素和数组最后一个元素做比较。如果中间元素小于或等于末尾元素,就说明最小值一定出现在左半区间,这时候我就把右指针移到中间位置左边,继续向左寻找。如果中间元素大于末尾元素,就说明最小值在右半区间,于是把左指针移到中间位置右边,向右缩小范围。

只依靠中间元素和末尾元素的大小关系,就能一步步缩小搜索范围。当循环结束时,左指针指向的位置就是整个数组的最小值所在下标,直接返回对应元素即可。

(2)解答
java 复制代码
class Solution {
    public int findMin(int[] nums) {
        int n = nums.length;
        int left = 0;
        int right = n - 1;

        //二分查找 , 闭区间
        while(left <= right){
            int mid = (left + right)/2;
    
            if(nums[n - 1] >= nums[mid]){     //中间元素与末尾元素比较
                right = mid - 1;             //末尾元素大或者相等,去左边找
            }else{
                left = mid + 1;              //末尾元素小,去右边找
            }
        }
        return nums[left];
    }
}

2、寻找两个正序数字的中位数(LC 4)

(1)分析

这道题是二分查找里难度较高的一道题,题目最关键的要求就是时间复杂度必须控制在 O (log (m+n)) 以内,所以不能把两个数组合并后再查找,只能使用二分分割的思想来解决。需要抓住中位数的本质,就是把两个有序数组分成左右两部分,让左边的所有元素都小于右边的所有元素,再根据总长度的奇偶性计算结果。

可以先保证 nums1 是长度更短的那个数组,这样可以减少二分的次数。新建了两个数组,在原数组的最前面和最后面分别加上最小值和最大值,这样就不用在分割时特意判断数组越界的问题,写起来轻松很多。

接下来就是核心的分割,用两个变量分别表示两个数组的分割位置,保证左边所有元素的总个数接近总长度的一半。当满足左边最大元素小于右边最小元素时,就说明找到了正确的分割方式。如果两个数组总长度是奇数,中位数就是左边部分的最大值;如果是偶数,就取左边最大值和右边最小值的平均值。通过不断调整分割位置来满足条件,最终直接算出中位数。

(2)解答
java 复制代码
class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {

        //保证nums1是长度较短的数组
        if(nums1.length > nums2.length){
            int[] temp = nums1;
            nums1 = nums2;
            nums2 = temp;
        }

        int m = nums1.length;
        int n = nums2.length;
        int[] a = new int[m+2];
        int[] b = new int[n+2];
        a[0] = Integer.MIN_VALUE;
        b[0] = Integer.MIN_VALUE;
        a[m+1] = Integer.MAX_VALUE;
        b[n+1] = Integer.MAX_VALUE;

        //复制数组,避免繁琐的边界条件判断
        //nums1拷贝到a,nums2拷贝到b,同时在首尾分别加两个元素
        System.arraycopy(nums1, 0, a, 1, m);
        System.arraycopy(nums2, 0, b, 1, n);

        int i = 0;           //a[i] 分割后数组a左边最大的元素
        int j = (m+n+1)/2;   //b[j] 分割后数组b左边最大的元素
                             //i+j=(m+1+1)/2
        while(true){
            if(a[i] <= b[j + 1] && b[j] <= a[i+1]){  //符合条件的分割
            int max1 = Math.max(a[i], b[j]);     //左侧的最大值
            int min2 = Math.min(a[i+1], b[j+1]);  //右侧的最小值
            return (m+n)%2 > 0 ? max1 : (max1 + min2)/2.0;
            }
            i++;
            j--;
        }
    }
}
相关推荐
吃好睡好便好1 小时前
在Matlab中绘制马鞍函数曲面图
开发语言·人工智能·学习·算法·matlab·信息可视化
wa的一声哭了1 小时前
Mit6.s081 Interrupts and device driver(中断和设备驱动)
linux·服务器·arm开发·数据库·python·gpt·算法
luyun0202021 小时前
实用小工具,吾爱出品
开发语言·c++·算法
NNYSJYKJ1 小时前
K12 学习常见问题破解:脑能思维链的算法与教育应用
学习·算法
2301_789015622 小时前
Linux:基础指令(二)
linux·运维·服务器·c语言·开发语言·c++·算法
闻缺陷则喜何志丹2 小时前
【区间合并】P7912 [CSP-J 2021] 小熊的果篮|普及+
c++·算法·洛谷·区间合并
栈溢出了2 小时前
GIN学习笔记
人工智能·神经网络·算法·机器学习·gin
chenyuhao20242 小时前
AI agent 开发之嵌入模型和提示词 前置知识
人工智能·深度学习·算法·langchain·agent·ai应用开发
靠沿2 小时前
【递归、搜索与回溯算法】专题六——记忆化搜索
算法