C++学习笔记六之STL

一、概念

二、STL六大组件

三、vector容器

1)概念

2)特性

3)构造函数

4)添加函数

5)删除函数

6)遍历方法

7)判断函数

8)获取大小函数

9)其他函数

10)代码实例

四、string类

1)构造函数和析构函数

2)   string对象的操作

3)string类函数

4)插入函数

5)提取子字符串

6)string到int的转换

7)字符串查找find函数

8)替换函数replace

9)删除函数erase

五、deque容器

1)概念

2)构造函数

3)赋值操作

4)获取大小

5)遍历

6)deque双端插入和删除操作

7)数据存取

8)插入

9)删除

10)排序

六、stack容器

1)概念

2)成员函数

3)代码实例

七、queue容器

1)概念

2)成员函数

3)遍历

八、list容器

 1)概念

 2)vector、deque与list的使用情况

3)成员函数

4)遍历

九、set容器

十、map容器

一、概念

STL(standard template library):标准模板库。从广义上分为容器、算法和迭代器。容器和算法之间通过迭代器进行无缝连接。

二、STL六大组件

容器、算法、迭代器、仿函数、适配器和控件配置器。

迭代器的种类:

三、vector容器

容器:vector

算法:for_each

迭代器:vector<int>::iterator

①概念

向量(Vector)是一个封装了动态大小数组的顺序容器(Sequence Container)。跟任意其它类型容器一样,它能够存放各种类型的对象。可以简单的认为,向量是一个能够存放任意类型的动态数组。

②特性

1.顺序序列 2.动态数组 3.能够感知内存分配器

③构造函数

    • vector():创建一个空vector
    • vector(int nSize):创建一个vector,元素个数为nSize
    • vector(int nSize,const t& t):创建一个vector,元素个数为nSize,且值均为t
    • vector(const vector&):复制构造函数
    • vector(begin,end):复制[begin,end)区间内另一个数组的元素到vector中

④添加函数

    • void push_back(const T& x):向量尾部增加一个元素X
    • iterator insert(iterator it,const T& x):向量中迭代器指向元素前增加一个元素x
    • iterator insert(iterator it,int n,const T& x):向量中迭代器指向元素前增加n个相同的元素x
    • iterator insert(iterator it,const_iterator first,const_iterator last):向量中迭代器指向元素前插入另一个相同类型向量的[first,last)间的数据

⑤删除函数

    • iterator erase(iterator it):删除向量中迭代器指向元素
    • iterator erase(iterator first,iterator last):删除向量中[first,last)中元素
    • void pop_back():删除向量中最后一个元素
    • void clear():清空向量中所有元素

⑥遍历函数

    • reference at(int pos):返回pos位置元素的引用
    • reference front():返回首元素的引用
    • reference back():返回尾元素的引用
    • iterator begin():返回向量头指针,指向第一个元素
    • iterator end():返回向量尾指针,指向向量最后一个元素的下一个位置
    • reverse_iterator rbegin():反向迭代器,指向最后一个元素
    • reverse_iterator rend():反向迭代器,指向第一个元素之前的位置

⑦判断函数

    • bool empty() const:判断向量是否为空,若为空,则向量中无元素

⑧大小函数

    • int size() const:返回向量中元素的个数
    • int capacity() const:返回当前向量所能容纳的最大元素值
    • int max_size() const:返回最大可允许的vector元素数量值

⑨其他函数

    • void swap(vector&):交换两个同类型向量的数据
    • void assign(int n,const T& x):设置向量中前n个元素的值为x
    • void assign(const_iterator first,const_iterator last):向量中[first,last)中元素设置成当前向量元素
    • reserve 预留空间

⑩代码实例

1.添加删除元素

#include <string.h>
#include <vector>
#include <iostream>
using namespace std;
 
int main()
{
    vector<int>obj;//创建一个向量存储容器 int
    for(int i=0;i<10;i++) // push_back(elem)在数组最后添加数据 
    {
        obj.push_back(i);
        cout<<obj[i]<<",";    
    }
 
    for(int i=0;i<5;i++)//去掉数组最后一个数据 
    {
        obj.pop_back();
    }
 
    cout<<"
"<<endl;
 
    for(int i=0;i<obj.size();i++)//size()容器中实际数据个数 
    {
        cout<<obj[i]<<",";
    }
 
    return 0;
}

