我在排序数组中多次出现一个键,我想对它们执行二进制搜索,正常的二进制搜索会为具有多次出现的键返回一些随机索引,其中我想要最后一次出现的索引那把钥匙。
int data[] = [1,2,3,4,4,4,4,5,5,6,6];
int key = 4;
int index = upperBoundBinarySearch(data, 0, data.length-1, key);
Index Returned = 6
答案 0 :(得分:6)
this answer中的Java实现发现了第一次出现的密钥。有一个comment关于如何更改它以找到 last 出现次数,但该建议会导致无限循环。不过,这个想法似乎很合理。
编辑:经过一番研究,我找到了neat solution on The Algo Blog。由于第一次找到的匹配不一定是必需的,因此您需要跟踪到目前为止的“最佳”匹配。获得匹配后,您将其存储并继续进行该匹配右侧的二进制搜索(low = mid + 1
)。
public static int binarySearch(int[] a, int key) {
return binarySearch(a, 0, a.length, key);
}
private static int binarySearch(int[] a, int fromIndex, int toIndex,
int key) {
int low = fromIndex;
int high = toIndex - 1;
int found = -1;
while (low <= high) {
int mid = (low + high) >>> 1;
int midVal = a[mid];
if (midVal < key) {
low = mid + 1;
} else if (midVal > key) {
high = mid - 1;
} else {
found = mid;
// For last occurrence:
low = mid + 1;
// For first occurrence:
// high = mid - 1;
}
}
return found;
}
此更改保持O(log n)
复杂性。实际上,性能取决于应用程序。当阵列的长度远大于所寻找的密钥的重复量时,对最后一次出现的线性搜索可能更快。但是当存在大量重复时,这种修改后的二进制搜索可能更可取。
答案 1 :(得分:2)
大概你想要一个 O(log N)解决方案? (否则你可以进行线性搜索。)
在C ++中,一种可能性(若干个)是使用std::upper_bound。这将为您提供比您要求的更大的第一个元素的迭代器,因此您需要检查前一个元素。这确实是 O(log N)。
我不知道Java是否提供了这种标准的库方法。但是,upper_bound
的伪代码在上面的链接中给出,并且应该很容易重新实现。
答案 2 :(得分:1)
好吧,多亏@Mattias,这个算法听起来不错。无论如何,我已经完成了自己的工作,似乎我可以提供更好的结果,但是如果有人可以帮助我衡量我和@Mattias算法的复杂性,或者任何人有更好的解决方案,那么欢迎.... 。 无论如何这里是我找到的问题的解决方案,
int upperBound(int[] array,int lo, int hi, int key)
{
int low = lo-1, high = hi;
while (low+1 != high)
{
int mid = (low+high)>>>1;
if (array[mid]> key) high=mid;
else low=mid;
}
int p = low;
if ( p >= hi || array[p] != key )
p=-1;//no key found
return p;
}
这是第一次出现,我也用另一个类似的帖子First occurrence in a binary search
更新相同的内容int lowerBound(int[] array,int lo, int hi, int key)
{
int low = lo-1, high = hi;
while (low+1 != high)
{
int mid = (low+high)>>>1;
if (array[mid]< key) low=mid;
else high=mid;
}
int p = high;
if ( p >= hi || array[p] != key )
p=-1;//no key found
return p;
}
答案 3 :(得分:0)
找到钥匙时。而不是返回它在数组上执行顺序搜索以获取最后一个。这将是O(N)解决方案。
答案 4 :(得分:0)
这是二进制搜索的递归版本。 对该版本进行一些调整将使您以零的努力和相同的复杂度O(log-n)获得最后一个索引或第一个索引。
原始的二进制搜索递归版本如下:
public static int binarySearch(List<Integer> a, int startIndex, int endIndex, int key) {
int midIndex = (endIndex - startIndex)/2 + startIndex;
if (a.get(midIndex) == key) // found!
return midIndex;
if (startIndex == endIndex || startIndex == endIndex - 1)
return -1;
else if (a.get(midIndex) > key) // Search in the left
return binarySearch(a, 0, midIndex, key);
else if (a.get(midIndex) < key) // Search in the right
return binarySearch(a, midIndex, endIndex, key);
else
return -1; // not found
}
只需对第一个if语句稍作更改,即可获得第一个索引:
public static int binarySearchLowIndex(List<Integer> a, int startIndex, int endIndex, int key) {
int midIndex = (endIndex - startIndex)/2 + startIndex;
if (a.get(midIndex) == key && a.get(midIndex - 1) != key) // found!
return midIndex;
if (startIndex == endIndex || startIndex == endIndex - 1)
return -1;
else if (a.get(midIndex) >= key) // Search in the left
return binarySearchLowIndex(a, 0, midIndex, key);
else if (a.get(midIndex) < key) // Search in the right
return binarySearchLowIndex(a, midIndex, endIndex, key);
else
return -1; // not found
}
最后一个索引也是如此:
public static int binarySearchHighIndex(List<Integer> a, int startIndex, int endIndex, int key) {
int midIndex = (endIndex - startIndex)/2 + startIndex;
if (a.get(midIndex) == key **&& a.get(midIndex + 1) != key**) // found!
return midIndex;
if (startIndex == endIndex || startIndex == endIndex - 1)
return -1;
else if (a.get(midIndex) > key) // Search in the left
return binarySearchHighIndex(a, 0, midIndex, key);
else if (a.get(midIndex) <= key) // Search in the right
return binarySearchHighIndex(a, midIndex, endIndex, key);
else
return -1; // not found
}
以下是一些测试示例(基于Junit):
@Test
public void binarySearchTest() {
assert(BinarySearch.binarySearch(Arrays.asList(5, 7, 7, 8, 8, 10), 0, 5, 5) == 0);
}
@Test
public void binarySearchLowIndexTest() {
assert(BinarySearch.binarySearchLowIndex(Arrays.asList(5, 8, 8, 8, 8, 10), 0, 5, 8) == 1);
}
@Test
public void binarySearchHighIndexTest() {
assert(BinarySearch.binarySearchHighIndex(Arrays.asList(5, 8, 8, 8, 8, 10), 0, 5, 8) == 4);
}
答案 5 :(得分:-2)
在二进制搜索中,您将键与数组数据[i]的元素进行比较。要获得最后一个匹配的索引,您应该更改比较函数,以便即使key等于data [i]也等于data [i + 1],它也会给出不等式。
int upperBoundBinarySearch(int data[],int start, int end, int key) {
while(start < end) {
int middle = start + (end-start)/2;
if (data[middle] == key && (middle == end || data[middle+1] != key))
return middle;
if (data[middle] > key)
end = middle;
else {
if (start == middle)
return start;
start = middle;
}
}
return start;
}