'''
154_寻找旋转排序数组中的最小值 II
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
请找出其中最小的元素。
注意数组中可能存在重复的元素。
示例 1:
输入: [1,3,5]
输出: 1
示例 2:
输入: [2,2,2,0,1]
输出: 0
说明:
这道题是 寻找旋转排序数组中的最小值的延伸题目。
允许重复会影响算法的时间复杂度吗?会如何影响,为什么?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
'''
class Solution:
'''
算法1:真正的二分查找,特殊情况是nums[L] == nums[m] == nums[R],此时无法判断哪一侧是纯递增的。
递归处理特殊情况,返回最小值,其他情况照搬无重复值循环递增数组的做法,消除了尾递归
执行用时 :128 ms, 在所有 python3 提交中击败了5.94%的用户
内存消耗 :29.7 MB, 在所有 python3 提交中击败了5.01%的用户
'''
def findMin(self, nums: list) -> int:
def ans(L, R):
if L + 1 == R: #单独判断只有2个元素的情形,以避免死循环
return min(nums[L], nums[R])
while L < R:
m = (L + R) // 2
if nums[L] == nums[m] == nums[R]:
return min(ans(L, m-1), ans(m, R))
elif nums[m] <= nums[R]:
R = m
else:
L = m + 1
return nums[R]
return ans(0, len(nums)-1)
'''
算法2:算法1的另一种写法,没有消除尾递归
执行用时 :148 ms, 在所有 python3 提交中击败了5.94%的用户
内存消耗 :29.7 MB, 在所有 python3 提交中击败了5.01%的用户
'''
def findMin2(self, nums: list) -> int:
def ans(L, R):
m = (L + R) // 2
if L == R:
return nums[L]
elif L + 1 == R:
return min(nums[L], nums[R])
elif nums[L] == nums[m] == nums[R]:
return min(ans(L, m-1), ans(m, R))
elif nums[m] <= nums[R]:
return ans(L, m)
else:
return ans(m+1, R)
return ans(0, len(nums)-1)
'''
算法3:当nums[L] == nums[m] == nums[R]时,采用顺序查找方法,逐步缩小左边界(或者右边界),以便下一轮循环时,不再出现此种情况。
其他情况则照搬无重复值循环递增数组的做法,实行对分查找。效率不如算法1高,但是代码更简洁。
执行用时 :136 ms, 在所有 python3 提交中击败了5.94%的用户
内存消耗 :29.7 MB, 在所有 python3 提交中击败了5.01%的用户
'''
def findMin3(self, nums: list) -> int:
L, R = 0, len(nums)-1
while L < R:
m = (L + R) // 2
if nums[L] == nums[m] == nums[R]:
L += 1 #或R -= 1
elif nums[m] <= nums[R]:
R = m
else:
L = m + 1
return nums[R]
'''
算法4:对算法3的条件语句做了一个顺序上的调整,也许效率更高
执行用时 :132 ms, 在所有 python3 提交中击败了5.94%的用户
内存消耗 :29.2 MB, 在所有 python3 提交中击败了5.01%的用户
'''
def findMin4(self, nums: list) -> int:
L, R = 0, len(nums)-1
while L < R:
m = (L + R) // 2
if nums[m] > nums[R]:
L = m + 1
elif nums[m] < nums[R]:
R = m
else:
R -= 1
return nums[R]
'''
算法5:算法3的一个变式,确保循环体内至少有3个元素,这样当nums[L] == nums[m] == nums[R]时,左右边界均可向中间移动1位,
最后剩下2个元素,输出最小值
执行用时 :132 ms, 在所有 python3 提交中击败了5.94%的用户
内存消耗 :29.2 MB, 在所有 python3 提交中击败了5.01%的用户
'''
def findMin5(self, nums: list) -> int:
L, R = 0, len(nums)-1
while L + 1 < R:
m = (L + R) // 2
if nums[L] == nums[m] == nums[R]:
L, R = L + 1, R - 1
elif nums[m] <= nums[R]:
R = m
else:
L = m + 1
return min(nums[L], nums[R])
'''
算法6:对算法5的条件语句做了一个顺序上的调整,也许效率更高
执行用时 :132 ms, 在所有 python3 提交中击败了5.94%的用户
内存消耗 :29.2 MB, 在所有 python3 提交中击败了5.01%的用户
'''
def findMin6(self, nums: list) -> int:
L, R = 0, len(nums)-1
while L + 1 < R:
m = (L + R) // 2
if nums[m] > nums[R]:
L = m + 1
elif nums[L] == nums[m] == nums[R]:
L, R = L + 1, R - 1
else:
R = m
return min(nums[L], nums[R])
x = Solution()
a = [3,4,5,1,2]
print(x.findMin(a), x.findMin2(a), x.findMin3(a), x.findMin4(a), x.findMin5(a), x.findMin6(a))
a = [1,3,3]
print(x.findMin(a), x.findMin2(a), x.findMin3(a), x.findMin4(a), x.findMin5(a), x.findMin6(a))