2.排序

注意 sort 需要头文件 #include <algorithm>

#include <string.h>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
 
int main()
{
    vector<int>obj;
 
    obj.push_back(1);
    obj.push_back(3);
    obj.push_back(0);
 
    sort(obj.begin(),obj.end());//从小到大
 
    cout<<"从小到大:"<<endl;
    for(int i=0;i<obj.size();i++)
    {
        cout<<obj[i]<<",";  
    } 
 
    cout<<"
"<<endl;
 
    cout<<"从大到小:"<<endl;
    reverse(obj.begin(),obj.end());//从大到小 
    for(int i=0;i<obj.size();i++)
    {
        cout<<obj[i]<<",";
    }
    return 0;
}

如果想 sort 来降序,可重写 sort

bool compare(int a,int b) 
{ 
    return a< b; //升序排列,如果改为return a>b,则为降序 
} 
int a[20]={2,4,1,23,5,76,0,43,24,65},i; 
for(i=0;i<20;i++) 
    cout<< a[i]<< endl; 
sort(a,a+20,compare);

3.遍历数组

#include <string.h>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
 
int main()
{
    //顺序访问
    vector<int>obj;
    for(int i=0;i<10;i++)
    {
        obj.push_back(i);   
    } 
 
    cout<<"直接利用数组:"; 
    for(int i=0;i<10;i++)//方法一 
    {
        cout<<obj[i]<<" ";
    }
 
    cout<<endl; 
    cout<<"利用迭代器:" ;
    //方法二,使用迭代器将容器中数据输出 
    vector<int>::iterator it;//声明一个迭代器,来访问vector容器,作用:遍历或者指向vector容器的元素 
    for(it=obj.begin();it!=obj.end();it++)
    {
        cout<<*it<<" ";
    }
    return 0;
}

4.二维数组(容器嵌套)

法一、

#include <string.h>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
 
 
int main()
{
    int N=5, M=6; 
    vector<vector<int> > obj(N); //定义二维动态数组大小5行 
    for(int i =0; i< obj.size(); i++)//动态二维数组为5行6列,值全为0 
    { 
        obj[i].resize(M); 
    } 
 
    for(int i=0; i< obj.size(); i++)//输出二维动态数组 
    {
        for(int j=0;j<obj[i].size();j++)
        {
            cout<<obj[i][j]<<" ";
        }
        cout<<"
";
    }
    return 0;
}

法二、

#include <string.h>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
 
 
int main()
{
    int N=5, M=6; 
    vector<vector<int> > obj(N, vector<int>(M)); //定义二维动态数组5行6列 
 
    for(int i=0; i< obj.size(); i++)//输出二维动态数组 
    {
        for(int j=0;j<obj[i].size();j++)
        {
            cout<<obj[i][j]<<" ";
        }
        cout<<"
";
    }
    return 0;
}

 四、string类

1)构造函数和析构函数

1)  string s;  // 生成一个空字符串s 
2)  string s(str) ; // 拷贝构造函数生成str的复制品 
3)  string s(str, stridx);  // 将字符串str内"始于位置stridx"的部分当作字符串的初值 
4)  string s(str, stridx, strlen) ; // 将字符串str内"始于stridx且长度顶多strlen"的部分作为字符串的初值 
5)  string s(cstr) ;  // 将C字符串(以NULL结束)作为s的初值 
6)  string s(chars, chars_len) ;  // 将C字符串前chars_len个字符作为字符串s的初值。 
7)  string s(num, ‘c’) ;  // 生成一个字符串,包含num个c字符 
8)  string s(“value”);  string s=“value”;  // 将s初始化为一个字符串字面值副本
9)  string s(begin, end);  // 以区间begin/end(不包含end)内的字符作为字符串s的初值 
10) s.~string();  //销毁所有字符,释放内存 

2)string对象的操作

1)  s.empty();  // s为空串 返回true
2)  s.size();  // 返回s中字符个数 类型应为:string::size_type
3)  s[n];  // 从0开始相当于下标访问
4)  s1+s2;  // 把s1和s2连接成新串 返回新串 
5)  s1=s2;  // 把s1替换为s2的副本
6)  v1==v2;  // 比较,相等返回true
7)  `!=, <, <=, >, >=`  惯有操作 任何一个大写字母都小于任意的小写字母

