2017-05-08 78 views
1

只是想知道如果有人能夠幫助我,我試圖編寫遞歸二進制搜索。 這是目前拋出了一個「超出範圍」的錯誤:遞歸二進制搜索'超出範圍'

terminate called after throwing an instance of 'std::out_of_range' 
what(): vector::_M_range_check: __n (which is 0) >= this->size() (which is 0) 
Aborted (core dumped) 

我敢肯定,這是在寫正確的遞歸(這我仍然在新的)我的失敗嘗試的事情。如果有人能夠給我一個關於問題出在哪裏的提示,我會非常感激。 這裏是我的代碼:

RecursiveBinarySearch.cpp

// RecursiveBinarySearch class constructor. 
RecursiveBinarySearch::RecursiveBinarySearch() 
{ 

} 

// Sets the object that is being searched for. 
// In this case, we are always looking for the integer '1'. 
int obj = 1; 

// Searching the vector given for obj. if obj is found the function returns true, otherwise it returns false. 
bool RecursiveBinarySearch::binarySearch(std::vector<int> vec, int mid) 
{ 
    int start = 0, end = vec.size() - 1; 
    std::cout << "mid : " << mid << "\n"; 

    while (start + 1 < end) 
    { 

     if (vec.at(mid) == obj) 
      return true; 

     else if (vec.at(mid) > obj) 
      //end = mid - 1; 
      return binarySearch(vec, mid - 1); 

     else 
      //start = mid + 1; 
      return binarySearch(vec, mid + 1); 

    } 

    if ((vec.at(start) == obj) || (vec.at(end) == obj)) 
     return true; 
    else 
    { 
     return false; 
    } 
} 

// RecursiveBinarySearch class destructor. 
RecursiveBinarySearch::~RecursiveBinarySearch() 
{ 

} 

main.cpp中:

int main() 
{ 

    // The user inputs a string of numbers (e.g. "6 4 -2 88 ..etc") and those integers are then put into a vector named 'vec'. 
    std::vector<int> vec; 
    int vecSize = vec.size(); 
    int mid = (vec.at(0) + vec.at(vecSize - 1))/2; 

    std::string line; 
    if (getline(std::cin, line)) 
    { 
     std::istringstream str(line); 

     int value; 
     str >> value; 
     vec.push_back(value); 
     while (str >> value) 
     { 
      vec.push_back(value); 
     } 
    } 

    // Creating RecursiveBinarySearch object. 
    RecursiveBinarySearch bSearch; 
    RecursiveBinarySearch *ptrBSearch = &bSearch; 
    bool bS = ptrBSearch->binarySearch(vec, mid); 

    // Print out inputted integers. 
    std::cout << "Binary Search Result: \n"; 
    std::cout << bS << "\n"; 

    return 0; 
} 

謝謝!

+0

Off topic:'bool RecursiveBinarySearch :: binarySearch(std :: vector vec,int mid)'通過值傳遞向量。儘管編譯器變得非常聰明,但這可能會有很多複製和大量內存。不過,我更喜歡'const'引用。 'bool RecursiveBinarySearch :: binarySearch(const std :: vector &vec,int mid)' – user4581301

+0

