跟我学c++中级篇——STL算法之查找

本文详细介绍了查找算法的种类,包括顺序查找、二分查找等,并探讨了STL中查找算法的分类,如已排序和未排序容器的查找。通过源码分析展示了`find`和`binary_search`等函数的工作原理,同时提供了实例演示了如何在C++中使用这些查找算法。STL的目标是简化编程并提高效率,但也允许自定义算法以适应特定需求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

一、查找算法

无论是在现实世界还是在计算机的虚拟世界中,对一个物品或者说对象的查找搜索,都是广泛出现的。比如我们在众多的苹果中寻找一个最大的,在很多的道路中查找出一个最近的。而这些映射到计算机世界中,就形成了抽象的查找算法,比如在编译器编译源码时会寻找一些TOKEN。查找的算法有很多类型,可以把相关的数据排序后查找,也可以不排序查找。可以只查找,不管理数据;也可以边查找边增加或者删除。
正如前面排序,查找也可以用复杂度来描述,主要包括两个方面:
1、平均查找长度:ASL(Average Search Length),查找运算中,因为需要耗费时间在关键字的比较上,故把平均需要和待查找值比较的关键字次数称为平均查找长度。
2、查找时间复杂度:简单的说就是需要的时间的量化表示。

主要包括:
1、顺序查找:非常好理解,一个个来,直到找到自己的需要的。
2、二分查找:也称折半查找,就是排序后不断的再次折半来查找具体目标。
3、插值查找:二分查找的进一步推广,将中间点的选择改为动态判定。
4、斐波那契查找:二分查找的一种推广,基于黄金分割点。
5、树表查找:利用树的特点为来查找
6、分块查找:基于分治的思想进一步推进查找的方法。
7、哈希查找:通过哈希算法来查找。

二、STL算法中的查找分类

STL中对查找分为两大类:
一类为已排序容器中进行查找,如:binary_search
另外一类为非排序容器中进行查找,如:find,find_if

从查找内容上,可以分为:
查找单个元素,如:find,min_element,find_first_of
查找区间元素,如:search,lower_bound,equal_range
查找条件元素,如:find_if,equal,count,value_comp,key_comp
更多请看:

 

三、源码分析

这里对典型的查找算法源码进行一下分析:

//find
template<class _InIt,
    class _Ty>
    _NODISCARD inline _InIt find(_InIt _First, const _InIt _Last, const _Ty& _Val)
    {   // find first matching _Val
    _Adl_verify_range(_First, _Last);
    _Seek_wrapped(_First,
        _Find_unchecked(_Get_unwrapped(_First), _Get_unwrapped(_Last), _Val));
    return (_First);
    }

template<class _InIt,
    class _Ty> inline
    _InIt _Find_unchecked1(_InIt _First, const _InIt _Last, const _Ty& _Val, false_type)
    {   // find first matching _Val  源码就是个迭代遍历
    for (; _First != _Last; ++_First)
        if (*_First == _Val)
            break;
    return (_First);
    }
//二分法查找 
template<class _FwdIt,
    class _Ty,
    class _Pr>
    _NODISCARD inline bool binary_search(_FwdIt _First, _FwdIt _Last, const _Ty& _Val, _Pr _Pred)
    {   // test if _Val equivalent to some element, using _Pred
    _Adl_verify_range(_First, _Last);
    auto _UFirst = _Get_unwrapped(_First);
    const auto _ULast = _Get_unwrapped(_Last);
    _UFirst = _STD lower_bound(_UFirst, _ULast, _Val, _Pass_fn(_Pred));
    return (_UFirst != _ULast && !_Pred(_Val, *_UFirst));
    }
template<class _FwdIt,
    class _Ty,
    class _Pr>
    _NODISCARD inline _FwdIt lower_bound(_FwdIt _First, const _FwdIt _Last,
        const _Ty& _Val, _Pr _Pred)
    {   // find first element not before _Val, using _Pred
    _Adl_verify_range(_First, _Last);
    auto _UFirst = _Get_unwrapped(_First);
    _Iter_diff_t<_FwdIt> _Count = _STD distance(_UFirst, _Get_unwrapped(_Last));

    while (0 < _Count)
        {   // divide and conquer, find half that contains answer 从一半中查找 
        const _Iter_diff_t<_FwdIt> _Count2 = _Count >> 1; // TRANSITION, VSO#433486
        const auto _UMid = _STD next(_UFirst, _Count2);
        if (_Pred(*_UMid, _Val))
            {   // try top half
            _UFirst = _Next_iter(_UMid);
            _Count -= _Count2 + 1;
            }
        else
            {
            _Count = _Count2;
            }
        }

    _Seek_wrapped(_First, _UFirst);
    return (_First);
    }

