力扣第33题:搜索旋转排序数组

力扣第33题:搜索旋转排序数组

题目描述

在一个旋转排序数组中搜索一个目标值,并返回其索引。若未找到目标值,则返回 -1。旋转排序数组是指一个升序排列的数组在某个未知的旋转点被旋转。例如,nums = [4, 5, 6, 7, 0, 1, 2]。你可以假设数组中不存在重复元素。

示例

plaintext 复制代码
输入: nums = [4, 5, 6, 7, 0, 1, 2], target = 0
输出: 4

解题思路

我们可以利用二分查找的思路,因为在旋转排序数组中,一半的部分总是有序的。通过比较中间值和左右边界值的大小关系,可以判断哪一部分是有序的,再根据目标值的位置来缩小搜索区间。

具体步骤

  1. 定义左右指针 :用 leftright 指针指向数组的左右边界。
  2. 中间值的计算 :计算 mid 作为 leftright 的中间索引。
  3. 比较中间值
    • 如果 nums[mid] == target,直接返回 mid
    • 如果 nums[left] <= nums[mid](即左半部分是有序的):
      • 判断目标值是否在左半部分,如果在,更新 right = mid - 1,否则更新 left = mid + 1
    • 如果右半部分是有序的(即 nums[mid] < nums[right]):
      • 判断目标值是否在右半部分,如果在,更新 left = mid + 1,否则更新 right = mid - 1
  4. 返回结果:循环结束后如果没有找到目标值,返回 -1。

代码实现

以下是完整的代码实现:

c 复制代码
#include <stdio.h>

int search(int* nums, int numsSize, int target) {
    int left = 0, right = numsSize - 1;

    while (left <= right) {
        int mid = left + (right - left) / 2;

        if (nums[mid] == target) {
            return mid; // 找到目标值,直接返回索引
        }

        // 判断左半部分是否有序
        if (nums[left] <= nums[mid]) {
            // 如果目标值在左半部分,则继续搜索左半部分
            if (nums[left] <= target && target < nums[mid]) {
                right = mid - 1;
            } else {
                left = mid + 1; // 否则,搜索右半部分
            }
        } 
        // 否则右半部分一定是有序的
        else {
            // 如果目标值在右半部分,则继续搜索右半部分
            if (nums[mid] < target && target <= nums[right]) {
                left = mid + 1;
            } else {
                right = mid - 1; // 否则,搜索左半部分
            }
        }
    }

    return -1; // 未找到目标值
}

代码分析

c 复制代码
int search(int* nums, int numsSize, int target) {
    int left = 0, right = numsSize - 1;
  • leftright 指针用于表示当前搜索范围的左右边界。
c 复制代码
    while (left <= right) {
        int mid = left + (right - left) / 2;
  • while 循环中,当 left 小于或等于 right 时,计算中间索引 mid
c 复制代码
        if (nums[mid] == target) {
            return mid; // 找到目标值,直接返回索引
        }
  • 检查 nums[mid] 是否等于目标值 target,如果相等,则返回 mid
c 复制代码
        // 判断左半部分是否有序
        if (nums[left] <= nums[mid]) {
  • 判断左半部分是否有序。若 nums[left] <= nums[mid],说明从 leftmid 的部分是连续递增的。
c 复制代码
            if (nums[left] <= target && target < nums[mid]) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
  • 在左半部分有序的情况下,判断目标值 target 是否在 leftmid 的范围内:
    • 如果是,移动 right 指针以缩小范围至左半部分;
    • 否则,移动 left 指针至右半部分继续搜索。
c 复制代码
        } else {
            if (nums[mid] < target && target <= nums[right]) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
  • 如果左半部分无序,那么右半部分必定有序。使用类似方法判断目标值在右半部分的位置,更新 leftright 指针。
c 复制代码
    }
    return -1;
}
  • while 循环结束仍未找到目标值,则返回 -1 表示不存在目标值。

时间复杂度

由于每次迭代后搜索区间缩小一半,时间复杂度为 O(log n),这是二分查找算法的标准复杂度。

总结

此算法利用旋转数组的一部分总是有序的性质,通过二分查找和分段判断有效缩小搜索范围,最终实现高效搜索。

示例

c 复制代码
int main() {
    int nums[] = {4, 5, 6, 7, 0, 1, 2};
    int target = 0;
    int index = search(nums, 7, target);
    printf("Target %d found at index: %d\n", target, index);
    return 0;
}

输出结果:

plaintext 复制代码
Target 0 found at index: 4
相关推荐
冲帕Chompa1 小时前
图论part10 bellman_ford算法
数据结构·算法·图论
緈福的街口1 小时前
【leetcode】144. 二叉树的前序遍历
算法·leetcode
GG不是gg1 小时前
排序算法之基础排序:冒泡,选择,插入排序详解
数据结构·算法·青少年编程·排序算法
随意起个昵称2 小时前
【双指针】供暖器
算法
倒霉蛋小马2 小时前
最小二乘法拟合直线,用线性回归法、梯度下降法实现
算法·最小二乘法·直线
codists2 小时前
《算法导论(第4版)》阅读笔记:p82-p82
算法
埃菲尔铁塔_CV算法2 小时前
深度学习驱动下的目标检测技术:原理、算法与应用创新
深度学习·算法·目标检测
Dream it possible!2 小时前
LeetCode 热题 100_寻找重复数(100_287_中等_C++)(技巧)(暴力解法;哈希集合;二分查找)
c++·leetcode·哈希算法
float_com3 小时前
【背包dp-----分组背包】------(标准的分组背包【可以不装满的 最大价值】)
算法·动态规划
丶Darling.3 小时前
Day119 | 灵神 | 二叉树 | 二叉树的最近共公共祖先
数据结构·c++·算法·二叉树