当前位置: 首页 > article >正文

搜索旋转排序数组(二分查找)

测试链接:https://leetcode.cn/problems/search-in-rotated-sorted-array/https://leetcode.cn/problems/search-in-rotated-sorted-array/https://leetcode.cn/problems/search-in-rotated-sorted-array/

问题描述

假设我们有一个旋转排序的数组,这个数组本身是一个升序数组,但是由于某种原因,它被旋转了一定的次数。我们要在这个数组中查找一个目标值。如果找到了目标值,返回其索引;如果没有找到,返回 -1。

旋转排序数组

比如一个升序排列的数组 [1, 2, 3, 4, 5, 6, 7] 被旋转过一次,得到 [4, 5, 6, 7, 1, 2, 3]。显然,原数组被旋转了 3 次。

这个旋转数组的特点是数组的前部分是递增的,后部分也是递增的,且存在一个旋转点使得数组的递增顺序断裂。通过这个特性,我们可以修改标准的二分查找算法来适应旋转排序数组的查找。

二分查找的修改版

思路

我们依然使用二分查找的基本思想:通过比较中间元素与目标值,决定继续查找的区间。不同的是,在旋转数组中,数组的两端(左端和右端)可能并非严格按顺序排列,这就要求我们在判断目标值和区间时多加小心。

  1. 首先判断中间元素与目标值:如果找到目标元素,直接返回索引。
  2. 处理重复元素的情况:如果左右元素相同,即 nums[l] == nums[m] == nums[r],则无法判断当前区间是有序还是无序的,因此只能通过将 l++r-- 来缩小查找区间。
  3. 判断区间的有序性:在每一次循环中,检查当前区间是有序的还是旋转的:
    • 如果左半部分是有序的,即 nums[l] <= nums[m],我们可以判断目标是否在左半部分。如果目标值在左半部分,则调整右边界;否则,调整左边界。
    • 如果右半部分是有序的,即 nums[m] <= nums[r],同样地,我们可以判断目标是否在右半部分。

实现

下面是针对旋转排序数组的二分查找实现:

class Solution {
    public int search(int[] nums, int target) {
        int l = 0;
        int r = nums.length - 1;
        while (l <= r) {
            int m = l + (r - l) / 2;  // 防止溢出
            if (nums[m] == target) {
                return m;  // 找到目标,返回索引
            }
            
            // 如果左右两端元素相同,无法确定哪一部分有序,直接移动边界
            if (nums[l] == nums[m] && nums[m] == nums[r]) {
                l++;
                r--;
            } 
            // 如果左半部分是有序的
            else if (nums[l] <= nums[m]) {
                // 判断目标值是否在左半部分
                if (nums[l] <= target && target < nums[m]) {
                    r = m - 1;
                } else {
                    l = m + 1;
                }
            } 
            // 如果右半部分是有序的
            else {
                // 判断目标值是否在右半部分
                if (nums[m] < target && target <= nums[r]) {
                    l = m + 1;
                } else {
                    r = m - 1;
                }
            }
        }
        return -1;  // 如果没有找到目标,返回-1
    }
}

关键点解析

  1. nums[l] == nums[m] == nums[r]:这是一个特殊情况,表示左端、右端和中间元素相等。在这种情况下,我们无法确定左半部分或右半部分是否有序,因此只能通过缩小边界来逐步排除元素。
  2. 区间有序性判断:每次选择中间元素后,通过判断左半部分或右半部分是否有序,来决定目标值的查找区间。
  3. 时间复杂度:在最坏情况下,当左右边界元素相同并且无法判断有序性时,我们会减少一个元素。因此,最坏情况下时间复杂度为 O(n)O(n)O(n),但通常情况的时间复杂度是 O(log⁡n)O(\log n)O(logn)。

大白话就是:先判断num[m]是否等于target,等于则直接返回索引m,如果不等,则继续判断。如果左右边界相等则无法判断哪一部分有序(若数组元素有序且无重复则无需做此判断,详见搜索旋转排序数组2),此时l++,r--移动左右边界;然后判断左右部分哪一部分有序,如果有序,则判断目标值是否在那一部分。

 

原文地址:https://blog.csdn.net/2302_81116822/article/details/145414148
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.kler.cn/a/529041.html

相关文章:

  • cf集合***
  • NTU:多模态大模型的知识获取能力评估
  • Python 梯度下降法(七):Summary
  • 第一个Python程序
  • 深入了解 SSRF 漏洞:原理、条件、危害
  • 2021 年 12 月大学英语四级考试真题(第 1 套)——纯享题目版
  • 使用frp访问内网web、ssh和隧道搭建
  • 本地部署 DeepSeek-R1:简单易上手,AI 随时可用!
  • 85.[1] 攻防世界 WEB easyphp
  • Java小白入门教程:ArrayList
  • (超实用教程)利用MSF制作exe程序木马
  • javaSE.Object类
  • Web_php_unserialize
  • 代码随想录-训练营-day16
  • MongoDB 删除文档
  • DeepSeek回答禅宗三重境界重构交易认知
  • 项目集成Spring Security认证部分
  • 深入解析 C++ 字符串处理:提取和分割的多种方法
  • 14JavaWeb——SpringBoot原理
  • JWT入门