3)string类函数

  1.  =, s.assign() // 赋以新值  
s.assign(str); // 不说 
s.assign(str,1,3); // 如果str是"iamangel" 就是把"ama"赋给字符串 
s.assign(str,2,string::npos); // 把字符串str从索引值2开始到结尾赋给s 
s.assign("gaint"); // 不说 
s.assign("nico",5); // 把’n’ ‘I’ ‘c’ ‘o’ ‘’赋给字符串 
s.assign(5,'x'); // 把五个x赋给字符串
  1.  swap() // 交换两个字符串的内容 
  2.  +=, s.append(), s.push_back() // 在尾部添加字符 
  3.  s.insert() // 插入字符 
  4.  s.erase() // 删除字符 
  5.  s.clear() // 删除全部字符 
  6.  s.replace() // 替换字符 
  7.  + // 串联字符串 
  8.  ==,!=,<,<=,>,>=,compare() // 比较字符串 
  9.  size(),length() // 返回字符数量 
  10.  max_size() // 返回字符的可能最大个数 
  11.  s.empty() // 判断字符串是否为空 
  12.  s.capacity() // 返回重新分配之前的字符容量 
  13.  reserve() // 保留一定量内存以容纳一定数量的字符 
  14.  [ ], at() // 存取单一字符 
  15.  >>,getline() // 从stream读取某值 
  16.  << // 将谋值写入stream 
  17.  copy() // 将某值赋值为一个C_string 
  18.  c_str() // 返回一个指向正规C字符串(C_string)的指针 内容与本string串相同 有’’ 
  19.  data() // 将内容以字符数组形式返回 无’’ 
  20.  s.substr() // 返回某个子字符串 
  21.  begin() end() // 提供类似STL的迭代器支持 
  22.  rbegin() rend() // 逆向迭代器 
  23.  get_allocator() // 返回配置器

4)插入函数

①插入字符串

s.insert(0,”my name”); 
s.insert(1,str); 

②插入单个字符

insert(size_type index, size_type num, chart c)和insert(iterator pos, size_type num, chart c)。  其中size_type是无符号整数,iterator是char*,所以,你这么调用insert函数是不行的:  insert(0, 1, ‘j’);这时候第一个参数将转换成哪一个呢?  所以你必须这么写:insert((string::size_type)0, 1, ‘j’)。

5)提取子字符串

s.substr(); // 返回s的全部内容 s.substr(11); // 从索引11往后的子串 s.substr(5,6); // 从索引5开始6个字符

6)string到int的转换

string result=”10000”;  
int n=0;
stream<<result;
stream>>n;  // n等于10000

7)字符串查找find函数

#include <string>
#include <iostream> 
using namespace std; int main()
{    
  // 待检索的字符串   
   string strOutput = "|0|1|2|";   
   // 需要检索的子串    
  string strObj = "|1|";     
  // 子串位于字符串中的位置    
  size_t nLoc = strOutput.find(strObj);   
   // 如果检索到子串在字符串中,则打印子串的位置   
   if (nLoc != string::npos)    
    {
        cout << "nLoc is: " << nLoc << endl;
    }     
  return 0;
}

8)替换函数replace

#include <iostream>
#include <string>
using namespace std;
int main ()
{
    string var ("abcdefghijklmn");
    const string dest ("1234");
    string dest2 ("567891234");
    var.replace (3,3, dest);
    cout << "1: " << var << endl;
    var = "abcdefghijklmn";
    var.replace (3,1, dest.c_str(), 1, 3);
    cout << "2: " << var << endl;
    var ="abcdefghijklmn";
    var.replace (3, 1, 5, 'x');
    cout << "3: " << var << endl;
    string::iterator itA, itB;
    string::iterator itC, itD;
    itA = var.begin();
    itB = var.end();
    var = "abcdefghijklmn";
    var.replace (itA, itB, dest);
    cout << "4: " << var << endl;
    itA = var.begin ();
    itB = var.end();
    itC = dest2.begin () +1;
    itD = dest2.end ();
    var = "abodefghijklmn";
    var.replace (itA, itB, itC, itD);
    cout << "5: " << var << endl;
    var = "abcdefghijklmn";
    var.replace (3, 1, dest.c_str(), 4); //这种方式会限定字符串替换的最大长度
    cout <<"6: " << var << endl;
    return 0;
}

9)删除函数erase

