2013-01-19 46 views
1

我有一个排序数组中的键的多次出现,我想对它们进行二分搜索,一个普通的二分搜索返回一些随机索引为有多个出现的键,其中,我想要的最后一次出现的索引那把钥匙。使用二进制搜索的多个键的最后索引?

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 
+6

Java和C++是不同的语言更新一样,你有兴趣吗? –

+0

是'int data [] = [1,2,3,4,4,4,4,5,5,6,6];'在Java中是否正确?我不这么认为。 – Nawaz

+1

对于C++,您可以尝试许多[标准算法](http://en.cppreference.com/w/cpp/algorithm)。 –

回答

1

好吧,多亏了所有@Mattias,算法听起来不错。无论如何,我已经完成了我自己的事情,似乎我给了更好的结果,但是如果有人能够帮助我衡量算法和Mine @Mattias的复杂性,或者任何人有更好的解决方案,它都欢迎.... 。 总之这里是解决方案,我发现这个问题,

int upperBound(int[] array,int fromIndex, int toIndex, int key) 
{ 
    int low = fromIndex-1, high = toIndex; 
    while (low+1 != high) 
    { 
     int mid = (low+high)>>>1; 
     if (array[mid]> key) high=mid; 
     else low=mid; 
    } 
    int p = low; 
    if (p >= toIndex || array[p] != key) 
     p=-1;//no key found 
    return p; 
} 

这是首次发生的,我也有一个其他类似的帖子First occurrence in a binary search

int lowerBound(int[] array,int fromIndex, int toIndex, int key) 
{ 
    int low = fromIndex-1, high = toIndex; 
    while (low+1 != high) 
    { 
     int mid = (low+high)>>>1; 
     if (array[mid]< key) low=mid; 
     else high=mid; 
    } 
    int p = high; 
    if (p >= toIndex || array[p] != key) 
     p=-1;//no key found 
    return p; 
} 
+1

因此,基本上,你[那篇文章宾利的方法]把(http://the-algo-blog.blogspot.be/2011/06/binary -search-to-find-last-and-first.html)并稍微重写它以查找最后一次出现而不是第一次?好的工作,但我必须同意这篇文章的作者:我也发现这个算法更复杂,更难理解。例如,我没有看到何时以及如何最终检查'找不到密钥'。 –

+0

是的,我发现从宾利的指数较低的算法,我只是稍微修改它以适应我的需要。是它的小麻烦,在被降低指数的情况下,存储最后在循环高的可变匹配,但在结束它确保了最后的存储值实际上是一个匹配,如将其存储在这两种情况下> = – rykhan

+0

@MattiasBuelens那最终检查是必要的。考虑数组为空时的情况。 – nawfal

2

想必你想要一个为O(log N)解决方案? (否则,你可能只是做一个线性搜索。)

在C++中,有一种可能性(超出几种),是使用std::upper_bound。这会给你一个迭代器,它的第一个元素大于你所要求的,所以你需要检查前一个元素。这的确是O(log N)

我不知道Java是否提供这种标准库方法。然而,upper_bound的伪代码在上面的链接中给出,应该很容易重新实现。

+0

是的,我想这UPPER_BOUND转换在Java中,但它总是给我的upper_bound_index + 1,并且有一定的问题,我的转换,我任何如何在阵列10K条目了,我要同一个键具有多个事件的上部和下部的索引,其中一些比线性更好的解决方案,二进制将正常工作与我因为它提供了我ATLEAST O(log2N)溶液 – rykhan

-2

在二分查找中,您将键与数组数据[i]的元素进行比较。为了得到最后一个匹配索引,你应该改变你的比较函数,这样即使密钥等于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; 
} 
+0

这将不能正常工作,并且不甚至可能与大多数搜索框架一起使用。 –

+0

添加工作片段... –

5

this answer的Java实现找到第一发生的一个关键的。有一个comment关于如何改变这个以找到最后的发生,但建议导致无限循环。虽然这个想法听起来很合理。

编辑:经过一番研究,我发现了一个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)的复杂性。但是,实际性能取决于应用程序。当阵列的长度远大于所寻找关键字的重复数量时,对最后一次出现的线性搜索可能会更快。虽然有很多重复,但这种修改的二进制搜索可能更可取。

+0

这是一个聪明的想法。 – MrSmith42

+0

我找到了一个工作解决方案,更新了我的答案。 –

+1

+1为理念,稍作修改的代码可能返所谓的插入点的最后一行:回报(发现= - 1!)?发现: - (低+ 1); –

0

当你找到钥匙。而不是返回它在数组上进行顺序搜索以获得最后一个。这将是O(N)解决方案。

+0

这不适合我,反正谢谢 – rykhan

+0

你想要的复杂性是什么?你可以在里面玩一些。让我知道你的约束 – blackmath

+0

@RizwanYasin这是一个体面的解决方案。复杂性是不是为O(n),但为O(log N + K) – nawfal