旋转数组

算法原理

旋转数组的算法原理可以参照 二分查找(有序数组),但是拥有比 二分查找(有序数组)更复杂的 IsBlue。下面分别从最小值和目标值介绍 如何判断旋转数组的 IsBlue

graph LR
A{判断中位点<br>与旋转点的位置}-->H[不包含重复值]
H-->B[最小值递增序列<br>比较右边界]
B-->C{是否大于<br>右边界值}
C--大于右边界值-->D[中间点在旋转点左侧]
C--小于右边界值-->E[中间点在旋转点右侧]
C--等于右边界点<br>中间点就是右边界点-->E
H-->G[最大值递增序列<br>比较左边界]
A-->I[包含重复值]
I-->N[最小值递增序列<br>比较右边界]
N-->J{是否大于<br>右边界值}
J--大于右边界值-->K[中间点在旋转点左侧]
J--小于右边界值-->L[中间点在旋转点右侧]
J--等于右边界点-->M[不能判断中间点与旋转点的位置<br>缩短区间right=right-1]
I-->O[最大值递增序列<br>比较左边界]

P{中位点与<br>目标值的位置关系}-->Q[中位点左侧有序]
Q--"left <= target < mid"-->R[right=mid]
Q--"target = mid"-->T[left=mid]
Q--else-->T
P-->U[中位点右侧有序]
U--"mid <= target < right"-->V[left=mid]
U--"target = mid"-->V
U--else-->W[right=mid]

最小值(不包含重复值)

Untitled

graph LR
A{判断中位点的位置}-->J["nums[mid] > nums[-1]"]
J-->B[中位点在最小值左侧<br>left=mid]
A-->E["nums[mid]>nums[min(right,len(nums)-1]"<br>因为right可能超出边界所以使用min进行规范]
E-->B
A-->F["nums[mid] < nums[-1]"]
F-->C[中位点在最小值右侧<br>right=mid]
A-->G["nums[mid] < nums[min(right,len(nums)-1]"<br>因为right可能超出边界所以使用min进行规范]
G-->C
A-->H["nums[mid] = nums[-1]"]
H-->D[中位点就在最右侧<br>此时数组中只有一个值<br>j返回right就可以了]
A-->I["nums[mid] = nums[min(right,len(nums)-1]"<br>因为right可能超出边界所以使用min进行规范]
I-->D

最小值(包含重复值)

Untitled

graph LR
A{判断中位点的位置}-->J["nums[mid] > nums[-1]"]
J-->B[中位点在最小值左侧<br>left=mid]
A-->E["nums[mid]>nums[min(right,len(nums)-1]"<br>因为right可能超出边界所以使用min进行规范]
E-->B
A-->F["nums[mid] < nums[-1]"]
F-->C[中位点在最小值右侧<br>right=mid]
A-->G["nums[mid] < nums[min(right,len(nums)-1]"<br>因为right可能超出边界所以使用min进行规范]
G-->C
A-->H["nums[mid] = nums[-1]"]
H-->D[此时不能判断中位点<br>在最小值的哪边<br>所以返回right=right-1<br>进一步进行判断]
A-->I["nums[mid] = nums[min(right,len(nums)-1]"<br>因为right可能超出边界所以使用min进行规范]
I-->D

寻找目标值(不包含重复值)

graph LR
M{判断中位点<br>与旋转点的位置}--左侧元素小于中位点-->N[旋转点在中位点右侧 <br> 中位点左侧是有序序列]
N-->O{目标值与<br>中位点的位置}
O-- 最左侧元素 <= 目标 < 中位数-->B[目标值在中位点左侧 <br> right=mid]
O-- 否则 -->D[目标值在中位点的右侧 <br> left=mid]
M--左侧元素大于中位点-->E[旋转点在中位点左侧 <br> 中位点右侧是有序序列]
E-->A{目标值与<br>中位点的位置}
A--中位数 <= 查找目标 <= 最右侧元素-->F[目标值在中位点的右侧 <br> left=mid]
A--否则-->G[目标值在中位点的左侧 <br> right=mid]
M--左侧元素等于中位点 <br> 这种情况说明mid=0-->N
M--右侧元素小于中位点-->N
M--右侧元素大于中位点-->E
M--右侧元素等于中位点 <br> 这种情况说明mid=-1-->E

寻找目标值(包含重复值)

graph LR
M--左侧元素等于中位点-->R[不能判断旋转点的位置]
R-->S[缩短搜索区间left=left+1]
M{判断中位点<br>与旋转点的位置}--左侧元素小于中位点-->N[旋转点在中位点右侧 <br> 中位点左侧是有序序列]
N-->O{目标值与<br>中位点的位置}
O-- 最左侧元素 <= 目标 < 中位数-->B[目标值在中位点左侧 <br> right=mid]
O-- 否则 -->D[目标值在中位点的右侧 <br> left=mid]
M--左侧元素大于中位点-->E[旋转点在中位点左侧 <br> 中位点右侧是有序序列]
E-->A{目标值与<br>中位点的位置}
A--中位数 <= 查找目标 <= 最右侧元素-->F[目标值在中位点的右侧 <br> left=mid]
A--否则-->G[目标值在中位点的左侧 <br> right=mid]
M--右侧元素小于中位点-->N
M--右侧元素大于中位点-->E
M--右侧元素等于中位点-->T[不能判断旋转点的位置]
T-->U[缩短搜索区间right=right+1]

2.寻找旋转排序数组中的最小值(153 - 中)

题目描述

整数数组 nums 按升序排列,数组中的值 互不相同,并按上述情形进行了多次旋转 。请你找出并返回数组中的 最小元素

示例 :

输入:nums = [3,4,5,1,2]
输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。

二分查找

我们考虑数组中的最后一个元素 nums[-1]:在最小值右侧的元素(不包括最后一个元素本身),它们的值一定都严格小于 nums[-1];而在最小值左侧的元素,它们的值一定都严格大于 num[-1]。因此,我们可以根据这一条性质,通过二分查找的方法找出最小值。

Untitled

横轴表示数组元素的下标,纵轴表示数组元素的值。图中标出了最小值的位置,是我们需要查找的目标。

综上:我们可以参照 二分查找基本原理 的模板,修改 IsBlue() 的条件。使得左侧>nums[-1],右侧≤nums[-1]。

代码实现:

    class Solution(object):
    def findMin(self, nums):
        # 如果没有数字则返回-1
        if len(nums)==0:
            return -1
        left = -1
        right =len(nums)
        large=len(nums)-1
        while left+1<right:
            mid=(left+right)>>1
            # nums[mid]>nums[-1]或nums[min(right,large)]说明nums[mid]是最小值右侧的元素。
			# 因为right可能超出边界所以使用min(right,len(nums)-1)进行规范
            if nums[mid]>nums[-1]: # 可以修改为if nums[mid]>nums[min(right,large)]。
                left=mid
            # nums[mid]>nums[-1],说明nums[mid]是最小值左侧的元素。
            else:    # 如果nums[mid]=nums[-1]。则说明只含有一个元素,返回right即可。
                right=mid
        return nums[min(right,large)]

3.寻找旋转排序数组中的最小值II(154 - 难)

题目描述

整数数组 nums 按升序排列,数组中的值 可能存在重复值 。返回数组中的 最小元素 。同 剑指 Offer 11. 旋转数组的最小数字

示例 :

输入:nums = [2,2,2,0,1]
输出:0