①删除字符串s从第pos个字符开始之后所有的字符(包括第pos个)

s.erase(pos)

②删除字符串s从第pos个字符开始的n个字符

s.erase(pos,n)

 

五、deque容器

1)概念

Vector 容器是单向开口的连续内存空间,deque 则是一种双向开口的连续线性空间。所谓的双向开口,意思是可以在头尾两端分别做元素的插入和删除操作,当然,vector 容器也可以在头尾两端插入元素,但是在其头部操作效率奇差,无法被接受。

2)构造函数

deque<T> deqT;//默认构造形式

deque(beg, end);//构造函数将[beg, end)区间中的元素拷贝给本身。

deque(n, elem);//构造函数将n个elem拷贝给本身。

deque(const deque &deq);//拷贝构造函数。

3)赋值操作

assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem);//将n个elem拷贝赋值给本身。
deque& operator=(const deque &deq); //重载等号操作符
swap(deq);// 将deq与本身的元素互换

4)获取大小

deque.size();//返回容器中元素的个数
deque.empty();//判断容器是否为空
deque.resize(num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
deque.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置,如果容器变短,则末尾超出容器长度的元素被删除。

5)遍历

void printDeque(const deque<int> &d)
{
    for(deque<int>::const_iterator it = d.begin(); it != d.end(); it++)
    {
        cout<<*it<<" ";
    }
    cout<<endl;
}

6)deque双端插入和删除操作

push_back(elem);//在容器尾部添加一个数据

push_front(elem);//在容器头部插入一个数据

pop_back();//删除容器最后一个数据

pop_front();//删除容器第一个数据

7)deque数据存取

at(idx);//返回索引idx所指的数据,如果idx越界,抛出out_of_range。

operator[];//返回索引idx所指的数据,如果idx越界,不抛出异常,直接出错。

front();//返回第一个数据。

back();//返回最后一个数据

8)插入操作

insert(pos,elem);//在pos位置插入一个elem元素的拷贝,返回新数据的位置。
insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值。
insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值。

9)删除操作

clear();//移除容器的所有数据

erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置。

erase(pos);//删除pos位置的数据,返回下一个数据的位置。

10)使用sort算法对deque进行排序

#include <iostream>
#include <deque>
#include <algorithm>
using namespace std;
 void printDeque(const deque<int> &d){
    for(deque<int>::const_iterator it = d.begin(); it != d.end(); it++)
    {
        cout<<*it<<" ";
    }
    cout<<endl;
}
 /* 回调函数 */
bool compare(int v1, int v2){
    return v1 > v2; 
/* 此时的排序是从大到小 */
/* 如果从小到大应该改为"v1 < v2" */
}
 void test(){
    deque<int> d;
    d.push_back(3);
    d.push_back(4);
    d.push_back(1);
    d.push_back(7);
    d.push_back(2);
     sort(d.begin(), d.end(), compare);
    printDeque(d);
}

六、stack容器

1)概念

C++ Stack(堆栈) 是一个容器类的改编,为程序员提供了堆栈的全部功能,——也就是说实现了一个先进后出(FILO)的数据结构。

2)成员函数

empty() 堆栈为空则返回真

pop() 移除栈顶元素

push() 在栈顶增加元素

size() 返回栈中元素数目

top() 返回栈顶元素

3)代码实例

#include <iostream>
#include <stack>  //使用stack需要包含此头文件
using namespace std;
int main()
{
    int n, k;
    stack <int> stk;
    cin >> n >> k;  //将n转换为k进制数
    if (n == 0) {
        cout << 0;
        return 0;
    }
    while (n) {
        stk.push(n%k);
        n /= k;
    }
    while (!stk.empty()) {
        cout << stk.top();
        stk.pop();
    }
    return 0;
}

七、queue容器

1)概念

只能访问 queue<T> 容器适配器的第一个和最后一个元素。只能在容器的末尾添加新元素,只能从头部移除元素。

