程序设计与算法(三) 第八周 标准模板库STL(一)

String 类

string类是一个模板类:

typedef basic_string< char > string

使用string类要包含头文件< string>

string 对象的初始化:

  • string s1(“hello”);
  • string month=”match”;
  • string s2(8,’x’);
  • 等。。。。
#include <iostream>
#include <string>
using namespace std;
int main(){
    string s1("hello");
    cout<<s1<<endl;
    string s2(8, 'x');
    cout<<s2<<endl;
    string match = "matrch";
    cout<<match<<endl;
    string s;
    s = 'n';
    cout<<s<<endl;
    return 0;
}
hello
xxxxxxxx
matrch
n

string支持getline函数

  • string s;
  • getline(cin,s)

string的赋值和连接

  • 用=赋值
  • string s1(“cat”), s2
  • s2=s1
  • 用assign成员函数赋值
  • string s1(“cat”), s2
  • s2.assign(s1);
  • 用assign成员函数部分复制
  • string s1(“catpig”), s3;
  • s3.assign(s1,1,3); //下标为1到3这3个元素赋值给s3
  • 单个字符赋值
  • 单个字符复制:s2[5]=s1[3]=’a’;
  • 逐个访问string中的字符string s1(“hello”) s1.at(i)
  • 用+运算符连接字符串
  • string s1(“good”), s2(“morning!”);
  • s1+=s2
  • 用成员函数append连接字符串
  • string s1(“good”), s2(“morning”)
  • s1.append(s2)
  • s1.append(s1, 3, s1.size()) //下标为3的位置开始,赋值s1.size()个字符,如果没用那么多字符,就赋值到最后一个字符
  • 用<,>,==比较字典序
  • 用成员函数compare比较string的大小
  • s1.compare(1,2,s3,0,3)是说,用s1的下标为1到2(包括2)的字符和s3的0到3的字符比较

子串

string s1(“hello world”), s2;
s2 = s1.substr(4,5); //从下标为4的位置,向后数5个字符进行赋值

交换两个string: s1.swap(s2)

