用二分查找法解决旋转数组的总结

这篇博客总结了如何使用二分查找法解决旋转排序数组的问题,包括找到旋转数组的最小数字、搜索目标值的索引以及查找元素的第一个和最后一个位置。详细解释了在处理旋转数组时二分查找的边界条件和递归策略,强调了处理重复元素和避免溢出的重要性。
摘要由CSDN通过智能技术生成

用二分算法实现Sqrt(x)函数

这道题用二分模板的第二个但是写的使用要注意mid*mid可能出现数据溢出的情况所以用mid*1ll*mid把int类型变成long long 类型

class Solution {
public:
    int mySqrt(int x) {
        int l=0,r=x;
        while(l<r)
        {
            int mid=(l+r+1ll)>>1;
            if(mid*1ll*mid>x)
                r=mid-1;
            else
                l=mid;     
        }
        return l;
    }
};

题意

旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个升序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
数组可能包含重复项。
注意:数组内所含元素非负,若数组大小为0,请返回-1。

样例

输入:nums=[2,2,2,0,1]
输出:0
注意这里有重复的元素,在进行二分查找的前需要去掉重复的元素,注意这里边界判定是与nums[0]做比较

class Solution {
public:
    int findMin(vector<int>& nums) {
        if(nums.size()<=0) return -1;
        int n=nums.size()-1;
        while(n>0&&nums[n]==nums[0]) n--;
        if(nums[n]>=nums[0]) return nums[0];
        int l=0,r=n;
        while(l<r)
        {
            int mid=(l+r)>>1;
            if(nums[mid]>=nums[0]) l=mid+1;
            else
            r=mid;
        }
        return nums[r];
    }
};

搜索旋转排序数组

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。

你可以假设数组中不存在重复的元素。

你的算法时间复杂度必须是 O(log n) 级别。
示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
二分查找,关键是找到中间值之后,判断接下来在左半段还是右半段继续查找。根据旋转数组的规律,数组经过旋转之后会被分为两段升序段,找到中间值nums[m]之后,将它和搜索区域最右边元素nums[r]比较,处理情况如下:
如果nums[m] == target,则返回m
如果nums[m] > nums[r],则中间值位于旋转数组的前一段升序段
如果target < nums[m] && target >= nums[l],则到m的左半段继续查找
否则,到m的右半段继续查找
如果nums[m] <= nums[r],则中间值位于旋转数组的后一段升序段
如果target > nums[m] && target <= nums[r],则到m的右半段继续查找
否则,到m的左半段继续查找

时间复杂度O(log n)
空间复杂度O(1)

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int len = nums.size();
        int l = 0,r = len-1;
        while(l <= r)//注意写的时候旋转数组搜索中结束while循环的条件是小于和等于
        {
            int m = (r + l)/2;
            if(nums[m] == target)
                return m;
            else if(nums[m] > nums[r])
            {
                if(target >= nums[l] && target < nums[m])
                    r = m-1;
                else
                    l = m+1;
            }
            else if(nums[m] <= nums[r])
            {
                if(target > nums[m] && target <= nums[r])
                    l = m+1;
                else
                    r = m-1;
            }
        }
        
        return -1;
    }
};

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

示例 1:

输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]

思路先找到左区间的端点,在找到右区间的端点
二分查找,由于要找到目标值的第一个位置和最后一个位置。当中间值nums[m] == target时: 如果要找第一个位置,则向m的左半段继续二分查找,–r直到nums[m-1] != target为止; 如果要找最后一个位置,则向m的右半段继续二分查找,++l直到nums[m+1] != target为止;

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        if(nums.empty()) return {-1,-1};
        int len = nums.size();
        int l = 0,r = nums.size() - 1;
        int start = -1,end = -1;
        while(l <= r)
        {
            int m = l + (r-l)/2;
            if(nums[m] < target) l=m+1;
            else if(nums[m] > target) r = m-1;
            else
            {
                if(m == 0)//分别判定边界情况
                {
                    start = 0;
                    break;
                }
                
                if(nums[m-1] != target)
                {
                    start = m;
                    break;
                }
                else r = m-1;
            }
        }
        
        if(l > r) return {-1,-1};
        
        l = 0;
        r = nums.size() - 1;
        while(l <= r)
        {
            int m = l + (r-l)/2;
            if(nums[m] < target) l = m+1;
            else if(nums[m] > target) r = m-1;
            else 
            {
                if(m == len-1)
                {
                    end = len-1;
                    break;
                }
                if(nums[m+1] != target)
                {
                    end = m;
                    break;
                }
                else l = m+1;
            }
        }
        
       return {start,end};
    }
};

二分总结

对于整数域上的二分,需要注意终止边界、左右区间取舍时的开闭情况避免漏掉答案或造成死循环;对于实数域上的二分,需要注意精度问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值