2)成员函数

  • front():返回 queue 中第一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
  • back():返回 queue 中最后一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
  • push(const T& obj):在 queue 的尾部添加一个元素的副本。这是通过调用底层容器的成员函数 push_back() 来完成的。
  • push(T&& obj):以移动的方式在 queue 的尾部添加元素。这是通过调用底层容器的具有右值引用参数的成员函数 push_back() 来完成的。
  • pop():删除 queue 中的第一个元素。
  • size():返回 queue 中元素的个数。
  • empty():如果 queue 中没有元素的话,返回 true。
  • emplace():用传给 emplace() 的参数调用 T 的构造函数,在 queue 的尾部生成对象。
  • swap(queue<T> &other_q):将当前 queue 中的元素和参数 queue 中的元素交换。它们需要包含相同类型的元素。也可以调用全局函数模板 swap() 来完成同样的操作。

3)遍历

和 stack 一样,queue 也没有迭代器。访问元素的唯一方式是遍历容器内容,并移除访问过的每一个元素。例如:

std::deque<double> values {1.5, 2.5, 3.5, 4.5};

std::queue<double> numbers(values); while (!numbers, empty()) { std ::cout << numbers. front() << " "; // Output the 1st element numbers. pop(); // Delete the 1st element } std::cout << std::endl;

八、list容器

1)概念

list就是数据结构中的双向链表(根据sgi stl源代码),因此它的内存空间是不连续的,通过指针来进行数据的访问,这个特点使得它的随即存取变的非常没有效率,因此它没有提供[]操作符的重载。但由于链表的特点,它可以以很好的效率支持任意地方的删除和插入。

2)vector、deque与list的使用情况

在实际使用时,如何选择这三个容器中哪一个,应根据你的需要而定,具体可以遵循下面的原则
1. 如果你需要高效的随即存取,而不在乎插入和删除的效率,使用vector
2. 如果你需要大量的插入和删除,而不关心随即存取,则应使用list
3. 如果你需要随即存取,而且关心两端数据的插入和删除,则应使用deque。

3)成员函数

构造函数

list() 声明一个空列表;

list(n) 声明一个有n个元素的列表,每个元素都是由其默认构造函数T()构造出来的

list(n,val) 声明一个由n个元素的列表,每个元素都是由其复制构造函数T(val)得来的

list(n,val) 声明一个和上面一样的列表

list(first,last) 声明一个列表,其元素的初始值来源于由区间所指定的序列中的元素

assign() 给list赋值 

back() 返回最后一个元素 

begin() 返回指向第一个元素的迭代器 

clear() 删除所有元素 

empty() 如果list是空的则返回true 

end() 返回末尾的迭代器 

erase() 删除一个元素 

front() 返回第一个元素 

get_allocator() 返回list的配置器 

insert() 插入一个元素到list中 

max_size() 返回list能容纳的最大元素数量 

merge() 合并两个list 

pop_back() 删除最后一个元素 

pop_front() 删除第一个元素 

push_back() 在list的末尾添加一个元素 

push_front() 在list的头部添加一个元素 

rbegin() 返回指向第一个元素的逆向迭代器 

remove() 从list删除元素 

remove_if() 按指定条件删除元素 

rend() 指向list末尾的逆向迭代器 

resize() 改变list的大小 

reverse() 把list的元素倒转 

size() 返回list中的元素个数 

sort() 给list排序 

splice() 合并两个list 

swap() 交换两个list 

unique() 删除list中重复的元素

4)遍历

    LISTINT listOne;
     //声明i为迭代器
     LISTINT::iterator i;
     //从前面向listOne容器中添加数据
     listOne.push_front (2);
     listOne.push_front (1);
     //从后面向listOne容器中添加数据
     listOne.push_back (3);
     listOne.push_back (4);
     //从前向后显示listOne中的数据
     cout<<"listOne.begin()--- listOne.end():"<<endl;
     for (i = listOne.begin(); i != listOne.end(); ++i)
         cout << *i << " ";
cout << endl; //从后向后显示listOne中的数据 LISTINT::reverse_iterator ir; cout<<"listOne.rbegin()---listOne.rend():"<<endl; for (ir =listOne.rbegin(); ir!=listOne.rend();ir++) { cout << *ir << " "; }

 九、set容器

1)概念

set是STL中一种标准关联容器。它底层使用平衡的搜索树——红黑树实现,插入删除操作时仅仅需要指针操作节点即可完成,不涉及到内存移动和拷贝,所以效率比较高。set,顾名思义是“集合”的意思,在set中元素都是唯一的,而且默认情况下会对元素自动进行升序排列,支持集合的交(set_intersection),差(set_difference) 并(set_union),对称差(set_symmetric_difference) 等一些集合上的操作,如果需要集合中的元素允许重复那么可以使用multiset。