相關的,這組用戶提供的數字是排序的,* right *?否則對數搜索不起作用。我想告訴你使用['std :: lower_bound'](http://en.cppreference。com/w/cpp/algorithm/lower_bound)以及非常多的報廢都會過分簡化。 – WhozCraig

+0

是的,我只是沒有添加快速排序的所有代碼,希望不會讓人們通過更多的代碼,而不是通過必要的代碼。對不起,我應該提到這一點。 –

回答

2

超出範圍簡單意味着您的索引超出了您的給定序列容器的範圍限制(低或高)。道具給你使用at()來解決問題。

您發佈的代碼有幾個問題。其中,最具破壞性的是中點計算不當。你正在尋找價值平均值;而不是中點,然後用這些作爲索引,這顯然是錯誤的。您的初始mid值也是錯誤的,因爲它是在您的容器中存在任何元素之前進行的。

重要的一點也是,你應該對容器使用const引用。否則,您將使用每次遞歸調用將整個容器複製到。這可能看起來並不是什麼大不了的事情,但是要做到這一點,有着十億件物品,我向你保證這將會是一件非常昂貴的事情。

也就是說,你的設置只是錯誤。遞歸二分搜索是關於分而治之的(我希望你知道)。作爲對數搜索的要求,序列必須排序。除此之外,最直接的方式與順序容器做到這一點,你需要知道的三兩件事:

  • 你所尋求的價值你的項目(杜)
  • 指數(或迭代器)從...開始。
  • 單向過去的索引(或結束迭代器)表示當前分區的結束序列位置。

最後一個問題總是讓人們對循環算法提出新的看法,但是當你開始進行數學運算時,它是有意義的。簡而言之,將其視爲您搜索的第一個索引,而不是而不是,而不是包含索引,其中。它還可以迭代地或遞歸地直接編碼算法。

只有當你具備以上所有條件時,才能產生一個轉義條件的遞歸算法,這是至關重要的。你必須有辦法停止做你在做什麼。

使用你的參數列表,並提供了缺失的部分,一個遞歸的版本是這樣的:

bool binarySearchR(std::vector<int> const& v, size_t beg, size_t end, int val) 
{ 
    // when these are equal it means there are no elements 
    // left to search, and that means no match was found. 
    if (beg == end) 
     return false; 

    // find midpoint 
    size_t mid = beg + (end-beg)/2; 

    // if the test value is less, recurse to upper partition 
    // important: we just checked 'mid', so the lower point 
    // is one *past* that; therefore ++mid is the recursed 
    // 'beg' index. 
    if (v[mid] < val) 
     return binarySearchR(v, ++mid, end, val); 

    // if the test value is greater, recurse to lower partition 
    // important: we don't check the 'end' index, it's the 
    // stopping point so just pass it as the recursed 'end' index; 
    // 'mid' is therefore not modified here. 
    if (val < v[mid]) 
     return binarySearchR(v, beg, mid, val); 

    // not lesser, not greater, thus equal 
    return true; 
} 

您可以進一步通過重載函數以const引用和值簡單地採取矢量簡化這個,然後調用遞歸函數:

bool binarySearchR(std::vector<int> const& v, int val) 
{ 
    return binarySearchR(v, 0, v.size(), val); 
} 

這使您可以調用它像這樣:

int main() 
{ 
    std::vector<int> vec { 1,2,3,4,6,9,10 }; 
    std::cout << std::boolalpha; 

    for (int i=-1; i<=11; ++i) 
     std::cout << std::setw(2) << i << ':' << binarySearchR(vec, i) << '\n'; 
} 

輸出

-1:false 
0:false 
1:true 
2:true 
3:true 
4:true 
5:false 
6:true 
7:false 
8:false 
9:true 
10:true 
11:false 

的輸出是如預期的,並且測試值和邊緣情況正常工作。


基於迭代器的遞歸二進制搜索

一種基於迭代器的方法是更加內嵌使用C如何現代++工程,並作爲紅利發放操作到其他序列容器,如std::deque。它遵循與上述相同的整體設計,但採用了基於模板的Iter類型:

template<class Iter> 
bool binarySearchR(Iter beg, Iter end, typename std::iterator_traits<Iter>::value_type const& arg) 
{ 
    if (beg == end) 
     return false; 

    Iter mid = std::next(beg, std::distance(beg,end)/2); 
    if (*mid < arg) 
     return binarySearchR(++mid, end, arg); 

    if (arg < *mid) 
     return binarySearchR(beg, mid, arg); 

    return true; 
} 

我們同樣可以超載這花一向量(我們假設排序)和測試值,但爲什麼停在那裏。我們可以製作一個模板,需要一個模板型作爲參數之一,並且由於C++ 11和可變參數模板參數,它帶來了很好的解決方案:

template<class T, template<class, class...> class C, class... Args> 
bool binarySearchR(C<T,Args...> const& seq, T const& val) 
{ 
    return binarySearchR(std::begin(seq), std::end(seq), val); 
} 

從現有部分中的相同的測試程序然後將工作,併產生相同的結果。


二進制搜索沒有遞歸

一旦你獲得了算法的竅門,你很快就會發現它很適合迭代算法,而不是遞歸。在調用堆棧空間方面,它確實無關緊要。對二十四億分揀物品進行二分搜索最多隻能緩存31次,但仍然是不必要的呼叫,如果我們能夠避免它們,這將是很好的。而且它可以優化更好,也總有一些值得考慮:

template<class Iter> 
bool binarySearchI(Iter beg, Iter end, typename std::iterator_traits<Iter>::value_type const& arg) 
{ 
    while (beg != end) 
    { 
     Iter mid = std::next(beg, std::distance(beg,end)/2); 
     if (*mid < arg) 
      beg = ++mid; 
     else if (arg < *mid) 
      end = mid; 
     else return true; 
    } 
    return false; 
} 

同樣適用於重載:

template<class T, template<class, class...> class C, class... Args> 
bool binarySearchI(C<T,Args...> const& seq, T const& val) 
{ 
    return binarySearchI(std::begin(seq), std::end(seq), val); 
} 

它產生我們期望相同的結果。

+0

哇,這是一個偉大的,深入的答案。謝謝你的幫助! :) –

0

讓我們在

std::vector<int> vec; 
    int vecSize = vec.size(); 
    int mid = (vec.at(0) + vec.at(vecSize - 1))/2; 

細看其分解,我們得到

std::vector<int> vec; 

創建一個空的載體

int vecSize = vec.size(); 

空載體的大小將爲零,所以

int mid = (vec.at(0) + vec.at(vecSize - 1))/2; 

始終解析

int mid = (vec.at(0) + vec.at(0 - 1))/2; 

vec.at(0 - 1)不是矢量去的好地方。

解決方案:計算中旬以後裝載後的載體。

在事後,考慮提供binarySearch與該項目以搜索作爲參數。目前的實施不可重入。

0

以你的算法一探究竟。要注意以下幾點:在2 PARAMS矢量和中

  1. 您的二進制搜索需要,的binarySearch(標準::向量VEC,INT中旬) 對於任何遞歸算法工作,你需要有兩件事情,一個停止條件(所以你不會在無限循環中結束)和遞歸條件(每次遞歸都會讓你更接近你的解決方案,如果是二分搜索,它會減少你的搜索空間的一半) 在你的情況下您在通過矢量始終是相同的,並且計算的開始和結束,也同樣應有盡有,導致同樣的搜索空間每次。你需要遞歸地傳遞一個新的開始和結束,或者你需要在每個遞歸遍中修改你的向量。您可以按如下方式定義您: int mid =(vec.at(0)+ vec.at(vecSize - 1))/ 2; 通過這樣做,您將您的中間定義爲矢量的第一個和最後一個元素的平均值,而不是其位置。例如。 vector = [2,5,60,75,80],你的中間是(2 + 80)/ 2 = 41,而41肯定不是你矢量中的有效位置。你的中間應該是(0 + 4)/ 2 = 2;你可以通過使用你的開始和結束得到它,並且應該每次在遞歸函數中重新計算。