zoukankan      html  css  js  c++  java
  • C++_标准模板库STL概念介绍4-算法

    STL包含很多处理容器的非成员函数:

    sort()

    copy()

    find()

    random_shuffle()

    set_union()

    set_intersection()

    set_difference()

    transform()

    它们的总体设计是相同的,都使用迭代器来标识要处理的数据区间和结果的放置位置。

    有些函数还接受一个函数对象参数,并使用它来处理数据。

    对于算法函数设计来说、有两个主要的通用部分:

    1)都使用模板来提供泛型;

    2)都使用迭代器来提供访问容器中数据的通用表示;

    因为指针是一种特殊的迭代器,因此诸如copy()等STL函数可用于常规数组。

    统一的容器设计使得不同类型的容器之间具有明显关系。

    ====================================================

    一、算法组:

    STL将算法库分成4组:

    1)非修改式序列操作

    2)修改式序列操作

    3)排序和相关操作

    4) 通用数字运算

    非修改式序列操作对区间中的每个元素进行操作。这些操作不修改容器的内容。

    修改式序列操作也对区间中的每个元素进行操作。它们可以修改容器的内容。可以修改值,也可以修改值的排列顺序。

    排序和相关操作包括多个排序函数(包括sort())和其他各种函数,包括集合操作。

    数字操作包括将区间的内容累积、计算两个容器的内部乘积、计算小计、计算相邻对象差的函数。

    ====================================================

    二、算法的通用特征:

    STL函数使用迭代器和迭代器区间。从函数原型可知有关迭代器的假设。

    例如:copy()函数的原型如下

    template<class InputIterator,  class OutputIterator>

    OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result); 

    对算法进行分类的方式之一,就是按结果放置的位置进行分类。

    有些算法就地完成工作,有些则创建拷贝。

    在sort()函数完成时,结果被存放在原始数据的位置上,因此sort()是就地算法

    copy()函数将结果发送到另一个位置,所以它是复制算法

    transform()函数可以以这两种方式完成工作。与copy()相似,它使用输出迭代器指示结果的存储位置;

    与copy()不同的是,transform()允许输出迭代器指向输入区间,因此它可以用计算结果覆盖原来的值。

    有些算法有两个版本:就地版本复制版本。STL的约定是,复制版本的名称将以_copy结尾

    例如,函数replace()的原型如下:

    template<class ForwardIterator, class T>

    void replace(ForwardIterator first, ForwardIterator last, const T& old_value, const T& new_value);

    它将所有的old_value替换为new_value,这是就地发生的。

    复制版本的原型如下:

    template<class InputIterator, class OutputIterator, class T>

    OutputIterator replace_copy(InputIterator first, InputIterator last, OutputIterator result, const T& old_value, const T& new_value);

    结果被复制到result指定的新位置。

    注意replace_copy返回类型为OutputIterator,对于复制算法而言,统一的约定是,返回一个迭代器,该迭代器指向复制的最后一个值后面的一个位置。

    还有一种常见的变体:

    以_if结尾的;根据将函数应用于容器元素得到的结果来执行操作。//这段话很绕,不是很理解

    原型如下:

    template<class ForwardIterator, class Predicate, class T>

    void replace_if(ForwardIterator first, ForwardIterator last, Predicate pred, const T& new_value);

    Predicate是模板参数名称,可以为T或者U。然而,STL选择用Predicate来提醒用户,实参应模拟Predicate概念。

    同样,STL使用诸如Generator和BinaryPredicate等术语来指示必须模拟其他函数对象概念的参数。

    虽然STL文档可以指定迭代器或函数符的需求,但编译器不会对此进行检查。

    如果您使用了错误的迭代器,则编译器试图实例化模板时,将出现大量错误信息。

    ====================================================

    三、STL和string类:

    sting类虽然不是STL的组成部分,但设计它时考虑到了STL。

    例如,它包含begin()、end()、rbegin()和rend()等成员,因此可以使用STL接口。

    下面的例子是用STL显示了一个词的字母可以得到的所有排列组合。

     1 // strgst1.cpp -- applying the STL to a string
     2 #include <iostream>
     3 #include <string>
     4 #include <algorithm>
     5 
     6 int main()
     7 {
     8     using namespace std;
     9     string letters;
    10     cout<<"Enter the letter grouping (quit to quit): ";
    11     while(cin>>letters && letters != "quit")
    12     {
    13         cout<<"Permutations of "<<letters<<endl;
    14         sort(letters.begin(), letters.end());
    15         cout<<letters<<endl;
    16         while (next_permutation(letter.begin(), letter.end()))
    17             cout<<letter<<endl;
    18         cout<<"Enter next sequence (quit to quit): ";    
    19     }
    20     cout<<"Done.
    ";
    21     return 0;
    22 }

    ====================================================

    四、函数和容器方法:

    有时可以选择使用STL方法或者STL函数。通常方法是更好的选择

    因为,它更适合特定的容器,其次容器方法作为成员函数,它可以使用模板类的内存管理工具,从而在需要的时候调整容器的长度。

    举个例子,假设有一个由数字组成的链表,并要删除链表中某个特定值(例如4)的所有实例。

    如果la是一个list<int>对象,则可以使用链表的remove()方法:

    la.remove(4);  //remove all 4s from the list;

    调用该方法后,链表中所有值为4的元素都将被删除,同时链表的长度将被自动调整。

    还有一个名为remove()的STL算法,它不是由对象调用,而是接受区间参数。因此如果lb是一个list<int>对象,则调用该函数的代码如下:

    remove(lb.begin(), lb.end(), 4);

    然而,由于该remove()函数不是成员,因此不能调整链表的长度。它将没被删除的元素放在链表的开始位置,并返回一个指向新的超尾值得迭代器。

    这样,便可以用迭代器来修改容器的长度。

    例如,可以使用链表的erase()方法来删除一个区间,该区间描述了链表中不再需要的部分。

    下面是一个例子,演示了如何进行:

     1 //listrmv.cpp  -- applying the STL to a string
     2 #inclue <iostream>
     3 #inclue <list>
     4 #inclue <algorithm>
     5 
     6 void Show(int);
     7 const int LIM = 10;
     8 
     9 int main()
    10 {
    11     using namespace std;
    12     int ar[LIM] = {4,5,4,2,2,3,4,8,1,4};
    13     list<int> la(ar, ar+LIM);
    14     list<int> lb(la);
    15     cout<<"Original list contents:
    	";
    16     for_each(la.begin(), la.end(), Show);
    17     
    18     cout<<endl;
    19     la.remove(4);
    20     cout<<"After using the remove() method:
    ";
    21     cout<<"la:	";
    22     for_each(la.begin(), la.end(), Show);
    23     cout<<endl;
    24     
    25     list<int>::iterator last;
    26     last = remove(la.begin(), lb.end(), 4);
    27     cout<<"After using the remove() function:
    ";
    28     cout<<"lb:	";
    29     for_each(lb.begin(), lb.end(), Show);
    30     cout<<endl;
    31     
    32     lb.erase(last, lb.end());
    33     cout<<"After using the erase() method:
    ";
    34     cout<<"lb:	";
    35     for_each(lb.begin(), lb.end(), Show);
    36     cout<<endl;
    37     return 0;
    38 }
    39 
    40 void Show(int v)
    41 {
    42     std::cout<<v<<' ';
    43 }

    程序输出:

    Original list contents:
    4 5 4 2 2 3 4 8 1 4
    After using the remove() method:
    la: 5 2 2 3 8 1

    After using the remove() function:
    lb: 5 2 2 3 8 1 4 8 1 4

    After using the erase() method:
    lb: 5 2 2 3 8 1

    ====================================================

    五、使用STL:

    STL是一个库,其组成部分被设计成协同工作。

    STL组件是工具,但也是创建其他工具的部件。

    接下来用一个例子来练习:

    假设要编写一个程序,让用户输入单词。希望最后得到一个按输入顺序排列的单词列表、

    一个按字母顺序排列的单词列表(忽略大小写),并记录每个单词被输入的次数。

    出于简化的目的,假设输入的过程中不添加数字和标点符号。

    输入和保存单词列表很简单,创建一个vector<string>对象,并用push_back()将输入的单词添加到矢量中

    vector<string> words;

    string input;

    while(cin>>input && input != 'quit')

      words.push_back(input);

     

    接下来如何将得到的单词列表按字母顺序排列

    为了避免覆盖原始数据,不能够使用就地算法。

    可以创建一个set<string>对象,然后将矢量中的单词复制到集合中(需要使用迭代器)。

    set<string> wordset;

    transform(words.begin(), words.end(), insert_iterator<set<string>>(wordset, wordset.begin()), ToLower)

    至于ToLower函数的编写如下:

    string & ToLower(string & st)

    {

      transform(st.begin(), st.end(), st.begin(), tolower);

      return st ;

    }

    有些情况下,tolower()函数被定义为int tolower(int),而我们希望函数与元素类型相匹配。

    char toLower(char ch) {return tolower(ch);}

    要获得每个单词在输入中出现的次数,可以使用count()函数。

    它将一个区间和一个值作为参数,并返回这个值在区间中出现的次数。

    map<string, int> wordmap;

    set<string>::iterator si;

    for(si = wordset.begin(), si != wordset.end(), si++)

      wordmap.insert(pair<string, int>(*si, count(words.begin(), words.end(), *si)));

    map类有个有趣的特征,可以用数组表示法(将键作为索引)来访问存储的值。

    例如,wordmap["the"]表示与键“the”相关联的值,这里是字符串“the”出现的次数。

    因为wordset容器保存了wordmap使用的全部键,所以可以用下面的代码来存储结果:

    for(si = wordset.begin(), si != wordset.end(), si++)

      wordmap[*si] = count(words.begin(), words.end(), *si);

    同样也可以使用数组表示法来报告结果:

    for(si = wordset.begin(), si !=wordset.end(), si++)

      cout<<*si<<": "<<wordmap[*si]<<endl;

     1 //usealgo.cpp -- using several STL elements
     2 #include <iostream>
     3 #include <string>
     4 #include <vector>
     5 #include <set>
     6 #include <map>
     7 #include <iteraotor>
     8 #include <algorithm>
     9 #include <cctype>
    10 
    11 using namespace std;
    12 
    13 char toLower(cahr ch) {return tolower(ch);}
    14 string & ToLower(string & st);
    15 void display(const string & s);
    16 
    17 int main()
    18 {
    19     vector<string> words;
    20     cout<<"Enter words(enter quit to quit):
    ";
    21     string input;
    22     while(cin>>input && input != "quit")
    23         words.push_back(input);
    24 
    25     cout<<"You entered the following words:
    ";
    26     for_each(words.begin(), words.end(), display);
    27     cout<<endl;
    28 
    29     //place words in set, converting to lowecase
    30     set<string> wordset;
    31     transform(words.begin(),words.end(), insert_iterator<set<string>>(wordset, wordset.begin()));
    32     cout<<"
    Alphabetic list of words:
    ";
    33     for_each(wordset.begin(), wordset.end(), display);
    34     cout<<endl;
    35 
    36     //place word and frequency in map
    37     map<string, int> wordmap;
    38     set<string> :: iterator si;
    39     for(si = wordset.begin(); si != wordset.end(); si++)
    40         wordmap[*si] = count(words.begin(), words.end(), *si);
    41     //display map contents
    42     cout<<"
    Word frequency:
    ";
    43     for(si = wordset.begin(); si != wordset.end(); si++)
    44         cout<<*si<<": "<<wordmap[*si]<<endl;
    45     return 0;    
    46 }
    47 
    48 string & ToLower(string & st)
    49 {
    50     transform(st.begin(), st.end(), st.begin(), toLower);
    51     return st;
    52 }
    53 
    54 woid display(const string & s)
    55 {
    56     cout<<s<<" ";
    57 }

    STL使用时应尽可能减少要编写的代码。

    STL通用、灵活的设计将节省大量的工作。

    另外,STL的设计者都是非常关心效率的算法人员,

    算法是经过仔细选择的,并且是内联的。

  • 相关阅读:
    关于素数的具体问题
    Scala Apply
    Scala内部类
    Scala 类和对象
    Scala Tuple类型
    Scala数组
    sql server 游标
    表变量和临时表详解
    子查询详解
    EXEC 和 SP_EXECUTESQL的区别
  • 原文地址:https://www.cnblogs.com/grooovvve/p/10491685.html
Copyright © 2011-2022 走看看