2)成员函数

  1. s.begin()      返回set容器的第一个元素
  2. s.end()      返回set容器的最后一个元素
  3. s.clear()       删除set容器中的所有的元素
  4. s.empty()     判断set容器是否为空
  5. s.insert()      插入一个元素
  6. s.erase()       删除一个元素
  7. s.size()     返回当前set容器中的元素个数
  8. s.find()       查找一个元素,如果容器中不存在该元素,返回值等于s.end()
  9. s.swap()     交换容器
  10. s.count()     统计元素个数

3)代码实例

1、插入

#include <iostream>
#include <set>
using namespace std;
set<int >s;
void setprint(int cnt){
    cout << "Test output :" << cnt << ":" << endl;
    for(set<int>::iterator it = s.begin(); it!= s.end(); it++)
        cout << *it << " ";
    puts("");
    return ;
}
int main(){
    int cnt = 1;
    s.insert(1);
    s.insert(2);
    s.insert(5);
    setprint(cnt++);

    s.insert(2); //set只允许用一个值出现一次,要插入相同元素请用multiset
    setprint(cnt++);

    int a[4] = {11,12,13,14};
    s.insert(a,a+4); //将区间[a, a+4]里的元素插入容器
    setprint(cnt++);

    return 0;
}

2、删除

#include <iostream>
#include <set>
using namespace std;
set<int >s;
void setprint(int cnt){
    cout << "Test output :" << cnt << ":" << endl;
    for(set<int>::iterator it = s.begin(); it!= s.end(); it++)
        cout << *it << " ";
    puts("");
    return ;
}

int main(){
    int cnt = 1;
    for(int i = 1; i < 11; i++){
        s.insert(i);
    }
    setprint(cnt++);

    s.erase(9); //根据元素删除
    setprint(cnt++);

    set<int>::iterator ita = s.begin();
    set<int>::iterator itb = s.begin();
    s.erase(ita);  //删除迭代器指向位置的元素
    setprint(cnt++);

    ita = s.begin();
    itb = s.begin();
    itb++;itb++;
    s.erase(ita,itb); //删除区间[ita,itb)的元素
    setprint(cnt);
    s.clear();
    return 0;
}

3、修改

不能直接修改容器内数据,所以只能删除某元素再插入要修改的数值。

4、查找

s.find()        查找一个元素,如果容器中不存在该元素,返回值等于s.end()

#include <iostream>
#include <set>
using namespace std;
set<int >s;
void setprint(int cnt){
    cout << "Test output :" << cnt << ":" << endl;
    for(set<int>::iterator it = s.begin(); it!= s.end(); it++)
        cout << *it << " ";
    puts("");
    return ;
}
int main(){
    int cnt = 1;
    s.insert(1);
    s.insert(2);
    s.insert(5);
    setprint(cnt++);

    if(s.find(2) != s.end()) 
        cout << "2 is existent" << endl;
    else 
        cout << "2 is non-existent" << endl;
    if(s.find(3) == s.end()) 
        cout << "3 is non-existent" << endl;
    else 
        cout << "2 is existent" << endl;
    return 0;
}

5、自定义比较函数

#include <iostream>
#include <set>
#include <functional>
using namespace std;

struct cmp{
    bool operator () (const int &a, const int &b){
        return a > b;
    }
};
set<int, cmp>s; //自定义排序函数构造set
void setprint(int cnt){
    cout << "Test output :" << cnt << ":" << endl;
    for(set<int,cmp>::iterator it = s.begin(); it!= s.end(); it++)
        cout << *it << " ";
    puts("");
    return ;
}
int main(){
    s.insert(1);
    s.insert(2);
    s.insert(6);
    setprint(1);
    return 0;
}

十、map容器

1)概念

Map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据处理能力,由于这个特性,它完成有可能在我们处理一对一数据的时候,在编程上提供快速通道。这里说下map内部数据的组织,map内部自建一颗红黑树(一种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,所以在map内部所有的数据都是有序的,后边我们会见识到有序的好处。

下面举例说明什么是一对一的数据映射。比如一个班级中,每个学生的学号跟他的姓名就存在着一一映射的关系,这个模型用map可能轻易描述,很明显学号用int描述,姓名用字符串描述(本篇文章中不用char *来描述字符串,而是采用STL中string来描述),下面给出map描述代码:

Map<int, string> mapStudent;

2)成员函数

