2013-01-19 41 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