嘿,我在接受采访时有这个问题,并想知道解决问题的最佳方法是什么。所以说你得到一个已经排序的数组,你想要找到某个值x的最低索引。
这是我想出的python /伪代码,我只是想知道是否有更好的方法可以解决它?
def findLowestIndex(arr, x):
index = binarySearch(0, len(arr), x)
if index != -1:
while index > 0:
if arr[index] == arr[index-1]:
index -= 1
else:
break
return index
谢谢!
答案 0 :(得分:7)
在最坏的情况下,您的方法需要线性时间,即数组中x
的计数为O( n )时。
可以通过更改二进制搜索本身来查找数组中的第一个x
而不是其中任何一个来获得O(lg n )解决方案:
def binary_search(x, a):
lo = 0
hi = len(a)
while lo < hi:
mid = (lo + hi) // 2
if a[mid] < x:
lo = mid + 1
elif a[mid] > x:
hi = mid
elif mid > 0 and a[mid-1] == x:
hi = mid
else:
return mid
return -1
答案 1 :(得分:3)
import bisect
l = [1,2,3,4,4,4,4,4,5,5,5,5,5,6,6,7,7,7,8,8]
bisect.bisect_left(l, 4)
编辑: 我只是想念一件事。 bisect会给你一个插入点。因此,如果x不在列表中,您仍将拥有结果索引。所以你需要先检查x是否在列表中:
if x in l:
....
但是对于面试问题,他们可能想看看你如何提出算法而不是使用图书馆......
答案 2 :(得分:1)
如果元素是整数 - 或枚举,您可以更快地完成:
请注意,在二进制搜索[算法,而不是python函数]中,如果元素不存在 - 您可以找到比索引大的最小元素。
x
- 并获取索引,将其设为i
。x-1
。如果它不在列表中,则进行二进制搜索
可以找到x
的第一个索引。j
:
j
到i
的子列表中进行二元搜索,然后搜索list[k] < list[k+1]
对于非枚举值,也可以通过在list[k] < list[k+1] and list[k+1] == x
时减少范围的相同想法来完成,但我发现首先要了解如何对整数进行更简单的操作,然后将其应用于一般解决方案。
请注意此解决方案为O(logn)
,而您建议的简单解决方案为O(n)
,列表中包含大量欺骗,因为二进制搜索后的迭代步骤。
答案 3 :(得分:1)
递归版本,如果有人感兴趣的话...
从https://github.com/reneargento/algorithms-sedgewick-wayne/blob/master/src/chapter1/section4/Exercise10.java进行重写,这是算法4 的练习。
def binary_search(key, a, low, high):
if low > high:
return -1;
middle = low + (high - low) / 2;
if a[middle] < key:
return binary_search(key, a, middle + 1, high)
elif a[middle] > key:
return binary_search(key, a, low, middle - 1)
else:
if (middle - 1 >= 0) and (a[middle - 1] != key):
return middle
else:
index = binary_search(key, a, 0, middle - 1)
if(index == -1):
return middle;
else:
return index;
a = [1,2,3,3,3,3,4,5,6,7,7,8,9]
print(binary_search(7, a, 0, len(a)))
递归版本不是总是比非递归版本更直接吗?为什么这看起来很难..?谁能写出更好的递归版本:D?
答案 4 :(得分:0)
如果x
不在X
f(x) = v
,那么答案是微不足道的:二进制搜索以找出它。
如果有一个x
使得f(x) = v
,那么答案也是微不足道的:二进制搜索以找出它。
如果x
有多个f(x) = v
,问题就很有趣了。如果存在恒定数量的x
,那么在算法上二分搜索是最佳的。只需二元搜索并按顺序检查较低的索引。
但是,如果有很多这些x
会怎么样?像这样的顺序搜索显然不是最佳的。事实上,如果有c * |X|
x
,则会在O(|X|)
中运行。
相反可以做的是将lbound
初始化为0
并进行二元搜索,直到找到i
处的元素,每当您向右走时,更新lbound
到刚刚使用的中期。然后从[lbound, i - 1]
进行二分搜索。这样做直到i == lbound
或您没有找到元素。如果前者发生,则所需索引为0
。如果发生后者,则所需的索引是先前使用的i
。最糟糕的情况是所需的索引是0
。
有趣的是,我认为这仍然在log(|X|)
时间运行。
答案 5 :(得分:0)
deferred detection of equality approach in binary search给出最小的索引,减少相等分支。
def binary_search(low, high, target, array):
while low < high:
mid = low + (high - low) / 2
if a[mid] < target:
low = mid + 1
else:
high = mid
if (array[low] == target) return low
else return -1
答案 6 :(得分:-1)
我敢打赌,g.d.d.c的评论是python的最快答案。否则,您的通用算法是正确的,除了在某些情况下您可以击败二进制搜索的O(log n)行为的事实。具体来说,在整数的情况下,您可以获得的最佳最坏情况行为是O(sqrt(log n)): https://stackoverflow.com/questions/4057258/faster-than-binary-search-for-ordered-list
答案 7 :(得分:-1)
修改二进制搜索以首次查找x的任何出现。