旋转数组求最小值和找指定位置的数

本文介绍了一种特殊的有序数组——旋转数组,并详细阐述了在不含重复值和含重复值的情况下,如何高效地通过二分查找法找到目标值。对于含有重复值的情况,文章提供了改进的查找策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

旋转数组的概念就是对有序数组循环向右移动K位得到的数组。 
例如[1,2,3,4,5]经过右移2位后旋转后得到:[4,5,1,2,3]

一、如何得到旋转数组

比如说现在要求旋转右移K位,求移动后的结果。

第一步要对K进行处理,因为假设原数组是[1,2,3]: 
当K=1,旋转数组为[3,1,2] 
当K=2,旋转数组为[2,3,1] 
当K=3,旋转数组为[1,2,3] 
当K=4,旋转数组为[3,1,2] 
… 
也就是说,当得到K时,先用K对n取模,n是数组大小。 
然后就是旋转操作,这里其实不需要借助辅助空间,伪代码如下: 
(1)逆置数组的前n-K个元素。 
(2)逆置数组的后K个元素。 
(3)逆置整个数组。 
举个例子数组[1,2,3,4,5] K=2 结果是[4,5,1,2,3]。

按照上面的步骤得到中间结果分别是: 
(1)[3,2,1,4,5] 
(2)[3,2,1,5,4] 
(3)[4,5,1,2,3] 
得到所求。


二、不含重复值的情况

由于原数组是有序的,经过右移得到2个升序的子序列,两个子序列的分界点刚好就是原序列中的最大和最小值,因此可以利用二分查找的某些性质。

