STL 算法

  • 算法部分主要由头文件 <algorithm>,<numeric>和<functional>组成。
  • <algorithm>是所有STL头文件中最大的一个,其中常用到的功能范围涉及到比较、交换、查找、遍历操作、复制、修改、反转、排序、合并等等。
  • <numeric>体积很小,只包括几个在序列上面进行简单数学运算的模板函数,包括加法和乘法在序列上的一些操作。
  • <functional>中则定义了一些模板类,用以声明函数对象。
  • STL提供了大量实现算法的模版函数,只要我们熟悉了STL之后,许多代码可以被大大的化简,只需要通过调用一两个算法模板,就可以完成所需要的功能,从而大大地提升效率。

STL中算法分类

  • 操作对象
    • 直接改变容器的内容
    • 将原容器的内容复制一份,修改其副本,然后传回该副本
  • 功能:
    • 非可变序列算法 指不直接修改其所操作的容器内容的算法
      • 计数算法 count、count_if
      • 搜索算法 search、find、find_if、find_first_of、…
      • 比较算法 equal、mismatch、lexicographical_compare
    • 可变序列算法 指可以修改它们所操作的容器内容的算法
      • 删除算法 remove、remove_if、remove_copy、…
      • 修改算法 for_each、transform
      • 排序算法 sort、stable_sort、partial_sort、
    • 排序算法 包括对序列进行排序和合并的算法、搜索算法以及有序序列上的集合操作
    • 数值算法 对容器内容进行数值计算

算法统计

  • 查找算法(13个):判断容器中是否包含某个值

  • 堆算法(4个)

  • 关系算法(8个)

  • 集合算法(4个)

  • 列组合算法(2个)

  • 排序和通用算法(14个):提供元素排序策略

  • 删除和替换算法(15个)

  • 生成和变异算法(6个)

  • 算数算法(4个)

常用算法汇总

  • 常用的查找算法:
    adjacent_find() //adjacent 是邻近的意思
    binary_search(), count(), count_if(), equal_range(), find(),find_if()
    
  • 常用的排序算法:
    merge(), sort(), random_shuffle() // shuffle是洗牌的意思 
    reverse()
    
  • 常用的拷贝和替换算法:
    copy(), replace(), replace_if(), swap()
    
  • 常用的算术和生成算法:
    accumulate() // accumulate 是求和的意思
    fill()
    
  • 常用的集合算法:
    set_union(),set_intersection(),set_difference()
    
  • 常用的遍历算法:
    for_each(), transform() //transform 是变换的意思
    