寻找string中的字符

  • 成员函数 find()和rfind()
    • string s1(“hello world”)
    • s1.find(“lo”)
    • 在s1中从前向后查找,”lo”第一次出现的地方,如果找到,返回”lo”开始的下标(“l”的下标) 如果找不到,返回string::npos(string中定义的静态常量)
    • s1.find(“lo”,1)//从下标为1的地方开始找
  • find_first_of() 和find_last_of()(这个是最后一次出现的地方)
    • string s1(“hello world”);
    • s1.find_first_of(“abcd”);
    • 在s1中从前向后找”abcd”中任何一个字符第一次出现的地方,如果找到,返回找到字母的位置,找不到则返回string::npos。
  • 成员函数find_first_not_of() 和find_last_not_of()
    • string s1(“hello world”)
    • s1.find_first_not_of(“abcd”);
    • 在s1中从前向后查找不在,“abcd”中的字母第一次出现的地方,如果找到,返回找到字母的位置,如果找不到,返回string::npos。
  • 删除string中的字符
    • 成员函数erase()
    • string s1(“hello world”)
    • s1.erase(5) //删除5以及之后的字符
  • 替换string中的字串
    • 成员函数replace()
    • string s1(“hello world”)
    • s1.replace(2,3,”haha”) // 将s1中下标2开始的3个字符换成”haha”
    • s1.replace(2,3,”haha”,1,2) // 将s1中下标2开始的3个字符换成“haha”中下标1开始的2个字符
  • 在string中插入字符
    • 成员函数insert()
    • string s1(“hello world”)
    • string s2(“show insert”)
    • s1.insert(5, s2)
    • s1.insert(2, s2, 5, 3)
  • 转换成C语言式char *字符串
    • 成员函数c_str()
    • string s1(“hello world”)
    • printf(“%s\n”,s1.c_str()) // s1.c_str() 返回传统的const char*类型字符串,该字符串以 ‘\0’ 结尾
  • 转换成c语言式char*字符串
    • 成员函数data()
    • string s1(“hello world”)
    • const char *p1=s1.data()
    • for(int i=0;i

字符串流处理

除了标准流和文件流输入输出之外,还可以从string进行输入输出
类似istream和ostream进行标准流输入输出,我们用istringstream和ostringstream进行字符串上的输入输出,也称为内存输入输出:include< sstream>
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main(){
    string s1("hello world");
    string ss1, ss2;
    istringstream inputstring(s1);
    inputstring>>ss1>>ss2;
    cout<<ss1<<" "<<ss2;
    return 0;
}
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main(){
    string s1("hello world");
    string ss1, ss2;
    istringstream inputstring(s1);
    inputstring>>ss1>>ss2;
    ostringstream out;
    out<<"the "<<22<<" ok"<<endl;
    cout<<out.str();
    cout<<"there";
    return 0;
}
the 22 ok
there

标准模板库(STL)概述

泛型程序设计

c++语言的核心优势之一就是便于软件的重用
c++中有两个方面体现重用,
  • 面向对象的思想:继承和多态,标准类库
  • 泛型程序设计的思想:模板机制以及标准模板库STL

STL中的基本的概念

  • 容器:可容纳各种数据类型的通用数据结构,是类模板
  • 迭代器:可用于一次存取容器中元素,类似于指针
  • 算法:用来操作容器中哦的元素的函数模板
  • sort() 来对一个vector中的数据进行排序
  • find()来搜索一个list中的对象
  • 算法本身与他们操作的数据的类型无关,因此他们可以在从简单数组到高度复杂容器的任何数据结构上使用

容器概述:可以用于存放各种类型的数据(基本数据类型的变量,对象等)的数据结构,都是类模板,分为三种:

  • 顺序容器:vector, deque, list
  • 关联容器:set, multiset, map, multimap
  • 容器适配器:stack, queue, priority_queue

顺序容器简介

vector:动态数组,元素在内存连续存放。
deque:头文件< deuqe>双向队列。元素在内存连续存放。随机存取任何元素都能在常数时间内完成(但次于vector)。在两端增删元素具有较佳的性能(大部分情况下是常数时间)。
list:头文件< list>双向链表。元素在内存不连续存放。在任何位置增删元素都能在常数时间内完成。不支持随机存取。

关联容器简介

  • 元素是排序的
  • 插入任何元素,都按相应的排序规则来确定其位置
  • 在查找时具有
  • 通常以平衡二叉树方式实现,插入和检索时间都是O(log(N))
  • set/multiset 头文件< set> set即集合。set中不允许相同元素,multiset中允许存在相同元素。
  • map/multimap 头文件< map>
  • map和set的不同在于map中存放的元素有且仅有两个成员变量,一个名为first,另一个名为second,map根据first值对元素进行从小到大排序,并可快速地根据first来检索元素。map同multimap的不同在于是否允许相同first值的元素。

容器适配器简介

  • stack:头文件:< stack> 栈。是项的有限序列,并满足序列中被删除、检索和修改的项只能是最近插入序列的项(栈顶的项)。后进先出
  • queue:头文件< queue>队列。插入只可以在尾部进行,删除、检索和修改只允许从头部进行。先进先出。
  • priority_queue:头文件< queue> 优先级队列。最高优先级元素总是第一个出列

顺序容器和关联容器中都有哪些成员函数

  • begin: 返回指向容器中第一个元素的迭代器
  • end: 返回指向容器中最后一个元素后面位置的迭代器
  • rbegin:返回指向容器中最后一个元素的迭代器
  • rend: 返回指向容器中第一个元素前面的位置的迭代器
  • erase: 从容器中删除一个或几个元素
  • clear: 从容器中删除所有元素

顺序容器的常用的成员函数

  • front: 返回容器中第一个元素的引用
  • back: 返回容器中最后一个元素的引用
  • push_back: 在容器末尾增加新元素
  • pop_back: 删除容器末尾的元素
  • erase:删除迭代器指向的元素(可能会使该迭代器失效),或删除一个区间,返回被删除元素后面的那个元素的迭代器

标准模板库STL概述(二)

迭代器

  • 用于指向顺序容器和关联容器中的元素
  • 迭代器用法和指针类似
  • 有const和非const两种
  • 通过迭代器可以读取它指向的元素
  • 通过非const迭代器还能修改其指向的元素

定义迭代器的方法

  • 容器类名::iterator 变量名;
  • 容器类名::const_iterator 变量名;
  • 访问一个迭代器指向的元素:*迭代器变量名

迭代器上可以执行++操作,以使其指向容器中的下一个元素。如果到达了最后一个元素后面,就会出错。

#include <vector>
#include <iostream>
using namespace std;
int main()
{
    vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    vector<int>::const_iterator i;
    for (i = v.begin(); i != v.end(); ++i) {
        cout<<*i<<",";
    }
    cout<<endl;
    vector<int>::reverse_iterator r;//反向迭代器,这里r++会指向容器中的前一个元素
    for (r = v.rbegin(); r!=v.rend(); ++r) {
        cout<<*r<<",";
    }
    vector<int>::iterator j;  //非常量迭代器
    for (j = v.begin(); j != v.end() ; ++j) {
        *j=100;
    }
    cout<<endl;
    for(auto x:v){
        cout<<x<<",";
    }
}
1,2,3,4,
4,3,2,1,
100,100,100,100,

双向迭代器

  • ++p, p++, –p, p–
  • *p //返回那个值(的引用)
  • p=p1
  • p==p1, p!=p1

随机访问迭代器

  • 双向迭代器的所有操作
  • p+=i
  • p-=i
  • p+i, p-i, p[i],
  • p

容器和容器上迭代器的种类

  • vector 随机访问
  • deque 随机访问
  • list 双向
  • set/multiset 双向
  • map/multimap 双向
  • stack 不支持迭代器
  • queue 不支持迭代器
  • priority_queue 不支持迭代器
有的容器,例如sort和binary_search需要通过随机访问迭代器来访问容器中的元素,那么list以及关联容器就不支持改算法

vector 的迭代器是随机迭代器

list是双向迭代器,用法如下:

    list<int> vv;
    vv.push_back(1);
    vv.push_back(3);
    vv.push_back(5);
    list<int> ::const_iterator ii;
    //这里不能用ii<vv.end(),不能用vv[i]
    for (ii=vv.begin();ii!=vv.end();++ii){
        cout<<*ii<<",";
    }

算法简介

  • 算法就是一个个函数模板,大多是在< algorithm>中定义
  • STL中提供能在各种容器中通用的算法,比如查找、排序等
  • 算法通过迭代器来操纵容器中的元素,许多算法可以对容器中的一个局部区间进行操作,因此需要两个参数,一个是起始元素的迭代器,一个是终止元素的后面一个元素的迭代器
  • 有的算法返回一个迭代器。比如find()算法,在容器中查找一个元素,并返回一个指向该元素的迭代器
  • 算法可以处理容器,也可以处理普通数组
算法示例 find()
  • template

STL中“大”,“小”的概念

  • 关联容器内部的元素是从小到大排序的
  • 有些算法要求其操作的区间是从小到大排序的,称为“有序区间算法”。例如:binary_search
  • 有些算法会对区间进行从小到大排序,成为排序算法。例如:sort
  • 还有一些其他算法会用到“大”,“小”的概念
  • 使用STL时,在缺省的情况下,以下三个说法等价:x比y小,x
#include <iostream>
#include <algorithm>
using namespace std;
class A{
    int v;
public:
    A(int n):v(n){}
    bool operator < (const A &a2)const{
        cout<<v<<"<"<<a2.v<<"?"<<endl;
        return false;
    }
    bool operator == (const A &a2) const{
        cout<<v<<"=="<<a2.v<<"?"<<endl;
        return v==a2.v;
    }
};
int main()
{
    A a[]={A(1), A(2), A(3), A(4), A(5)};
    cout<<binary_search(a, a+4, A(9));//折半查找中,只是判断了是否小于。。返回1是找到了
    return 0;
}
3<9?
2<9?
1<9?
9<1?
1

vector 和deque

vector

#include <iostream>
#include <vector>
using namespace std;
template <class T>
void PrintVector(T s, T e){
    for(;s!=e;++s)
        cout<<*s<<" ";
    cout<<endl;
}
int main(){
    int a[5]={1,2,3,4,5};
    vector<int> v(a, a+5);
    cout<<"1)"<<v.end()-v.begin()<<endl;
    cout<<"2)";
    PrintVector(v.begin(), v.end());
    v.insert(v.begin()+2, 13);//插入。。第一个参数是位置,第二个位置是元素
    cout<<"3)";
    PrintVector(v.begin(), v.end());
    v.erase(v.begin()+2);//删除
    cout<<"4)";
    PrintVector(v.begin(), v.end());
    vector<int> v2(4, 100);//4个元素,每个元素都是100
    v2.insert(v2.begin(),v.begin()+1, v.begin()+3);//在v2里面插入某个区间
    cout<<"5) v2:";
    PrintVector(v2.begin(), v2.end());
    v.erase(v.begin()+1, v.begin()+3);
    cout<<"6) ";
    PrintVector(v.begin(), v.end());
    return 0;
}
1)5
2)1 2 3 4 5 
3)1 2 13 3 4 5 
4)1 2 3 4 5 
5) v2:2 3 100 100 100 100 
6) 1 4 5 

