题目
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增序列的数组的一个旋转,输出旋转数组的最小元素。
例:
数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,该数组的最小值为1。
思路
- 直接遍历查找最小值
- 时间复杂度:O(n)
- 空间复杂度:O(1)
- 直接遍历没有利用数组已排序的信息,为了更快查找,我们可以利用二分法查找。
可以观察到,数组旋转后成为2个递增区间,且存在第一个区间最小值>=第二个区间最大值的特点。因此我们对中间下标的数字与右端点的数字比较,即可知道中间下标数字处于第一区间还是第二区间,再更新对应端点,直到左=右端点。- 左端点:标记第一区间
- 右端点:标记第二区间
- 中间点:< =右端点,在第二区间,否则,在第一区间。但是当出现 左=右=中 的情况时,就无法判断了,此时只能让右端点向左移动,退化为遍历查找,直到出现其他情况。
- 时间复杂度:最差全等数组,O(n)
- 空间复杂度:O(1)
代码
def min_number_in_rotated_array(r_nums):
"""
:param r_nums:rotated arrat
:return: min number
"""
if not r_nums:
return None
left = 0
right = len(r_nums)-1
while left < right:
mid = (left + right) // 2
if r_nums[mid] == r_nums[right] == r_nums[left]:
right -= 1
elif r_nums[mid] <= r_nums[right]:
right = mid
else:
left = mid + 1
return r_nums[left]
思考
…本题我的实现和书上细节上不太一样。但是我自己测试是没什么问题,希望能够这本书能出写测试库,这样也方便我们学习。
- 对于出现左=中=右,书上在一开始检验,然后直接调用顺序查找。而我只是单步顺序查找,一但出现其他情况,仍然可以采用二分。对于[1,1,1,1,1,1,0,1,1,1]这样的案例,书上O(n),而我在右=中=0.index之后,又回到二分法,显然略快于顺序。
- 书上判断区间缩小,用的是mid >= left,left = mid;mid <= right, right = mid。而我用的是 mid<=right, right = mid;其余left = mid+1,比书收敛的略快一点。
- 总之,目前看来我的代码是更加简短一点,性能也略好一点。不知道有没有谬误。
leetcode 33: 搜索旋转数组
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。
示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
示例 2:
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1
代码
class Solution(object):
def search(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
if not nums:
return -1
left = 0
right = len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:return mid
elif nums[mid] < target:
if nums[mid] <= nums[right] < nums[left] <= target:right = mid - 1
else:left = mid + 1
else:
if target <= nums[right] < nums[left] <= nums[mid]:left = mid + 1
else:right = mid - 1
return -1