常用的查找算法

  • adjacent_find ()
    在 iterator 对标识元素范围内,查找一对相邻重复元素,找到则返回指向这对元素的第一个元素的迭代器。否则返回 past-the-end

    void demo1()
    {
        vector<int> vecInt;
        vecInt.push_back(1);
        vecInt.push_back(2);
        vecInt.push_back(2);
        vecInt.push_back(4);
        vecInt.push_back(5);
        vecInt.push_back(5);
    
        vector<int>::iterator it = adjacent_find(vecInt.begin(), vecInt.end());
        //*it == 2
        if (it ==vecInt.end())
            cout << "not found" << endl;
        else
            cout << *it << endl;
    }
    
  • binary_search ()
    在有序序列中查找value,找到则返回true。注意:在无序序列中,不可使用

    void demo2()
    {
        // set 插入的时候就排好序了
        set<int> setInt;
        setInt.insert(3);
        setInt.insert(1);
        setInt.insert(7);
        setInt.insert(5);
        setInt.insert(9);
    
        bool bFind = binary_search(setInt.begin(),setInt.end(),5);
    
        if (bFind == 1)
            cout << "true" << endl;
        else
            cout << "false" << endl;
    }
    
  • count ()
    利用等于操作符,把标志范围内的元素与输入值比较,返回相等的个数

    void demo3()
    {
        vector<int> vecInt;
        vecInt.push_back(1);
        vecInt.push_back(2);
        vecInt.push_back(2);
        vecInt.push_back(4);
        vecInt.push_back(2);
        vecInt.push_back(5);
        int iCount = count(vecInt.begin(),vecInt.end(),2);  //iCount==3
    
        cout << iCount << endl;
    }   
    
  • count_if ()

    //先定义比较函数
    bool GreaterThree(int iNum) {
        return iNum >= 3 ? true : false;
    }
    
    template<typename T>
    class A {
    public:
        bool operator()(T iNum)  {
            return iNum >= 3 ? true : false;
        }
    };
    
    void demo4()
    {
        vector<int> vecInt;
        vecInt.push_back(1);
        vecInt.push_back(3);
        vecInt.push_back(5);
        vecInt.push_back(7);
        vecInt.push_back(9);
    
        A<int> a;
    
        //回调函数可以是GreaterThree、a、A<int>()
        int iCount = count_if(vecInt.begin(), vecInt.end(), A<int>());
    
        //此时iCount == 4
        cout << iCount << endl;
    }
    
  • find ()

    • find: 利用底层元素的等于操作符,对指定范围内的元素与输入值进行比较。当匹配时,结束搜索,返回该元素的迭代器
    • equal_range: 返回一对 iterator,第一个表示 lower_bound, 第二个表示 upper_bound
      void demo5() 
      {
          vector<int> vecInt;
          vecInt.push_back(1);
          vecInt.push_back(3);
          vecInt.push_back(5);
          vecInt.push_back(7);
          vecInt.push_back(9);
      
          vector<int>::iterator it = find(vecInt.begin(), vecInt.end(), 5);
          // *it = 5
          cout << *it << endl;
      }
      
  • find_if ()
    使用输入的函数代替等于操作符执行find。返回被找到的元素的迭代器

    //先定义比较函数
    bool GreaterThree(int iNum) {
        return iNum >= 3 ? true : false;
    }
    
    void demo6()
    {
        vector<int> vecInt;
        vecInt.push_back(1);
        vecInt.push_back(3);
        vecInt.push_back(5);
        vecInt.push_back(7);
        vecInt.push_back(9);
    
        vector<int>::iterator it =  find_if(vecInt.begin(), vecInt.end(), GreaterThree);
    
        // *it = 3
        cout << *it << endl;
    }
    

常用的排序算法

  • merge ()
    合并两个有序序列,存放到另一个序列

    void demo7() 
    {
        vector<int> vecIntA;
        vecIntA.push_back(1);
        vecIntA.push_back(3);
        vecIntA.push_back(5);
        vecIntA.push_back(7);
        vecIntA.push_back(9);
    
        vector<int> vecIntB;
        vecIntB.push_back(2);
        vecIntB.push_back(3);
        vecIntB.push_back(6);
        vecIntB.push_back(7);
    
        vector<int> vecIntC;
        vecIntC.resize(vecIntA.size() + vecIntB.size());
        merge(vecIntA.begin(), vecIntA.end(), vecIntB.begin(), vecIntB.end(),vecIntC.begin());
    
    
        for(auto x : vecIntC)
            cout << x << endl;
    }
    
  • sort ()
    以默认升序的方式重新排列指定范围内的元素。若要改排序规则,可以输入比较函数。

    //学生类
    class CStudent
    {
    public:
        CStudent(int iID, string strName)
        {
            m_iID = iID;
            m_strName = strName;
        }
        friend bool Compare(const CStudent &stuA,const CStudent &stuB);
        friend void demo8();
    private:
        int m_iID;
        string m_strName;
    };
    
    //学号比较函数
    bool Compare(const CStudent &stuA, const CStudent &stuB)
    {
        return (stuA.m_iID < stuB.m_iID);
    }
    
    
    void demo8()
    {
        vector<CStudent> vecStu;
        vecStu.push_back(CStudent(2,"老二"));
        vecStu.push_back(CStudent(1,"老大"));
        vecStu.push_back(CStudent(3,"老三"));
        vecStu.push_back(CStudent(4,"老四"));
    
        
        sort(vecStu.begin(),vecStu.end(),Compare);
    
        // 此时,vecStu容器包含了按顺序的"老大对象","老二对象","老三对象","老四对象"
        for(auto x : vecStu)
            cout << x.m_iID << " " << x.m_strName << endl;
    }
    
  • random_shuffle ()
    对指定范围内的元素随机调整次序

    void demo9()
    {
        //设置随机种子
        srand(time(0));         
    
        vector<int> vecInt;
        vecInt.push_back(1);
        vecInt.push_back(3);
        vecInt.push_back(5);
        vecInt.push_back(7);
        vecInt.push_back(9);
    
        string str("helloworld");
    
        random_shuffle(vecInt.begin(), vecInt.end());   // 顺序容器随机排序,结果比如:9,7,1,5,3
        random_shuffle(str.begin(), str.end()); // 字符串随机排序,结果比如:"lolwoehlrd"       
    
        for(auto x : vecInt)
            cout << x << " ";
        cout << endl << str << endl;
    }
    
  • reverse ()

    void demo10()
    {
        vector<int> vecInt;
        vecInt.push_back(1);
        vecInt.push_back(3);
        vecInt.push_back(5);
        vecInt.push_back(7);
        vecInt.push_back(9);
    
        reverse(vecInt.begin(), vecInt.end());      //{9,7,5,3,1}
    
        for(auto x : vecInt)
            cout << x << endl;
    }
    