看上面的代码发现如果除去一些复杂的定义和模板的判别,其实是非常简单的。所以说简单才是美。这是每个Coder的追求才对。

四、实例

看一下几个相关的实例(cppreference.com):

#include <iostream>
#include <algorithm>
#include <vector>
#include <iterator>

int main()
{
    std::vector<int> v{1, 2, 3, 4};
    int n1 = 3;
    int n2 = 5;
    auto is_even = [](int i){ return i%2 == 0; };

    auto result1 = std::find(begin(v), end(v), n1);
    auto result2 = std::find(begin(v), end(v), n2);
    auto result3 = std::find_if(begin(v), end(v), is_even);

    (result1 != std::end(v))
        ? std::cout << "v contains " << n1 << '\n'
        : std::cout << "v does not contain " << n1 << '\n';

    (result2 != std::end(v))
        ? std::cout << "v contains " << n2 << '\n'
        : std::cout << "v does not contain " << n2 << '\n';

    (result3 != std::end(v))
        ? std::cout << "v contains an even number: " << *result3 << '\n'
        : std::cout << "v does not contain even numbers\n";
}

输出结果:

v contains 3
v does not contain 5
v contains an even number: 2
#include <algorithm>
#include <vector>
#include <iostream>

struct S
{
    int number;
    char name;
    // note: name is ignored by this comparison operator
    bool operator< ( const S& s ) const { return number < s.number; }
};

int main()
{
    // note: not ordered, only partitioned w.r.t. S defined below
    const std::vector<S> vec = { {1,'A'}, {2,'B'}, {2,'C'}, {2,'D'}, {4,'G'}, {3,'F'} };

    const S value = {2, '?'};

    std::cout << "Compare using S::operator<(): ";
    const auto p = std::equal_range(vec.begin(), vec.end(), value);

    for ( auto i = p.first; i != p.second; ++i )
        std::cout << i->name << ' ';


    std::cout << "\n" "Using heterogeneous comparison: ";
    struct Comp
    {
        bool operator() ( const S& s, int i ) const { return s.number < i; }
        bool operator() ( int i, const S& s ) const { return i < s.number; }
    };

    const auto p2 = std::equal_range(vec.begin(),vec.end(), 2, Comp{});

    for ( auto i = p2.first; i != p2.second; ++i )
        std::cout << i->name << ' ';
}

输出结果:

Compare using S::operator<(): B C D 
Using heterogeneous comparison: B C D
#include <iostream>
#include <algorithm>
#include <vector>

int main()
{
    std::vector<int> haystack {1, 3, 4, 5, 9};
    std::vector<int> needles {1, 2, 3};

    for (auto needle : needles) {
        std::cout << "Searching for " << needle << '\n';
        if (std::binary_search(haystack.begin(), haystack.end(), needle)) {
            std::cout << "Found " << needle << '\n';
        } else {
            std::cout << "no dice!\n";
        }
    }
}

运行结果:

Searching for 1
Found 1
Searching for 2
no dice!
Searching for 3
Found 3

由此可见,STL的目标仍然是把c++的编程难度门槛降低然后提高便捷性。在网站上还有更多的例程和相关的说明,请大家自行查阅!

五、总结

无论是在查找还是其它算法中,STL都会提供一个接近平衡的算法来处理相关的元素,这也意味着在某些情况下,可能效率会有不足,此时可以使用提供自定义算法的接口来实现更好的算法实现。但是一般到这种地步,更多的是完全手撸代码了,STL反而不是问题。从STL的源码中可以学习到什么?看到什么设计思想?能不能为我所用,这才是学习STL和其相关源码的主要目的。
切中肯綮!方可游刃有余!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值