力扣【二分查找】:33. 搜索旋转排序数组

  1. 题目描述
    整数数组 nums 按升序排列,数组中的值 互不相同 。

在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 向左旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 下标 3 上向左旋转后可能变为 [4,5,6,7,0,1,2] 。

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

  1. 算法思路

题目要求查找指定数值且要求时间复杂度为logn,且数组升序排列不存在重复值经过旋转后得到,考虑使用二分查找

首先,由于数组经过旋转后被划分为两个分别有序的子数组,因此数组中必然存在一个idx使得 nums[idx] > nums[idx + 1],其中 0 < idx < size - 1

因此可以先遍历找到这个idx然后分别对两个子数组使用二分查找,但这样的时间复杂度在特殊情况下会达到O(n),因此不能用这种方法

相较于标准的二分查找问题,这里数组经过旋转后可能被分为两个分别升序排列的子数组,

在标准的二分查找中通过比较target和nums[mid]就可以不断逼近target,直到最后找到target或者发现数组中不存在target

观察数组发现,如果按照标准二分查找,如果mid落在left一侧,当target <= nums[mid]时target可能大于等于nums[left],也可能小于等于nums[right]

这说明我们无法简单地在这种情况下让right = mid,而是需要细化区分target所在的范围,如果target <= nums[right] 则就需要让left = mid + 1才能逼近target,

而当target > nums[mid]时,很显然可以让left = mid + 1,因为这样的区间只可能位于mid右侧,

同样如果mid落在right一侧,当target > nums[mid]时存在两种情况,要么target位于mid和right之间,这时可以让left = mid + 1,

要么target大于等于nums[left],此时right = mid才能逼近target

分析后发现,该题需要先判断mid位于left一侧或是right一侧,然后再根据target落入的区间决定left和right的变化,从而逼近target可能存在的位置

  1. 代码
    时间复杂度:O(log n)
int search(vector<int>& nums, int target) {
        int left = 0, right = nums.size() - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;

            if (nums[mid] > nums[right]) {
                if (nums[left] <= target && target <= nums[mid]) {
                    right = mid;
                } else {
                    left = mid + 1;
                }
            } else {
                if (nums[mid] < target && target <= nums[right]) {
                    left = mid + 1;
                } else {
                    right = mid;
                }
            }
        }
        if (nums[left] == target) {
            return left;
        }
        return -1;
    }
posted @ 2026-08-13 14:38  lf123z  阅读(1)  评论(0)    收藏  举报