常用的拷贝和替换算法

  • copy ()
    拷贝指定范围所有元素到新的容器

    void demo11()
    {
        vector<int> vecIntA;
        vecIntA.push_back(1);
        vecIntA.push_back(3);
        vecIntA.push_back(5);
        vecIntA.push_back(7);
        vecIntA.push_back(9);
    
        vector<int> vecIntB;
        vecIntB.resize(vecIntA.size());  //扩大空间
    
        copy(vecIntA.begin(), vecIntA.end(), vecIntB.begin());  //vecIntB: {1,3,5,7,9}
    
        for(auto x : vecIntB)
            cout << x << endl;
    }
    
  • replace ()

    replace(beg, end, oldValue, newValue)
    

    将指定范围内的所有等于 oldValue 的元素替换成 newValue

    void demo12()
    {
        vector<int> vecIntA;
        vecIntA.push_back(1);
        vecIntA.push_back(3);
        vecIntA.push_back(5);
        vecIntA.push_back(3);
        vecIntA.push_back(9);
    
        // 把所有为 3 的元素替换为 8 
        replace(vecIntA.begin(), vecIntA.end(), 3, 8); // {1,8,5,8,9}
    
        for(auto x : vecIntA)
            cout << x << endl;
    }
    
  • replace_if ()

    replace_if(vecIntA.begin(),vecIntA.end(),GreaterThree,newVal)
    

    将指定范围内所有操作结果为 true 的元素用新值替换。


bool GreaterThree(int iNum) {
    return iNum >= 3 ? true : false;
}

void demo13()
{
    vector<int> vecIntA;
    vecIntA.push_back(1);
    vecIntA.push_back(3);
    vecIntA.push_back(5);
    vecIntA.push_back(3);
    vecIntA.push_back(9);

    // 把大于等于 3 的元素替换成 8
    replace_if(vecIntA.begin(), vecIntA.end(), GreaterThree, 8);

    // 1 8 8 8 8
    for(auto x : vecIntA)
        cout << x << endl;
}
  • swap ()
    交换两个容器的元素
    void demo14()
    {
        vector<int> vecIntA;
        vecIntA.push_back(1);
        vecIntA.push_back(3);
        vecIntA.push_back(5);
        
        vector<int> vecIntB;
        vecIntB.push_back(2);
        vecIntB.push_back(4);
    
        swap(vecIntA, vecIntB);  //交换
    }
    

常用的算术和生成算法

用到 #include <numeric>

  • accumulate ()
    对指定范围内的元素求和,然后结果再加上一个由 val 指定的初始值
    void demo15()
    {
        vector<int> vecIntA;
        vecIntA.push_back(1);
        vecIntA.push_back(3);
        vecIntA.push_back(5);
        vecIntA.push_back(7);
        vecIntA.push_back(9);
        int iSum = accumulate(vecIntA.begin(), vecIntA.end(), 100);     //iSum==125
        cout << iSum << endl;
    }
    
  • fill ()
    将输入值赋给标志范围内的所有元素
void demo16()
{
    vector<int> vecIntA;
    vecIntA.push_back(1);
    vecIntA.push_back(3);
    vecIntA.push_back(5);
    vecIntA.push_back(7);
    vecIntA.push_back(9);
    fill(vecIntA.begin(), vecIntA.end(), 8);        //8, 8, 8, 8, 8
}

