一道微软面试题:“半”有序数组如何进行二分查找?
Posted Python那些事
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一道微软面试题:“半”有序数组如何进行二分查找?相关的知识,希望对你有一定的参考价值。
最近是毕业季,有很多同学忙于找工作,参加了面试。看了一道面试题,题目如下:
问题1:对于一个先升序后降序的数组,比如数组[3, 5, 7, 8, 4, 2, 1],如何查找某个元素?
这让我想起了我在微软面试遇到的一道题目。很类似,但却有区别:
问题2:一个分段有序的数组,两段分别有序,连接后也是有序的,比如 [4, 5, 7, 8, 1, 2, 3],如何查找某个元素?
区别是什么呢?那就是问题2更像是一个“半”有序数组,是环形有序,在缩小查找范围更容易,因而解决起来更容易。今天,就探讨下这两个问题的解法。
顺序查找
首先,最简单的,就是顺序查找。这对于任何有序的、无序的数组都适用,当然,这并不是面试官想要的算法,这种算法是无法获得offer的。我们可以简单实现下:
def sequence_search(list, item):
'''
:param list: 任何列表
:param item: 要查找的元素
:return: item在list中的索引,若不在list中返回None
'''
iter = 0
while iter < len(list):
if list[iter] == item:
return iter
else:
iter = iter + 1
return None
二分查找
这两个题目的特点都是分段有序。我们清楚,二分查找是实现有序数组查找的高效算法:
def binary_search(list, item):
'''
:param list: 有序列表
:param item: 要查找的元素
:return: item在list中的索引,若不在list中返回None
'''
low = 0
high = len(list) - 1
while low <= high:
midpoint = (low + high) // 2
if list[midpoint] == item:
return midpoint
elif list[midpoint] < item:
low = midpoint + 1
elif list[midpoint] > item:
high = midpoint - 1
return None
那么,先从问题2开始,咱们可以基于二分查找算法,需要考虑一个问题,即若list[midpoint] != item, 如何缩小查找范围呢?这可以分为两种情形讨论:
情形1: 二分查找中,对于数组[4, 5, 7, 8, 9, 1, 2, 3],list[low] < list[midpoint] ,说明low至midpoint这一段是升序的,在这种情况下,若 item < list[low] 且 item > list[midpoint],那么说明要查找的元素并非在此段,此种情况下,需要在 midpoint+1到high这一段来查找;其余情况,说明 item 就在此段,那么需要在 low至midpoint-1这一段查找即可。
情形2: 二分查找中,对于数组[ 7, 8, 9, 1, 2, 3, 4, 5],list[low] > list[midpoint] ,说明midpoint至high这一段是升序的,在这种情况下,若 item < list[low] 且 item > list[midpoint],那么说明要查找的元素就在此段,此种情况下,需要在 midpoint+1到high这一段来查找;其余情况,说明 item 不在此段,那么需要在 low至midpoint-1这一段查找即可。
Python代码实现如下:
def binary_search_for_halfsequence(list, item):
'''
:param list: 两段合并有序列表
:param item: 要查找的元素
:return: item在list中的索引,若不在list中返回None
'''
low = 0
high = len(list) - 1
while low <= high:
midpoint = (low + high) // 2
if list[midpoint] == item:
return midpoint
elif (list[low] <= list[midpoint] and (item < list[low] or item > list[midpoint])) or (
list[low] >= list[midpoint] and (item < list[low] or item > list[midpoint])):
low = midpoint + 1
else:
high = midpoint - 1
return None
而对于问题1,最关键的是什么呢?
因为是先升序后降序,与问题2很不同,问题2两段可以构成一个有序数组,而问题1则无法实现。因为是两段有序的,所以最关键的一点,就是找到最大值的位置,这样既可获得有序数组,从而用二分查找完成。
那么,如何寻得这样一个数组的最大值呢?依旧是二分查找。这与问题2有点不同,问题2只需要考虑如何缩小查找范围即可,而问题1则还需要考虑,什么情形下是满足条件的最大值。
让我们分析看看。最大值的特点是什么?很明显,最大值若为index,则满足:
list[index] > list[index-1] 且 list[index] < list[index+1]
所以,就可以用二分查找来搜索最大值,考虑两种情形来缩小查找范围:
情形1:若list[midpoint-1] < list[midpoint] 并且 list[midpoint] < list[midpoint+1],说明midpoint此处是升序的,要找到最大值,只能在midpoint+1至high这一段来查找;
情形2:若list[midpoint-1] > list[midpoint] 并且 list[midpoint] > list[midpoint+1],说明midpoint此处是降序的,要找到最大值,只能在low至midpoint-1这一段来查找。
Python代码实现如下:
def max_search(list):
'''
:param list: 先升序后降序列表
:param item: 要查找的元素
:return: item在list中的索引,若不在list中返回None
'''
if len(list) == 1:
return 0
elif len(list) == 2:
return 0 if list[0] > list[1] else 1
else:
low = 0
high = len(list) - 1
while low <= high:
midpoint = (low + high) // 2
if list[midpoint - 1] < list[midpoint] and list[midpoint] < list[midpoint + 1]:
low = midpoint + 1
elif list[midpoint - 1] > list[midpoint] and list[midpoint] > list[midpoint + 1]:
high = midpoint - 1
else:
return midpoint
寻得最大值之后,在搜寻相关的数值就可以使用二分查找了。具体算法就留给大家来完成吧,大家有好的算法也可以在留言区分享哦。
小结
通过这两道题,我们可以分析在用二分查找时,最关键的两点:
寻找满足条件的特点,比如相等或者是最大值;
寻找缩小查找范围的条件,即要是下一步在low至midpoint-1范围内查找需要满足什么条件。
(完)
看完本文有收获?请转发分享给更多人
关注「Python那些事」,做全栈开发工程师
以上是关于一道微软面试题:“半”有序数组如何进行二分查找?的主要内容,如果未能解决你的问题,请参考以下文章