vector 二维数组

#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector< vector<int> > v(3);
    for (int i = 0; i < v.size(); ++i) {
        for (int j = 0; j < 4; ++j) {
            v[i].push_back(j);
        }
    }
    for (int i = 0; i < v.size(); ++i) {
        for (int j = 0; j < 4; ++j) {
            cout<<v[i][j]<<" ";
        }
        cout<<endl;
    }
    return 0;
}
0 1 2 3 
0 1 2 3 
0 1 2 3 

deque:适合vector的操作都适用于deque。

deque还要push_front(将元素插入到前面)和pop_front(删除最前面的元素)操作,复杂度是O(1)

双向链表:list

  • 在任何位置插入删除都是常数时间,不支持随机存取
  • 除了具有所有容器都有的成员函数以外,还支持8个成员函数:
  • push_front: 在前面插入
  • pop_front: 删除前面的元素
  • sort:排序(list不支持STL的算法sort)
  • remove: 删除和指定值相同的所有元素
  • unique: 删除所有和前一个元素相同的元素(要做到元素不重复,则unique之前还需要sort)
  • merge:合并两个链表,并清空被合并的那个
  • reverse:颠倒链表
  • splice: 在指定位置前面插入另一个链表中的一个或多个元素,并在另一链表中删除被插入的元素