常用的集合算法

  • set_union() 构造一个有序序列,包含两个有序序列的并集
  • set_intersection() 构造一个有序序列,包含两个有序序列的交集
  • set_difference() 构造一个有序序列,该序列保留第一个有序序列中存在而第二个有序序列中不存在的元素
void demo17()
{
    vector<int> vecIntA;
    vecIntA.push_back(1);
    vecIntA.push_back(3);
    vecIntA.push_back(5);
    vecIntA.push_back(7);
    vecIntA.push_back(9);

    vector<int> vecIntB;
    vecIntB.push_back(1);
    vecIntB.push_back(3);
    vecIntB.push_back(5);
    vecIntB.push_back(6);
    vecIntB.push_back(8);

    vector<int> vecIntC;
    vecIntC.resize(10);

    //并集
    //vecIntC : {1,3,5,6,7,8,9,0,0,0}
    set_union(vecIntA.begin(), vecIntA.end(), vecIntB.begin(), vecIntB.end(), vecIntC.begin());     

    //交集
    fill(vecIntC.begin(),vecIntC.end(),0);
    //vecIntC: {1,3,5,0,0,0,0,0,0,0}
    set_intersection(vecIntA.begin(), vecIntA.end(), vecIntB.begin(), vecIntB.end(), vecIntC.begin());      

    //差集
    fill(vecIntC.begin(),vecIntC.end(),0);
    //vecIntC: {7,9,0,0,0,0,0,0,0,0}
    set_difference(vecIntA.begin(), vecIntA.end(), vecIntB.begin(), vecIntB.end(), vecIntC.begin());
}           

常用的遍历算法

  • for_each ()
    用指定函数依次对指定范围内所有元素进行迭代访问。该函数不得修改序列中的元素

    void show(const int &iItem)
    {
        cout << iItem << " ";
    }
    
    void demo18()
    {
        int iArray[] = {0,1,2,3,4};
        vector<int> vecInt(iArray, iArray+sizeof(iArray)/sizeof(iArray[0]));
        for_each(vecInt.begin(), vecInt.end(), show);
        // 结果打印出0 1 2 3 4
    }
    
  • transform ()
    与 for_each 类似,遍历所有元素,但可对容器的元素进行修改

    int increase (int i)  
    {  
        return i+1;   
    }  
    
    void demo19()
    {
        vector<int> vecIntA;
        vecIntA.push_back(1);
        vecIntA.push_back(3);
        vecIntA.push_back(5);
        vecIntA.push_back(7);
        vecIntA.push_back(9);
    
    
        transform(vecIntA.begin(), vecIntA.end(), vecIntA.begin(), increase);       
        // vecIntA : {2,4,6,8,10}
    
        for(auto x : vecIntA)
            cout << x << endl;
    }
    

STL 综合案例

案例:学校演讲比赛

  • 某市举行一场演讲比赛,共有24个人参加,按参加顺序设置参赛号。比赛共三轮,前两轮为淘汰赛,第三轮为决赛。
  • 比赛方式:分组比赛
    • 第一轮分为4个小组,根据参赛号顺序依次划分,比如100-105为一组,106-111为第二组,依次类推,每组6个人,每人分别按参赛号顺序演讲。当小组演讲完后,淘汰组内排名最后的三个选手,然后继续下一个小组的比赛。
    • 第二轮分为2个小组,每组6人,每个人分别按参赛号顺序演讲。当小组完后,淘汰组内排名最后的三个选手,然后继续下一个小组的比赛。
    • 第三轮只剩下6个人,本轮为决赛,选出前三名。选手每次要随机分组,进行比赛。
  • 比赛评分:10个评委打分,去除最低、最高分,求平均分。每个选手演讲完由10个评委分别打分。该选手的最终得分是去掉一个最高分和一个最低分,求得剩下的8个成绩的平均分。选手的名次按得分降序排列,若得分一样,按参赛号升序排名。

用STL编程,求解一下问题

  1. 请打印出所有选手的名字与参赛号,并以参赛号的升序排列。
  2. 打印每一轮比赛前,分组情况
  3. 打印每一轮比赛后,小组晋级名单
  4. 打印决赛前三名,选手名称、成绩。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354