class Solution {
public:
    int findMin(vector<int>& nums) {
        int low = 0;
        int high = nums.size()-1;
        int mid;
        while(low < high)
        {
            mid = (low + high)/2;
            if(nums[mid]>nums[high])
            {
                low=mid+1;
            }
            else if(nums[mid] < nums[high])
            {
                high=mid;
            }
        }
        return nums[low];
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

三、含重复值的情况

如果原序列中含有重复的元素,譬如[1,2,2,3,4],那么旋转之后依然含有重复元素,STL排序再去重的操作在这个地方没有必要,依然是利用二分查找的思想,跟上面类似,不过,当比较元素相等时,挪动一下索引,再进入下次计算。

class Solution {
public:
    int findMin(vector<int>& nums) {
        int low=0;
        int high=nums.size()-1;
        int mid;
        while(low < high)
        {
            mid = (low+high)/2;
            if(nums[mid]>nums[high])
            {
                low=mid+1;
            }
            else if(nums[mid]<nums[high])
            {
                high=mid;
            }
            else
            {
                --high;
            }
        }
        return nums[low];
    }

};=======

题目链接:search-in-rotated-sorted-array-ii

[java]  view plain  copy
  1. /** 
  2.  *  
  3.  * Follow up for "Search in Rotated Sorted Array": 
  4.  * What if duplicates are allowed? 
  5.  *  
  6.  * Would this affect the run-time complexity? How and why? 
  7.  *  
  8.  * Write a function to determine if a given target is in the array. 
  9.  * 
  10.  */  
  11. public class SearchInRotatedSortedArrayII {  
  12.   
  13.       
  14. //  271 / 271 test cases passed.  
  15. //  Status: Accepted  
  16. //  Runtime: 259 ms  
  17. //  Submitted: 1 minute ago  
  18.   
  19.   
  20.     public boolean search(int[] A, int target) {  
  21.           
  22.         if(A.length == 0return false;  
  23.           
  24.         int low = 0int high = A.length;  
  25.           
  26.         while(low != high) {  
  27.             int mid = low + (high - low) / 2;  
  28.               
  29.             if(A[mid] == target) return true;  
  30.               
  31.             if(A[low] < A[mid]) {  
  32.                 if(A[low] <= target && target < A[mid]) high = mid;  
  33.                 else low = mid + 1;  
  34.             } else if(A[low] > A[mid]){  
  35.                 if(target <= A[high - 1] && A[mid] < target) low = mid + 1;  
  36.                 else high = mid;  
  37.             } else {  
  38.                 low ++;  
  39.             }  
  40.         }  
  41.         return false;  
  42.     }  
  43.   
  44.     public static void main(String[] args) {  
  45.         // TODO Auto-generated method stub  
  46.   
  47.     }  
  48.   
  49. }  

题目链接:search-in-rotated-sorted-array

[java]  view plain  copy
  1. /** 
  2.  *  
  3.  * Suppose a sorted array is rotated at some pivot unknown to you beforehand. 
  4.  
  5.  * (i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2). 
  6.  
  7.  * You are given a target value to search. If found in the array return its index, otherwise return -1. 
  8.  
  9.  * You may assume no duplicate exists in the array. 
  10.  * 
  11.  */  
  12. public class SearchInRotatedSortedArray {  
  13.   
  14.       
  15. //  194 / 194 test cases passed.  
  16. //  Status: Accepted  
  17. //  Runtime: 212 ms  
  18. //  Submitted: 0 minutes ago  
  19.   
  20.     public int search(int[] A, int target) {  
  21.           
  22.         if(A.length == 0return -1;  
  23.           
  24.         int low = 0int high = A.length;  
  25.           
  26.         while(low != high) {  
  27.             int mid = low + (high - low) / 2;  
  28.               
  29.             if(A[mid] == target) return mid;  
  30.               
  31.             if(A[low] <= A[mid]) {  
  32.                 if(A[low] <= target && target < A[mid]) high = mid;  
  33.                 else low = mid + 1;  
  34.             } else {  
  35.                 if(target <= A[high - 1] && A[mid] < target) low = mid + 1;  
  36.                 else high = mid;  
  37.             }  
  38.         }  
  39.         return -1;  
  40.     }  
  41.   
  42.     public static void main(String[] args) {  
  43.         // TODO Auto-generated method stub  
  44.   
  45.     }  
  46.   
  47. }  
<think>嗯,用户想了解数组二分法。首先,我需要确定他们对二分法的理解程度。可能他们刚开始学习算法,或者在做题时遇到了困难。得从基础讲起,别用太专业的术语。 二分法的核心思想是分而治之,对吧?每次把搜索范围减半,这样时间复杂度可以降到O(log n)。但用户可能更关心的是如何实现,以及适用场景。得举几个例子,比如有序数组,或者旋转数组最小值。 然后要强调前提条件:数组必须有序,或者至少部分有序。不过有时候题目可能看起来无序,但通过观察可以到某种规律,这时候也能用二分。比如在旋转数组中查目标值,虽然整体无序,但分成两部分后,其中一部分仍然有序。 接下来,步骤分解。确定左右边界,计算中间位置,比较中间元素目标值,调整左右边界。可能需要处理边界问题,比如left <= right还是left < right,mid是上取整还是下取整。这些细节容易出错,得提醒用户注意。 还要举一个具体的例子,比如在有序数组目标值的位置。用代码示例的话,可能需要用Python或Java,但用户没有指定语言,用中文描述逻辑更安全。不过要注意用户是否希望看到代码,但根据指令,可能不需要具体代码,而是步骤说明。 另外,常见的应用场景有哪些呢?比如查插入位置,寻峰值,旋转数组最小值,或者值的平方根近似计算。这些例子能帮助用户理解二分法的灵活性。 可能存在的误区,比如认为二分法只能用于严格有序数组,或者必须使用递归实现。其实迭代方式更高效,而且部分有序的情况也能应用。需要澄清这些点,避免用户理解偏差。 最后,总结使用二分法的关键点:确定有序性,正确调整边界,处理循环条件。可能还要提到测试用例的重要性,比如处理目标值不存在的情况,或者数组长度为1时的边界情况。 总之,结构应该从定义、原理、步骤、应用场景、注意事项几个方面展开,用简单易懂的语言,结合实例,让用户能够逐步理解并应用二分法解决问题。</think>数组二分法是一种基于分治思想的高效查算法,适用于有序或部分有序的数组。其核心原理是通过不断缩小搜索范围,将时间复杂度优化至$O(\log n)$。以下是逐步解析: --- ### 一、适用条件 1. **数组有序性**:数组本身有序(升序/降序) 2. **存在明确边界**:可通过比较中间元素快速确定目标值所在的区间 3. **单调性问题**:如寻峰值、旋转点等特殊场景(需通过条件判断构造单调性) --- ### 二、算法步骤 1. **初始化边界** 设左边界$left=0$,右边界$right=len(nums)-1$ 2. **循环缩小范围** ```plaintext while left <= right: mid = left + (right - left) // 2 # 防溢出写法 比较 nums[mid] 与目标值 target ``` 3. **调整边界** - 若`nums[mid] < target` → 目标在右半区 → `left = mid + 1` - 若`nums[mid] > target` → 目标在左半区 → `right = mid - 1` - 若`nums[mid] == target` → 到目标 → 返回索引 4. **终止条件** 当`left > right`时,未到目标值,返回特定标识(如-1) --- ### 三、典型应用场景 1. **基础查** 在有序数组中查目标值,例如: $$nums = [1,3,5,7,9],\ target=5 \Rightarrow 返回索引2$$ 2. **寻边界** 如查第一个等于目标值的位置(左边界)或最后一个等于目标值的位置(右边界) 3. **旋转数组问题** 例如在$[4,5,6,7,0,1,2]$中寻最小值,通过比较`nums[mid]`与`nums[right]`确定有序区间 4. **值计算** 可用于计算平方根(如$\sqrt{x}$时,将搜索范围设为$[0, x]$) --- ### 四、关键注意事项 1. **区间开闭选择** 根据需选择`left <= right`(闭区间)或`left < right`(左闭右开) 2. **避免死循环** `mid`计算需与边界调整方式匹配,例如: ```plaintext mid = left + (right - left) // 2 # 偏左中位 mid = left + (right - left + 1) // 2 # 偏右中位 ``` 3. **重复元素处理** 当存在重复值时,需通过额外条件判断确定具体边界 --- ### 五、代码模板示例(Python) ```python def binary_search(nums, target): left, right = 0, len(nums)-1 while left <= right: mid = left + (right - left) // 2 if nums[mid] == target: return mid elif nums[mid] < target: left = mid + 1 else: right = mid - 1 return -1 ``` --- 通过掌握二分法的核心逻辑边界处理技巧,可高效解决大量与有序数组相关的算法问题。实际应用中需结合具体场景调整比较条件边界更新策略。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值