微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

二分查找的实现

如何解决二分查找的实现

我正在尝试实现一个二进制搜索版本,它是一个函数模板。它需要两个迭代器来定义范围,以及它正在搜索的值。如果在范围内找到该值,则函数模板将返回该值的迭代器。如果没有找到,则返回off-the-end迭代器。为简单起见,您必须能够使用提供的迭代器进行迭代器算术。

这是我目前所拥有的:

template <typename It,typename T> It binary(It beg,It end,T val) {

        
    It mid = beg + (end - beg) / 2;
    if (*mid == val) 
        return mid;
             
    else if (*mid < val)
        return mid = binary(mid,end,val);
            
    else if (val < *mid) 
        return mid = binary(beg,mid,val);

    return mid;
} 

如果找到值,这个函数模板就可以正常工作。但是,如果找不到该值,我的程序就会崩溃。发生的情况是,如果在范围内找不到要搜索的值,则迭代器 mid 会“卡住”,因为它指向容器中的最后一个元素或指向容器中的第一个元素容器。当再次进行递归调用时,mid 将重新计算到其完全相同的位置,从而导致无限循环。

我试图通过添加这段代码来修复它:

template <typename It,T val) {
    //New piece of code
    if (end - beg == 1 and *beg < val)
        return end;
    
    if (end == beg)
        //*** WHAT TO RETURN HERE? ***
    
    //old code
    It mid = beg + (end - beg) / 2;
    if (*mid == val) 
        return mid;
             
    else if (*mid < val)
        return mid = binary(mid,val);

    return mid;
} 

如果要搜索的值大于容器中的所有值,这解决了问题,但是如果要搜索的值小于容器中的所有值,那么我返回什么? (请参阅我在代码中的评论,询问“我要返回什么?”)。我想返回一个迭代器,它超过原始范围中的最后一个元素,但是当我达到 beg == end 的条件时,我可能已经对 binary 执行了多次递归调用,并且因此我无法访问原始的 end 迭代器。

我希望所有这些都有意义。我很感激任何帮助。另外,我想在仍然使用递归的同时解决这个问题。

解决方法

{
  if (beg==end) return end;

正确处理空范围。从根本上说,这就是您提出的问题的正确答案。

下一步是备份,看看std库是如何处理的。 std 库公开了两个函数,下界和上界,再加上一个两者兼而有之——范围相等。

下限返回不小于参数的第一个元素,上限返回不小于或等于该元素的第一个元素。

它们一起构成了一系列元素,如果非空,则包含您想要的元素。

你的代码版本

  if (beg==end) return end;

现在是 lower_bound。您可以添加 upper_bound 并通过搜索 equal_rangeanswer 元素 end(又名 >)来生成 !<=,其中 {{1} } 只是小于参数颠倒。

因为它使代码更简洁,所以我写了一个简单的范围类型:

>

捆绑一对迭代器。

template<class It>
struct range {
  It b,e;

  range(It s,It f):b(s),e(f) {}
  range(It s,std::size_t n):range(s,s+n) {}

  It begin()const{ return b; }
  It end()const{ return e; }

  bool empty()const{ return begin()==end(); }
  std::size_t size()const{ return end()-begin(); }

  range without_front(std::size_t n=1)const{
    n = (std::min)(size(),n);
    return {begin()+n,end()};
  }
  range without_back(std::size_t n=1)const{
    n = (std::min)(size(),n);
    return {begin(),end()-n};
  }

  range only_front(std::size_t n=1)const{
    n = (std::min)(size(),begin()+n};
  }
  range only_back(std::size_t n=1)const{
    n = (std::min)(size(),n);
    return {end()-n,end()};
  }

  decltype(*std::declval<It const&>()) front()const
  { return *begin(); }
  decltype(*std::declval<It const&>()) back()const
  { return *std::prev(end()); }
};

所以现在我们有一个有趣的函数来查找等于 template <class It,class T> range<It> binary(range<It> toSearch,T val) { if (toSearch.empty()) return toSearch; // note,for a size 1 range,left can be empty auto left = toSearch.only_front( toSearch.size()/2 ); // while right cannot. auto right = toSearch.without_front( toSearch.size()/2 ); // is val only left or right? If so,recurse if (right.front() < val) { return binary( right.without_front(1),val ); } else if (val < right.front()) { return binary( left,val ); } // otherwise,right.front() == val at this point // find entire range recursively: auto lhs = binary(left,val); auto rhs = binary(right,val); return {lhs.begin(),rhs.end()}; } 的元素范围。

然后我们会解决您的问题:

val

完成。

您会注意到此代码仅使用 template <typename It,typename T> It binary(It beg,It end,T val) { auto r = binary( range<It>{beg,end},val ); if (r.empty()) return end; return r.begin(); } 来比较值,它从不检查 <。这与 std 库的工作方式相匹配。

一般来说,二分查找有两种特殊情况;空的一个,和 1 个元素的一个。以上明确处理空的; 1 元素由 == 隐式处理,这也是对一般搜索的优化(我们已经排除了右边的前元素)。

另一个有趣的事情是这段代码:

.without_front(1)

我们真的想在这里进行 3 向比较。在 C++17 中,我们得到 if (right.front() < val) { return binary( right.without_front(1),val ); } else if (val < right.front()) { return binary( left,val ); } 正是这样做的。这不是巧合。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。