begin() 返回指向容器中第一个(注意,是已排好序的第一个)键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
end() 返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
rbegin() 返回指向最后一个(注意,是已排好序的最后一个)元素的反向双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。
rend() 返回指向第一个(注意,是已排好序的第一个)元素所在位置前一个位置的反向双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。
cbegin() 和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。
cend() 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。
crbegin() 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。
crend() 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。
find(key) 在 map 容器中查找键为 key 的键值对,如果成功找到,则返回指向该键值对的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
lower_bound(key) 返回一个指向当前 map 容器中第一个大于或等于 key 的键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
upper_bound(key) 返回一个指向当前 map 容器中第一个大于 key 的键值对的迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
equal_range(key) 该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的键为 key 的键值对(map 容器键值对唯一,因此该范围最多包含一个键值对)。
empty()  若容器为空,则返回 true;否则 false。
size() 返回当前 map 容器中存有键值对的个数。
max_size() 返回 map 容器所能容纳键值对的最大个数,不同的操作系统,其返回值亦不相同。
operator[] map容器重载了 [] 运算符,只要知道 map 容器中某个键值对的键的值,就可以向获取数组中元素那样,通过键直接获取对应的值。
at(key) 找到 map 容器中 key 键对应的值,如果找不到,该函数会引发 out_of_range 异常。
insert() 向 map 容器中插入键值对。
erase() 删除 map 容器指定位置、指定键(key)值或者指定区域内的键值对。后续章节还会对该方法做重点讲解。
swap() 交换 2 个 map 容器中存储的键值对,这意味着,操作的 2 个键值对的类型必须相同。
clear() 清空 map 容器中所有的键值对,即使 map 容器的 size() 为 0。
emplace() 在当前 map 容器中的指定位置处构造新键值对。其效果和插入键值对一样,但效率更高。
emplace_hint() 在本质上和 emplace() 在 map 容器中构造新键值对的方式是一样的,不同之处在于,使用者必须为该方法提供一个指示键值对生成位置的迭代器,并作为该方法的第一个参数。
count(key) 在当前 map 容器中,查找键为 key 的键值对的个数并返回。注意,由于 map 容器中各键值对的键的值是唯一的,因此该函数的返回值最大为 1。

3)代码实例

1、插入

map mymap;  
string str;  
pair::iterator, bool> inspos;  
  
//insert函数返回一个pair对象,该对象包含一个迭代器和一个bool值  
//如果bool为true则说明正确插入,否则说明插入的值含有重复的键值,此时不做任何操作  
//返回的迭代器都为指定元素  
inspos = mymap.insert(map::value_type(str, 1));  
  
if (pairIns.second == false)  
{  
    ((*(pairIns.first)).first).append("OK");  
}  
  
//另外一种insert方法,这种方法不作是否有重复键值的判断,直接插入。  
mymap.insert(pair(str, 1));  

用数组方式插入数据

   map<int, string> mapStudent;
    mapStudent[1] = "student_one";
    mapStudent[2] = "student_two";
    mapStudent[3] = "student_three";
    map<int, string>::iterator iter;
    for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
        cout<<iter->first<<' '<<iter->second<<endl;

2、删除

移除某个map中某个条目用erase()

该成员方法的定义如下:

iterator erase(iterator it);//通过一个条目对象删除

iterator erase(iterator first,iterator last)//删除一个范围

size_type erase(const Key&key);//通过关键字删除

clear()就相当于enumMap.erase(enumMap.begin(),enumMap.end());

       map<int, string> mapStudent;
       mapStudent.insert(pair<int, string>(1, "student_one"));
       mapStudent.insert(pair<int, string>(2, "student_two"));
       mapStudent.insert(pair<int, string>(3, "student_three"));
        //如果你要演示输出效果,请选择以下的一种,你看到的效果会比较好
       //如果要删除1,用迭代器删除
       map<int, string>::iterator iter;
       iter = mapStudent.find(1);
       mapStudent.erase(iter);
       //如果要删除1,用关键字删除
       int n = mapStudent.erase(1);//如果删除了会返回1,否则返回0
       //用迭代器,成片的删除
       //一下代码把整个map清空
       mapStudent.erase( mapStudent.begin(), mapStudent.end() );

3、遍历