#include <list>
#include <iostream>
#include <algorithm>
using namespace std;
class A{
private:
    int n;
public:
    A(int _n):n(_n){}
    friend bool operator<(const A &a1, const A &a2);
    friend bool operator==(const A &a1, const A &a2);
    friend ostream &operator<<(ostream &o, const A &a);
};
bool operator<(const A &a1, const A &a2) {
    return a1.n<a2.n;
}
bool operator==(const A &a1, const A &a2) {
    return a1.n==a2.n;
}
ostream& operator<<(ostream &o, const A &a) {
    o<<a.n;
    return o;
}
template <class T>
void PrintList(const list<T> & lst){
    //不推荐的写法,用两个迭代器更好
    typename list<T>::const_iterator i; //typename用来说明list<T>::const_iterator是个类型
    i = lst.begin();
    for (i = lst.begin(); i!=lst.end(); ++i) {
        cout<<*i<<",";
    }
}
int main()
{
    list<A> lst1, lst2;
    lst1.push_back(1);lst1.push_back(3);
    lst1.push_back(2);lst1.push_back(4);
    lst1.push_back(2);
    lst2.push_back(10);
    lst2.push_front(20);
    lst2.push_back(30);
    lst2.push_front(30);
    lst2.push_back(30);
    lst2.push_front(40);
    lst2.push_back(40);
    cout<<"1)";
    PrintList(lst1);cout<<endl;
    cout<<"2)";
    PrintList(lst2);cout<<endl;
    lst2.sort();
    cout<<"3)";
    PrintList(lst2);
    cout<<endl;
    lst2.pop_front();//删掉头部元素
    cout<<"4)";
    PrintList(lst2);
    cout<<endl;
    lst1.remove(2); //删除和A(2)相等的元素
    cout<<"5)";
    PrintList(lst1);
    cout<<endl;
    lst2.unique();
    cout<<"6)";
    PrintList(lst2);
    cout<<endl;
    lst1.merge(lst2); //合并lst2到lst1并清空lst2
    cout<<"7)";
    PrintList(lst1);
    cout<<endl;
    cout<<"8)";
    PrintList(lst2);
    cout<<endl;
    lst1.reverse();
    cout<<"9)";
    PrintList(lst1);
    cout<<endl;
    lst2.push_back(100);
    lst2.push_back(200);
    lst2.push_back(300);
    lst2.push_back(400);
    list<A>::iterator p1, p2, p3;
    p1 = find(lst1.begin(), lst1.end(), 3);
    p2 = find(lst2.begin(), lst2.end(), 200);
    p3 = find(lst2.begin(), lst2.end(), 400);
    lst1.splice(p1, lst2, p2, p3);//将lst2[p2,p3)插入到p1之前, 并从lst2中删除[p2, p3)
    cout<<"10)";
    PrintList(lst1);
    cout<<endl;
    cout<<"11)";
    PrintList(lst2);
    cout<<endl;
    return 0;
}
1)1,3,2,4,2,
2)40,30,20,10,30,30,40,
3)10,20,30,30,30,40,40,
4)20,30,30,30,40,40,
5)1,3,4,
6)20,30,40,
7)1,3,4,20,30,40,
8)
9)40,30,20,4,3,1,
10)40,30,20,4,200,300,3,1,
11)100,400,

函数对象:若一个类重载了运算符“()”,则该类的对象就成为函数对象

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <functional>
using namespace std;

int SumSquares(int total, int value)
{return total+value*value;}

template <class T>
void PrintInterval(T first, T last)
{
    for(;first!=last;++first)
    {
        cout<<*first<<" ";
    }
    cout<<endl;
}
template <class T>
class SumPowers
{
private:
    int power;
public:
    SumPowers(int p):power(p){}
    const T operator()(const T&total, const T&value){
        T v=value;
        for(int i=0;i<power-1;i++) v=v*value;
        return total+v;
    }
};

int main()
{
    const int SIZE = 10;
    int a1[]={1,2,3,4,5,6,7,8,9,10};
    vector<int> v(a1, a1+SIZE);
    cout<<"1)";
    PrintInterval(v.begin(), v.end());
    int result = accumulate(v.begin(), v.end(), 0, SumSquares);//SumSquares是个函数
    /*
     这里实例化了:
     int accumulate(vector<int>::iterator first, vector<int>::iterator last, int init, int(*op)(int, int))
     {
        for(;first!=last;++first)
            init=op(init, *first);
        return init;
     }
     */
    cout<<"2)平方和"<<result<<endl;
    //SumPowers是个类模板,SumPowers<int>模板类,SumPowers<int>(3)是个对象,这个对象重载了“()”
    result = accumulate(v.begin(), v.end(), 0, SumPowers<int>(3));
    /*
     int accumulate(vector<int>::iterator first, vector<int>::iterator last, int init, SumPowers<int> op)
     {
        for(;first!=last;++first)
            init=op(init, *first);
        return init;
     }
     */
    cout<<"3)立方和"<<result<<endl;
    result=accumulate(v.begin(), v.end(), 0, SumPowers<int>(4));
    cout<<"4)4次方和"<<result<<endl;
    return 0;
}
1)1 2 3 4 5 6 7 8 9 10 
2)平方和385
3)立方和3025
4)4次方和25333
//在c++源码中如下:

  template<typename _InputIterator, typename _Tp>
    inline _Tp
    accumulate(_InputIterator __first, _InputIterator __last, _Tp __init)
    {
      // concept requirements
      __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
      __glibcxx_requires_valid_range(__first, __last);

      for (; __first != __last; ++__first)
    __init = __init + *__first;
      return __init;
    }

  template<typename _InputIterator, typename _Tp, typename _BinaryOperation>
    inline _Tp
    accumulate(_InputIterator __first, _InputIterator __last, _Tp __init,
           _BinaryOperation __binary_op)
    {
      // concept requirements
      __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
      __glibcxx_requires_valid_range(__first, __last);

      for (; __first != __last; ++__first)
    __init = __binary_op(__init, *__first);
      return __init;
    }

STL中的函数对象类模板:头文件:< functional>

greater 函数对象类模板

//在c++中源码如下:

  template<typename _Tp>
    struct greater : public binary_function<_Tp, _Tp, bool>
    {
      _GLIBCXX14_CONSTEXPR
      bool
      operator()(const _Tp& __x, const _Tp& __y) const
      { return __x > __y; }
    };

greater的应用

list有两个sort成员函数
  • void sort(); 将list中的元素按”<”规定的比较方法升序排列
  • template< class Compare>
  • void sort(Compare op); 将list中的元素按op规定的比较方法升序排列。即要比较x, y大小时,看op(x, y)的返回值,为true则认为x小于y
#include <list>
#include <iostream>
using namespace std;
class MyLess{
public:
    bool operator()(const int &c1, const int &c2)
    {
        //比较各位数
        return (c1%10)<(c2%10);
    }
};
template <class T>
void Print(T first, T last){
    for(;first!=last;++first) cout<<*first<<",";
}
int main()
{
    const int SIZE=5;
    int a[SIZE]={5,21,14,2,3};
    list<int> lst(a, a+SIZE);
    lst.sort(MyLess());
    Print(lst.begin(), lst.end());
    cout<<endl;
    lst.sort(greater<int>());//greater<int>()是个对象
    Print(lst.begin(), lst.end());
    cout<<endl;
    return 0;
}
21,2,3,14,5,
21,14,5,3,2,

在STL中使用自定义的“大”,“小”关系

关联容器和STL中许多算法,都是可以用函数或函数对象自定义比较器的。在自定义了比较器op的情况下,以下三个说法是等价的:1.x小于y;2.op(x,y) 返回true;3.y大于x
#include <iostream>
#include <iterator>
using namespace std;

class MyLess{
public:
    bool operator()(int a1, int a2){
        if((a1%10)<(a2%10)) return true;
        else return false;
    }
};

bool MyCompare(int a1, int a2){
    if((a1%10)<(a2%10)) return false;
    else return true;
}

template <class T, class pred>
T MyMax(T first, T last, pred op){
    T temp = first;
    for(;first!=last;++first){
        if(op(*temp, *first)) 如果*temp小于*first
            temp = first;
    }
    return temp;
};

int main()
{
    int a[]={35, 7,13, 19, 12};
    cout<<*MyMax(a, a+5, MyLess())<<endl;
    cout<<*MyMax(a, a+5, MyCompare)<<endl;
    return 0;
}
19
12
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值