【LeetCode 热题 100】二分查找 系列

📁 35. 搜索插入位置

        将数组划分为两个区间[0 , left] [right , n],其中有 左区间的元素都小于targe,右区间的元素都大于等于target。 

        left指针维护左区间结束位置,right维护右区间的起始位置,right所在位置一定是第一个大于等于target的位置

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int left = 0 , right = nums.size();
        while(left < right)
        {
            int mid = (left + right) >> 1;
            if(nums[mid] < target)
                left = mid + 1;
            else    
                right = mid;

        }
        return left; 循环结束 此时left==right
    }
};

📁 74. 搜索二维矩阵

        这一道题目还是类似上一道题目,只不过多加了一层循环。

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int m = matrix.size() , n = matrix[0].size();

        for(int i = 0 ; i < m ; ++i)
        {
            int left = 0 , right = n - 1;
            while(left < right)
            {
                int mid = (left + right) >> 1;
                if(matrix[i][mid] < target)
                    left = mid + 1;
                else
                    right = mid;
            }
            if(matrix[i][right] == target)
                return true;
        }

        return false;
    }
};

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

        进行两次二分,寻找左区间的右端点 以及 寻找右区间的左端点。

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        if(nums.size() == 0)
            return {- 1 , -1};

        int n = nums.size();
        int left = 0 , right = n - 1;
        int begin = -1 , end = -1;
        
        while(left < right)
        {
            int mid = (left + right) >> 1;
            if(nums[mid] < target)
                left = mid + 1;
            else
                right = mid;
        }
        if(nums[left] != target)
            return {begin , end};

        begin = left , left = 0 , right = n - 1;

        while(left < right)
        {
            int mid = (left + right + 1) >> 1;
            if(nums[mid] > target)
                right = mid - 1;
            else
                left = mid;
        }
        end = right;
        
        return {begin , end};
    }
};

📁 33. 搜索旋转排序数组

    二分的条件就是: 有序或部分有序

    1. 进行二分后,一定会后一个有序数组,有一个或有序或无序的数组,例如:

        a.  4  5  6  [7]  0  1  2

        b.  6  7  0  [1]  2  4  5

    2. 根据有序数组,缩小搜索范围

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int n = nums.size();
        int left = 0 , right = n - 1;

        while(left <= right)
        {
            int mid = (left + right) >> 1;
            if(nums[mid] == target)
                return mid;

            if(nums[left] <= nums[mid])
            {
                if(target >= nums[left] && target <= nums[mid])
                    right = mid - 1;
                else    
                    left = mid + 1;
            }
            else
            {
                if(target >= nums[mid + 1] && target <= nums[right])
                    left = mid + 1;
                else    
                    right = mid - 1;
            }
        }
        return -1;
    }
};

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

将 mid 和 数组最后一个元素n 比较:

1. mid > n , 说明数组一定被划分成为两段,其中第一段的元素均大于第二段的元素

2. mid <= n , 说明x要么是最小值,要么最小值在右边(nums本身是递增数组, 只有一段)  

int findMin(vector<int>& nums) {
        int left = 0 , right = nums.size() - 1;
        int x = nums[nums.size() - 1];

        while(left < right)
        {
            int mid = (left + right) >> 1;
            if(nums[mid] > x)
                left = mid + 1;
            else 
                right = mid;
        }

        return nums[left];
    }

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

规则: 使用一条分割线把两个数组分割成两个部分

1. 左边和右边的元素个数相等,或左边元素的个数比右边个数的多1个

2. 左边元素之和 <= 右边元素之和 (若不满足交叉小于等于的关系 就需要调整分割线的位置)

    中位数一定就与分割切两侧的元素相关 确定这个红线使用二分查找

    交叉小于等于:

      arr1[分割线左侧元素] <= arr2[分割线右侧元素] && arr2[分割线左侧元素] <= arr1[分割线右侧元素]

    因为需要通过访问分割线左右两边的元素,因此应该在较短的数组上确定分割线的位置

    i: 第一个数组分割线右边的第一个元素的下标 i = 第一个数组分割线左侧元素的个数

    j: 第二个数组分割线右边的第一个元素的下标 j = 第二个数组 分割线左侧元素的个数

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        //需要访问 "中间分割线" 左右两边的元素,因此需要在较短的数组上确定 "中间分割线"的位置,避免越界
        if(nums1.size() > nums2.size()) 
        {
            vector<int> tmp = nums1;
            nums1 = nums2;
            nums2 = tmp;
        }

        //通过元素个数较少的的数组确定分割线数量,避免数组越界
        int m = nums1.size() , n = nums2.size();
        int totalLeft = (m + n + 1) / 2;    //表示合并后数组中​​左半部分的元素数量​​
        int left = 0 , right = m;

        while(left < right)
        {
            int i = left + (right - left + 1) / 2;
            int j = totalLeft - i;
            if(nums1[i - 1] > nums2[j])
                right = i - 1;
            else
                left = i; 
        }
        
        int i = left , j = totalLeft - i;

        //处理边界情况
        int num1LeftMax = i == 0 ? INT_MIN : nums1[i - 1];
        int num1RightMin = i == m ? INT_MAX : nums1[i];
         
        int num2LeftMax = j == 0 ? INT_MIN : nums2[j-1];
        int num2RightMin = j == n ? INT_MAX : nums2[j];

        if((m + n ) % 2 == 1)
            return max(num1LeftMax , num2LeftMax);
        else
            return (double)((max(num1LeftMax , num2LeftMax) + min(num1RightMin , num2RightMin))) / 2;
    }   
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋刀鱼的滋味@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值