繁体   English   中英

递归二进制搜索“超出范围”

[英]Recursive Binary Search 'Out of Range'

我只是想知道是否有人可以帮助我编写递归二进制搜索。 当前抛出“超出范围”错误:

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;
}

谢谢!

超出范围只是意味着您索引超出了给定序列容器的范围限制(低或高)。 并建议您使用at()来解决问题。

您发布的代码有几个问题。 其中,最具破坏性的是不正确的中点计算。 您正在寻找价值平均值; 不是中点,然后将其用作索引,这显然是错误的。 您的初始mid值也是错误的,因为它是在将任何元素放入容器之前获取的。

还有一点很重要,您应该使用对容器的const引用。 否则,您将在每次递归调用时复制整个容器 看起来似乎没什么大不了的,但是用亿万个项目来做到这一点,我向您保证这将是非常昂贵的。

就是说,您的设置完全错误 递归二进制搜索是关于分而治之的(我希望您知道这一点)。 作为对数搜索的要求, 必须对序列进行排序 除此之外,使用序列容器完成此操作的最简单方法要求您了解三件事:

  • 您寻求的价值(duh)
  • 您开始的项目的索引(或迭代器)。
  • 表示当前分区的序列结束位置的过去一索引(或结束迭代器)。

最后一个总是让算法的新手陷入困境,但是当您开始进行数学运算时才有意义。 总之,把它想成,你不是在搜索的第一个索引,而不是一个包容性的指标,你正在寻找。 这也使得对算法进行迭代或递归编码变得简单。

只有具备以上所有条件 ,才能生成具有转义条件的递归算法,这是至关重要的。 必须有一种方法来停止做您正在做的事情。

使用参数列表并提供缺少的部分,递归版本如下所示:

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-reference和一个值简单地获取向量,然后调用递归函数来进一步简化此过程:

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;
}

同样,我们可以重载它以仅获取一个向量(我们假设它已排序)和一个测试值,但是为什么要停在那里。 我们可以制作一个以template-type作为参数之一的模板,并且由于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);
}

然后,与上一部分相同的测试程序将起作用,并产生相同的结果。


没有递归的二进制搜索

一旦掌握了算法的要点,就会很快发现它很适合迭代算法,而不是递归算法。 老实说,就调用栈空间而言,这无关紧要。 对24亿个排序项的二进制搜索最多只能重复执行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);
}

它产生的结果与我们预期的相同。

让我们仔细看看

    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提供要搜索的项目作为参数。 当前的实现不是可重入的。

仔细研究您的算法。 几件事要注意:

  1. 您的二进制搜索采用2个参数vector和mid,binarySearch(std :: vector vec,int mid)要使任何递归算法正常工作,您需要具备2种条件,即停止条件(因此您不会陷入无限循环中)和一个递归条件(每次递归都使您更接近解决方案,在二进制搜索的情况下,这会使您的搜索空间减少一半)。在这种情况下,传入的向量始终相同,并且开始和结束计算得出的所有内容也相同,每次都产生相同的搜索空间。 您需要递归输入新的起点和终点,或者需要在每次递归传递中修改向量。

  2. 您对mid的定义如下: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; 您可以通过使用开始和结束来获得此值,并且每次在递归函数中都应重新计算。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM