有一道leetcode题,简单点说,就是一个有序序列,随机截成两段,然后这两段交换位置,让你在O(logn)的时间里找一个search一个值,本质上是二分查找的变异
Suppose a sorted array is rotated at some pivot unknown to you beforehand.
(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).
You are given a target value to search. If found in the array return its index, otherwise return -1.
You may assume no duplicate exists in the array.
我的解题过程:
- 先想,理论上想通了,直接开始干,然后写了半天,发现不对劲
- 分析不对劲的原因,基础不牢,地动山摇,所以先写了一份简单的二分查找
- 然后回到原来的代码,搞定,通过测试
- 查看网络上大牛写的,优化了一份代码
- 结果,生成了三份代码
理论
本质上考察的分类讨论的思想,随机截断,交换位置,会出现三种情况,三种情况依次处理,即可得到正确答案
- 情况一:没有改变顺序,也就是原始序列
- 情况二:左边多于右边
- 情况三:左边少于右边
- 特殊情况:两边相等,由于我取mid的方法,是向下取整,所以,第一次取mid,会落在左边,也就是和情况二相同
为何这么分?对于情况二,第一次取mid的值为5,属于后半截(正常排序的后半截),而对于情况三,第一次取mid的值为1,属于前半截(正常排序的前半截),会导致采取的策略不一样
最终答案
这是最后优化的版本,参考这篇博文写的
完整代码可以参考这里:
https://gitee.com/invisibleDes/algorithm/blob/master/rotated_search.py
class BinarySearch():
def search(self, array, target):
n = len(array)
if n <= 0:
return "data error"
return self.rotated_binary_search(array, 0 , n-1, target)
def rotated_binary_search(self, a, left , right, target):
mid = math.floor((left+right)/2)
if a[left] == target:
return left
if a[mid] == target:
return mid
if a[right] == target:
return right
if a[left] < a[right]:
if target < a[left] or target > a[right]:
return "not found"
elif target < a[mid]:
return self.rotated_binary_search(a, left+1, mid-1, target)
else:
return self.rotated_binary_search(a, mid+1, right-1, target)
elif a[mid] > a[left]:
if target < a[mid] and target > a[left]:
return self.rotated_binary_search(a, left+1, mid-1, target)
else:
return self.rotated_binary_search(a, mid+1, right-1, target)
else:
if target > a[mid] and target < a[right]:
return self.rotated_binary_search(a, mid+1, right-1, target)
else:
return self.rotated_binary_search(a, left+1, mid-1, target)
def test_c_binary_search():
bs = BinarySearch()
print(bs.search([4, 5, 6, 7, 0, 1, 2], 1))
if __name__ == "__main__":
test_c_binary_search()