map<int, string>::iterator iter;
for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
  cout<<iter->first<<' '<<iter->second<<endl;

4、查找元素

第一种:用count函数来判定关键字是否出现,其缺点是无法定位数据出现位置,由于map的特性,一对一的映射关系,就决定了count函数的返回值只有两个,要么是0,要么是1,出现的情况,当然是返回1了。

第二种:用find函数来定位数据出现位置,它返回的一个迭代器,当数据出现时,它返回数据所在位置的迭代器,如果map中没有要查找的数据,它返回的迭代器等于end函数返回的迭代器。

查找map中是否包含某个关键字条目用find()方法,传入的参数是要查找的key,在这里需要提到的是begin()和end()两个成员,分别代表map对象中第一个条目和最后一个条目,这两个数据的类型是iterator。

  map<int, string> mapStudent;
    mapStudent.insert(pair<int, string>(1, "student_one"));
    mapStudent.insert(pair<int, string>(2, "student_two"));
    mapStudent.insert(pair<int, string>(3, "student_three"));
    map<int, string>::iterator iter;
    iter = mapStudent.find(1);
    if(iter != mapStudent.end())
       cout<<"Find, the value is "<<iter->second<<endl;
    else
       cout<<"Do not Find"<<endl;
    return 0;

5、排序

map中的元素是自动按Key升序排序,所以不能对map用sort函数;

这里要讲的是一点比较高深的用法了,排序问题,STL中默认是采用小于号来排序的,以上代码在排序上是不存在任何问题的,因为上面的关键字是int 型,它本身支持小于号运算,在一些特殊情况,比如关键字是一个结构体,涉及到排序就会出现问题,因为它没有小于号操作,insert等函数在编译的时候过 不去,下面给出2个方法解决这个问题。

法一:

#include <iostream>
#include <string>
#include <map>
using namespace std;
typedef struct tagStudentinfo
{
       int      niD;
       string   strName;
       bool operator < (tagStudentinfo const& _A) const
       {     //这个函数指定排序策略,按niD排序,如果niD相等的话,按strName排序
            if(niD < _A.niD) return true;
            if(niD == _A.niD)
                return strName.compare(_A.strName) < 0;
        return false;
       }
}Studentinfo, *PStudentinfo; //学生信息
int main()
{
    int nSize;   //用学生信息映射分数
    map<Studentinfo, int>mapStudent;
    map<Studentinfo, int>::iterator iter;
    Studentinfo studentinfo;
    studentinfo.niD = 1;
    studentinfo.strName = "student_one";
    mapStudent.insert(pair<Studentinfo, int>(studentinfo, 90));
    studentinfo.niD = 2;
    studentinfo.strName = "student_two";
    mapStudent.insert(pair<Studentinfo, int>(studentinfo, 80));
    for (iter=mapStudent.begin(); iter!=mapStudent.end(); iter++)
  cout<<iter->first.niD<<' '<<iter->first.strName<<' '<<iter->second<<endl; return 0; }

法二:

//第二种:仿函数的应用,这个时候结构体中没有直接的小于号重载,程序说明
#include <iostream>
#include <map>
#include <string>
using namespace std;
typedef struct tagStudentinfo
{
       int      niD;
       string   strName;
}Studentinfo, *PStudentinfo; //学生信息
class cmp
{
public:
    bool operator() (Studentinfo const &_A, Studentinfo const &_B) const
    {
        if(_A.niD < _B.niD)
            return true;
        if(_A.niD == _B.niD)
            return _A.strName.compare(_B.strName) < 0;
    return false;
    }
};
int main()
{   //用学生信息映射分数
    map<Studentinfo, int, cmp>mapStudent;
    map<Studentinfo, int>::iterator iter;
    Studentinfo studentinfo;
    studentinfo.niD = 1;
    studentinfo.strName = "student_one";
    mapStudent.insert(pair<Studentinfo, int>(studentinfo, 90));
    studentinfo.niD = 2;
    studentinfo.strName = "student_two";
    mapStudent.insert(pair<Studentinfo, int>(studentinfo, 80));
    for (iter=mapStudent.begin(); iter!=mapStudent.end(); iter++)
        cout<<iter->first.niD<<' '<<iter->first.strName<<' '<<iter->second<<endl;
}
原文地址:https://www.cnblogs.com/mango1997/p/14557370.html