C++ STL

C++ STL中最基本以及最常用的类或容器无非就是以下几个:

string
vector
set
list
map
下面就依次介绍它们,并给出一些最常见的最实用的使用方法,做到快速入门。

string

首先看看我们C语言一般怎么使用字符串的

char* s1 = "Hello SYSU!"; //创建指针指向字符串常量,这段字符串我们是不能修改的

//想要创建 可以修改的字符串,我们可以使用数组分配空间
char s2[20] = "Hello SYSU!";
//或者这样
char s3[] = "Hello SYSU!";

//当然我们也可以动态分配内存
char* s4 = (char*)malloc(20);
gets(s4);

C++ 标准库中的string表示可变长的字符串,它在头文件#include <string>里面。
用string初始化字符串分两类:用“=”号就是拷贝初始化,否则就是直接初始化。
这里写图片描述

string s1;//初始化字符串,空字符串
string s2 = s1; //拷贝初始化,深拷贝字符串
string s3 = "I am Yasuo"; //直接初始化,s3存了字符串
string s4(10, 'a'); //s4存的字符串是aaaaaaaaaa
string s5(s4); //拷贝初始化,深拷贝字符串
string s6("I am Ali"); //直接初始化
string s7 = string(6, 'c'); //拷贝初始化,cccccc

这里写图片描述

#include <iostream>
#include <string>

using namespace std;

int main()
{
    string s1;//初始化字符串,空字符串
    string s2 = s1; //拷贝初始化,深拷贝字符串
    string s3 = "I am Yasuo"; //直接初始化,s3存了字符串
    string s4(10, 'a'); //s4存的字符串是aaaaaaaaaa
    string s5(s4); //拷贝初始化,深拷贝字符串
    string s6("I am Ali"); //直接初始化
    string s7 = string(6, 'c'); //拷贝初始化,cccccc

    //string的各种操作
    string s8 = s3 + s6;//将两个字符串合并成一个
    s3 = s6;//用一个字符串来替代另一个字符串的对用元素

    cin >> s1;

    cout << s1 << endl;
    cout << s2 << endl;
    cout << s3 << endl;
    cout << s4 << endl;
    cout << s5 << endl;
    cout << s6 << endl;
    cout << s7 << endl;
    cout << s8 << endl;
    cout << "s7 size = " << s7.size() << endl; //字符串长度,不包括结束符
    cout << (s2.empty() ? "This string is empty" : "This string is not empty") << endl;;

    system("pause");
    return 0;
}

这里写图片描述
string的IO操作
使用cin读入字符串时,遇到空白就停止读取。比如程序输入的是
" Hello World"
那么我们得到的字符串将是"Hello",前面的空白没了,后面的world也读不出来。

如果我们想把整个hello world读进来怎么办?那就这样做
cin>>s1>>s2;
hello存在s1里,world存在s2里了。

有时我们想把一个句子存下来,又不想像上面那样创建多个string来存储单词,怎么办?

那就是用getline来获取一整行内容。

string str;
getline(cin, str);
cout << str << endl;

当把string对象和字符面值及字符串面值混在一条语句中使用时,必须确保+的两侧的运算对象至少有一个是string

string s1 = s2 + ", "; //正确
string s3 = "s " + ", "; //错误
string s4 = "hello" + ", " + s1; //错误
string s5 = s1 + "hello " + ", "; //改一下顺序,s1放前头,正确了,注意理解=号右边的运算顺序

####处理string中的字符
访问字符串的每个字符

for (int i = 0; i < s3.size(); i++)
{
    cout << s3[i] << endl;
    s3[i] = 's';
}

在C语言中我都是用下标或者指针来访问数组元素,而在C++里,有个新奇的东西叫做迭代器iterator,我们可以使用它来访问容器元素。
这里写图片描述

string str("hi sysu");
for (string::iterator it = str.begin(); it != str.end(); it++)
{
    cout << *it << endl;
}

我们也可以是使用const_iterator使得访问元素时是能读不能写,这跟常量指针意思差不多。

string str2("hi sysu");
for (string::const_iterator it = str2.begin(); it != str2.end(); it++)
{
    cout << *it << endl;
    *it = 'l'; //这是错误的,不能写
}

这里写图片描述
####string还有一些很好用的函数,比如找子串

string sq("heoolo sdaa ss");
cout << s.find("aa", 0) << endl; //返回的是子串位置。第二个参数是查找的起始位置,如果找不到,就返回string::npos
if (s.find("aa1", 0) == string::npos)
{
    cout << "找不到该子串!" << endl;
}

vector

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

C++ STL中的verctor好比是C语言中的数组,但是vector又具有数组没有的一些高级功能。与数组相比,vector就是一个可以不用再初始化就必须制定大小的边长数组,当然了,它还有许多高级功能。

要想用vector首先得包含头文件vector。

#include <vector>

二、容器特性
1.顺序序列
顺序容器中的元素按照严格的线性顺序排序。可以通过元素在序列中的位置访问对应的元素。

2.动态数组
支持对序列中的任意元素进行快速直接访问,甚至可以通过指针算述进行该操作。操供了在序列末尾相对快速地添加/删除元素的操作。

3.能够感知内存分配器的(Allocator-aware)
容器使用一个内存分配器对象来动态地处理它的存储需求。

三、基本函数实现
1.构造函数
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中
2.增加函数
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)间的数据
3.删除函数
iterator erase(iterator it):删除向量中迭代器指向元素
iterator erase(iterator first,iterator last):删除向量中[first,last)中元素
void pop_back():删除向量中最后一个元素
void clear():清空向量中所有元素
4.遍历函数
reference at(int pos):返回pos位置元素的引用
reference front():返回首元素的引用
reference back():返回尾元素的引用
iterator begin():返回向量头指针,指向第一个元素
iterator end():返回向量尾指针,指向向量最后一个元素的下一个位置
reverse_iterator rbegin():反向迭代器,指向最后一个元素
reverse_iterator rend():反向迭代器,指向第一个元素之前的位置
5.判断函数
bool empty() const:判断向量是否为空,若为空,则向量中无元素
6.大小函数
int size() const:返回向量中元素的个数
int capacity() const:返回当前向量张红所能容纳的最大元素值
int max_size() const:返回最大可允许的vector元素数量值
7.其他函数
void swap(vector&):交换两个同类型向量的数据
void assign(int n,const T& x):设置向量中第n个元素的值为x
void assign(const_iterator first,const_iterator last):向量中[first,last)中元素设置成当前向量元素
8.看着清楚
1.push_back 在数组的最后添加一个数据

2.pop_back 去掉数组的最后一个数据

3.at 得到编号位置的数据

4.begin 得到数组头的指针

5.end 得到数组的最后一个单元+1的指针

6.front 得到数组头的引用

7.back 得到数组的最后一个单元的引用

8.max_size 得到vector最大可以是多大

9.capacity 当前vector分配的大小

10.size 当前使用数据的大小

11.resize 改变当前使用数据的大小,如果它比当前使用的大,者填充默认值

12.reserve 改变当前vecotr所分配空间的大小

13.erase 删除指针指向的数据项

14.clear 清空当前的vector

15.rbegin 将vector反转后的开始指针返回(其实就是原来的end-1)

16.rend 将vector反转构的结束指针返回(其实就是原来的begin-1)

17.empty 判断vector是否为空

18.swap 与另一个vector交换数据

这里写图片描述
如果vector的元素类型是int,默认初始化为0;如果vector元素类型为string,则默认初始化为空字符串。

vector<int> v1;
vector<father> v2;
vector<string> v3;
vector<vector<int> >;  //注意空格。这里相当于二维数组int a[n][n];
vector<int> v5 = { 1,2,3,4,5 }; //列表初始化,注意使用的是花括号
vector<string> v6 = { "hi","my","name","is","lee" };
vector<int> v7(5, -1); //初始化为-1,-1,-1,-1,-1。第一个参数是数目,第二个参数是要初始化的值
vector<string> v8(3, "hi");
vector<int> v9(10); //默认初始化为0
vector<int> v10(4); //默认初始化为空字符串

这里写图片描述
这里写图片描述

请使用push_back加入元素,并且这个元素是被加在数组尾部的。

for (int i = 0; i < 20; i++)
{
    v1.push_back(i);
}

访问和操作vector中的每个元素

for (int i = 0; i < v1.size(); i++)
{
    cout << v1[i] << endl;
    v1[i] = 100;
    cout << v1[i] << endl;
}

注意:只能对已存在的元素进行赋值或者修改操作,如果是要加入新元素,务必使用push_back。push_back的作用有两个:告诉编译器为新元素开辟空间、将新元素存入新空间里。

比如下面的代码是错误的,但是编译器不会报错,就像是数组越界。
vector<int> vec;
vec[0] = 1; //错误!
当然我们也可以选择使用迭代器来访问元素

vector<string> v6 = { "hi","my","name","is","lee" };
for (vector<string>::iterator iter = v6.begin(); iter != v6.end(); iter++)
{
    cout << *iter << endl;
    //下面两种方法都行
    cout << (*iter).empty() << endl;
    cout << iter->empty() << endl; 
}

上面是正向迭代,如果我们想从后往前迭代该如何操作?
使用反向迭代器

for (vector<string>::reverse_iterator iter = v6.rbegin(); iter != v6.rend(); iter++)
{
    cout << *iter << endl;
}

###实例

#include <iostream>
#include <vector>
#include <string>

using namespace std;

template <typename T>
void showvector(vector<T> v)
{
    for (vector<T>::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << *it;
    }
    cout << endl;
}

int main()
{
    vector<string> v6 = { "hi","my","name","is","lee" };
    v6.resize(3);  //重新调整vector容量大小
    showvector(v6);

    vector<int> v5 = { 1,2,3,4,5 }; //列表初始化,注意使用的是花括号
    cout << v5.front() << endl; //访问第一个元素
    cout << v5.back() << endl; //访问最后一个元素

    showvector(v5);
    v5.pop_back(); //删除最后一个元素
    showvector(v5);
    v5.push_back(6); //加入一个元素并把它放在最后
    showvector(v5);
    v5.insert(v5.begin()+1,9); //在第二个位置插入新元素
    showvector(v5);
    v5.erase(v5.begin() + 3);  //删除第四个元素
    showvector(v5);
    v5.insert(v5.begin() + 1, 7,8); //连续插入7个8
    showvector(v5);
    v5.clear(); //清除所有内容
    showvector(v5);

    system("pause");
    return 0;
} 

这里写图片描述
注意:虽然vertor对象可以动态增长,但是也或有一点副作用:已知的一个限制就是不能再范围for循环中向vector对象添加元素。另外一个限制就是任何一种可能改变vector对象容量的操作,不如push_back,都会使该迭代器失效。

总而言之就是:但凡使用了迭代器的循环体,都不要向迭代器所属的容器添加元素!

C++中push_back和insert两个有什么区别?

顾名思义push_back把元素插入容器末尾,insert把元素插入任何你指定的位置。
不过push_back速度一般比insert快。如果能用push_back尽量先用push_back。

1.pop_back()&push_back(elem)实例在容器最后移除和插入数据

#include <string.h> 
#include <vector>
#include <iostream>
using namespace std;

int main()
{
    vector<int>obj(10,0);//最大容器为10,都初始化为0
    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<<"\n"<<endl;

    for(int i=0;i<obj.size();i++)//size()容器中实际数据个数 
    {
        cout<<obj[i]<<",";
    }

    return 0;
}
输出结果为:

0,1,2,3,4,5,6,7,8,9,

0,1,2,3,4,

2.clear()清除容器中所以数据

#include <string.h>
#include <vector>
#include <iostream>
using namespace std;
 
int main()
{
    vector<int>obj;
    for(int i=0;i<10;i++)//push_back(elem)在数组最后添加数据 
    {
        obj.push_back(i);
        cout<<obj[i]<<",";
    }
 
    obj.clear();//清除容器中所以数据
    for(int i=0;i<obj.size();i++)
    {
        cout<<obj[i]<<endl;
    }
 
    return 0;
}
输出结果为:

0,1,2,3,4,5,6,7,8,9,

3.排序

#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<<"\n"<<endl;
 
    cout<<"从大到小:"<<endl;
    reverse(obj.begin(),obj.end());//从大到小 
    for(int i=0;i<obj.size();i++)
    {
        cout<<obj[i]<<",";
    }
    return 0;
}
输出结果为:

从小到大:
0,1,3,

从大到小:
3,1,0,

1.注意 sort 需要头文件 #include

2.如果想 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);

4.访问(直接数组访问&迭代器访问)

#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;
}

5.二维数组两种定义方法(结果一样)

输出结果为:

直接利用数组:0 1 2 3 4 5 6 7 8 9 
利用迭代器:0 1 2 3 4 5 6 7 8 9
方法一
#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<<"\n";
    }
    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<<"\n";
    }
    return 0;
}
输出结果为:

0 0 0 0 0 0 
0 0 0 0 0 0 
0 0 0 0 0 0 
0 0 0 0 0 0 
0 0 0 0 0 0 

set

set跟vector差不多,它跟vector的唯一区别就是,set里面的元素是有序的且唯一的,只要你往set里添加元素,它就会自动排序,而且,如果你添加的元素set里面本来就存在,那么这次添加操作就不执行。要想用set先加个头文件set。
#include <set>

#include <iostream>
#include <set>
#include <string>

using namespace std;

template <typename T>
void showset(set<T> v)
{
    for (set<T>::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << *it;
    }
    cout << endl;
}

int main()
{
    set<int> s1{9,8,1,2,3,4,5,5,5,6,7,7 }; //自动排序,从小到大,剔除相同项
    showset(s1);
    set<string> s2{ "hello","sysy","school","hello" }; //字典序排序
    showset(s2);
    s1.insert(9); //有这个值了,do nothing
    showset(s1);
    s2.insert("aaa"); //没有这个字符串,添加并且排序
    showset(s2);
    
    system("pause");
    return 0;
} 

这里写图片描述

set的各成员函数列表如下:

c++ stl容器set成员函数:begin()–返回指向第一个元素的迭代器

c++ stl容器set成员函数:clear()–清除所有元素

c++ stl容器set成员函数:count()–返回某个值元素的个数

c++ stl容器set成员函数:empty()–如果集合为空,返回true

c++ stl容器set成员函数:end()–返回指向最后一个元素的迭代器

c++ stl容器set成员函数:equal_range()–返回集合中与给定值相等的上下限的两个迭代器

c++ stl容器set成员函数:erase()–删除集合中的元素

c++ stl容器set成员函数:find()–返回一个指向被查找到元素的迭代器

c++ stl容器set成员函数:get_allocator()–返回集合的分配器

c++ stl容器set成员函数:insert()–在集合中插入元素

c++ stl容器set成员函数:lower_bound()–返回指向大于(或等于)某值的第一个元素的迭代器

c++ stl容器set成员函数:key_comp()–返回一个用于元素间值比较的函数

c++ stl容器set成员函数:max_size()–返回集合能容纳的元素的最大限值

c++ stl容器set成员函数:rbegin()–返回指向集合中最后一个元素的反向迭代器

c++ stl容器set成员函数:rend()–返回指向集合中第一个元素的反向迭代器

c++ stl容器set成员函数:size()–集合中元素的数目

c++ stl容器set成员函数:swap()–交换两个集合变量

c++ stl容器set成员函数:upper_bound()–返回大于某个值元素的迭代器

c++ stl容器set成员函数:value_comp()–返回一个用于比较元素间的值的函数

常用操作:
1.元素插入:insert()
2.中序遍历:类似vector遍历(用迭代器)
3.反向遍历:利用反向迭代器reverse_iterator。
例:

1 set<int> s;
2 ......
3 set<int>::reverse_iterator rit;
4 for(rit=s.rbegin();rit!=s.rend();rit++)

4.元素删除:与插入一样,可以高效的删除,并自动调整使红黑树平衡。

1 set<int> s;
2 s.erase(2);        //删除键值为2的元素
3 s.clear();

5.元素检索:find(),若找到,返回该键值迭代器的位置,否则,返回最后一个元素后面一个位置。

set<int> s;
set<int>::iterator it;
it = s.find(5);    //查找键值为5的元素
if (it != s.end())    //找到
cout << *it << endl;
else            //未找到
cout << "未找到";

6.自定义比较函数
(1)元素不是结构体:
例:
//自定义比较函数myComp,重载“()”操作符

struct myComp
{
    bool operator()(const your_type &a, const your_type &b){
        return a.data - b.data > 0;
    }
}
set<int, myComp>s;
......
set<int, myComp>::iterator it; erator it;

(2)如果元素是结构体,可以直接将比较函数写在结构体内。
例:

struct Info
{
    string name;
    float score;
    //重载“<”操作符,自定义排序规则
    bool operator < (const Info &a) const
    {
        //按score从大到小排列
        return a.score<score;
    }
}
set<Info> s;
......
set<Info>::iterator it;
#include<iostream> 
#include<set> 
using namespace std; 
//set插入元素操作  
int main() 
{ 
    //定义一个int型集合对象s,当前没有任何元素.由www.169it.com搜集整理
    set<int> s; 
    s.insert(8);  //第一次插入8,可以插入  
    s.insert(1); 
    s.insert(12); 
    s.insert(6); 
    s.insert(8);   //第二次插入8,重复元素,不会插入  
    set<int>::iterator it; //定义前向迭代器 
    for(it=s.begin();it!=s.end();it++) 
    cout<<*it<<endl;    
    system("pause"); 
    return 0; 
}

对于集合来说,我们一般有并集、交集、差集、补集这几种操作,所以在set的操作中我们也有类似的集合操作,它们都在#include的头文件下:

std::set_intersection() :这个函数是求两个集合的交集。
std::set_union() :求两个集合的并集
std::set_difference():差集
std::set_symmetric_difference(): 取集合对称差集

其中,关于函数的五个参数问题做一下小结:

1、这几个函数的前四个参数一样,只有第五个参数有多重版本。

2、EX1:set_union(A.begin(),A.end(),B.begin(),B.end(),inserter( C1 , C1.begin() ) );前四个参数依次是第一的集合的头尾,第二个集合的头尾。第五个参数的意思是将集合A、B取合集后的结果存入集合C中。

EX2:set_union(A.begin(),A.end(),B.begin(),B.end(),ostream_iterator(cout," “));这里的第五个参数的意思是将A、B取合集后的结果直接输出,(cout," ")双引号里面是输出你想用来间隔集合元素的符号或是空格。

#include <iostream>
#include <iterator>
#include <set>
#include <algorithm>
using namespace std;
int main()
{
    set<int> eg1;
    eg1.insert(1);
    eg1.insert(100);
    eg1.insert(5);
    eg1.insert(1); //元素1因为已经存在所以set中不会再次插入1
    eg1.insert(10);
    eg1.insert(9);
    //遍历set,可以发现元素是有序的
    set<int>::iterator set_iter = eg1.begin();
    cout << "Set named eg1:" << endl;
    for (; set_iter != eg1.end(); set_iter++) cout << *set_iter << " ";
    cout << endl;
    //使用size()函数可以获得当前元素个数
    cout << "Now there are " << eg1.size() << " elements in the set eg1" << endl;
    if (eg1.find(200) == eg1.end())//find()函数可以查找元素是否存在
        cout << "200 isn't in the set eg1" << endl;



    set<int> eg2;
    for (int i = 6; i < 15; i++)
        eg2.insert(i);
    cout << "Set named eg2:" << endl;
    for (set_iter = eg2.begin(); set_iter != eg2.end(); set_iter++)
        cout << *set_iter << " ";
    cout << endl;

    //获得两个set的并
    set<int> eg3;
    cout << "Union(两个set的并集):";
    set_union(eg1.begin(),
        eg1.end(),
        eg2.begin(),
        eg2.end(),
        insert_iterator<set<int> >(eg3, eg3.begin())
        );//注意第五个参数的形式
    copy(eg3.begin(), eg3.end(), ostream_iterator<int>(cout, " "));
    cout << endl;

    //获得两个set的交,注意进行集合操作之前接收结果的set要调用clear()函数清空一下
    eg3.clear();
    set_intersection(eg1.begin(),
        eg1.end(),
        eg2.begin(),
        eg2.end(),
        insert_iterator<set<int> >(eg3, eg3.begin())
        );
    cout << "Intersection:";
    copy(eg3.begin(), eg3.end(), ostream_iterator<int>(cout, " "));
    cout << endl;

    //获得两个set的差
    eg3.clear();
    set_difference(eg1.begin(),
        eg1.end(), eg2.begin(),
        eg2.end(),
        insert_iterator<set<int> >(eg3, eg3.begin())
        );
    cout << "Difference:";
    copy(eg3.begin(), eg3.end(), ostream_iterator<int>(cout, " "));
    cout << endl;

    //获得两个set的对称差,也就是假设两个集合分别为A和B那么对称差为AUB-A∩B
    eg3.clear();
    set_symmetric_difference(eg1.begin(), eg1.end(), eg2.begin(), eg2.end(), insert_iterator<set<int> >(eg3, eg3.begin()));
    copy(eg3.begin(), eg3.end(), ostream_iterator<int>(cout, " "));
    cout << endl;

    return 0;
}



下面给出一个关键字类型为char*的示例代码

#include<iostream>
#include<iterator>
#include<set>
using namespace std;
struct ltstr
{
    bool operator() (const char* s1, const char* s2) const
    {
        return strcmp(s1, s2) < 0;
    }
};

int main()
{
    const int N = 6;
    const char* a[N] = { "isomer", "ephemeral", "prosaic",
        "nugatory", "artichoke", "serif" };
    const char* b[N] = { "flat", "this", "artichoke",
        "frigate", "prosaic", "isomer" };

    set<const char*, ltstr> A(a, a + N);
    set<const char*, ltstr> B(b, b + N);
    set<const char*, ltstr> C;

    cout << "Set A: ";
    //copy(A.begin(), A.end(), ostream_iterator<const char*>(cout, " "));
    set<const char*, ltstr>::iterator itr;
    for (itr = A.begin(); itr != A.end(); itr++) cout << *itr << " ";
    cout << endl;
    cout << "Set B: ";
    copy(B.begin(), B.end(), ostream_iterator<const char*>(cout, " "));
    cout << endl;

    cout << "Union: ";
    set_union(A.begin(), A.end(), B.begin(), B.end(),
        ostream_iterator<const char*>(cout, " "),
        ltstr());
    cout << endl;

    cout << "Intersection: ";
    set_intersection(A.begin(), A.end(), B.begin(), B.end(), ostream_iterator<const char*>(cout, " "), ltstr());
    cout << endl;
    set_difference(A.begin(), A.end(), B.begin(), B.end(), inserter(C, C.begin()), ltstr());
    cout << "Set C (difference of A and B): ";
    copy(C.begin(), C.end(), ostream_iterator<const char*>(cout, " "));
    cout << endl;
    return 0;
}

其中的ltstr也可以这样定义
class ltstr
{
public:
    bool operator() (const char* s1, const char*s2)const
    {
        return strcmp(s1, s2) < 0;
    }
};

更加通用的应用方式那就是数据类型也是由用户自定义的类来替代,比较的函数自定义,甚至可以加上二级比较,比如首先按照总分数排序,对于分数相同的按照id排序,下面是示例代码

#include<set>
#include<iostream>
using namespace std;
struct
{
    int id;
    int score;
    string name;
};
struct compare
{
    bool operator()(const Entity& e1, const Entity& e2)const   {
        if (e1.score < e2.score) return true;
        else
        if (e1.score == e2.score)
        if (e1.id < e2.id) return true;

        return false;
    }
};

int main()
{
    set<Entity, compare>s_test;
    Entity a, b, c;
    a.id = 123; a.score = 90; a.name = "bill";
    b.id = 121; b.score = 85; b.name = "mary";
    c.id = 130; c.score = 85; c.name = "jerry";
    s_test.insert(a); s_test.insert(b); s_test.insert(c);
    set<Entity, compare>::iterator itr;
    cout << "Score List(ordered by score):\n";
    for (itr = s_test.begin(); itr != s_test.end(); itr++)
        cout << itr->id << "---" << itr->name << "---" << itr->score << endl;
    return 0;
}

list

list就是链表,在C语言中我们想使用链表都是自己去实现的,实现起来倒不难,但是如果有现成的高效的链表可以使用的话,我们就不需要重复造轮子了。STL就提供了list容器给我们。

list是一个双向链表,而单链表对应的容器则是foward_list。

list即双向链表的优点是插入和删除元素都比较快捷,缺点是不能随机访问元素。

初始化方式就大同小异了,跟vector基本一样。要想用list先加个头文件list。
#include <list>

#include <iostream>
#include <list>
#include <string>

using namespace std;

template <typename T>
void showlist(list<T> v)
{
    for (list<T>::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << *it;
    }
    cout << endl;
}

int main()
{
    list<int> l1{ 1,2,3,4,5,5,6,7,7 };
    showlist(l1);
    list<double> l2;
    list<char> l3(10);
    list<int> l4(5, 10); //将元素都初始化为10
    showlist(l4);

    
    system("pause");
    return 0;
} 

这里写图片描述
值得注意的是,list容器不能调用algorithm下的sort函数进行排序,因为sort函数要求容器必须可以随机存储,而list做不到。所以,list自己做了一个自己用的排序函数,用法如下:

list<int> l1{ 8,5,7,6,1,2,3,4,5,5,6,7,7 };
l1.sort();

map

map运用了哈希表地址映射的思想,也就是key-value的思想,来实现的。

首先给出map最好用也最最常用的用法例子,就是用字符串作为key去查询操作对应的value。

C++中map容器提供一个键值对容器,map与multimap差别仅仅在于multiple允许一个键对应多个值。本文主要总结一下map基本用法和嵌套用法示例。

####map基本用法

1 头文件
#include <map>

2 定义
map<int, int> my_Map; //注意这里的int和int可以是其他类型
或者是typedef map<int, int> MY_MAP;
MY_MAP my_Map;

3 插入数据
(1) my_Map[1] = 1;
(2) my_Map.insert(map<int, int>::value_type(2,2));
(3) my_Map.insert(pair<int,int>(3,3));
(4) my_Map.insert(make_pair<string,int>(4,4));

4 查找数据和修改数据

 (1)    int i = my_Map[1]; 
        my_Map[1]   =   i; 
  (2)   MY_MAP::iterator   my_Itr; 
        my_Itr.find(2); 
        int j = my_Itr->second; 
        my_Itr->second = j; 

注意:

A.键本身是不能被修改的,除非删除。

B.不管键存不存在,比如my_Map[1] = i;,都会执行赋值操作。

5 删除数据
(1) my_Map.erase(my_Itr);
(2) my_Map.erase(3);

6 遍历数据
for (my_Itr=my_Map.begin(); my_Itr!=my_Map.end(); ++my_Itr) {}

7 其它方法
my_Map.size() :返回元素数目
my_Map.empty():判断是否为空
my_Map.clear():清空所有元素

二、嵌套用法

1.示例如下:


    map<int,map<int,int> >multiMap; //对于这样的map嵌套定义,  
    map<int, int> temp;    //定义一个map<int, string>变量,对其定义后在插入multiMap  
    temp[9] = 9;  
    temp[10] = 10;  
    multiMap[10] = temp;  
    multiMap[10][11]=11;   
    multiMap[5][30]=30;  
    map<int,map<int,int> >::iterator multitr;  // 以下是如何遍历本multiMap  
    map<int,int>::iterator intertr;  
    for(multitr=multiMap.begin();multitr!=multiMap.end();multitr++)  
    { 
        for(intertr= multitr ->second.begin(); intertr != multitr ->second.end(); intertr ++)  
            cout<< multitr ->first<<" "<<intertr->first<<" ("<<intertr -> second <<")"<<endl;  
    } 

or

map<int,map<int,int>* >multiMap;  
map<int, int>* temp = new map<int, int>;  
multiMap[10]=temp;  

这样动态new内存,就要记得delete,否则会有内存泄露,delete如下:


map<int, int>* temp1;
for(multitr=multiMap.begin();multitr!=multiMap.end();multitr++)  
{ 
	temp1 = multitr ->second;
        delete  temp1;
        temp1 = NULL;
} 

例子:

#include <iostream>
#include <map>
#include <string>

using namespace std;

void showmap(map<string, int> v)
{
    for (map<string, int>::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << it->first << "  " << it->second << endl;  //注意用法,不是用*it来访问了。first表示的是key,second存的是value
    }
    cout << endl;
}

int main()
{
    map<string, int> m1; //<>里的第一个参数表示key的类型,第二个参数表示value的类型
    m1["Kobe"] = 100;
    m1["James"] = 99;
    m1["Curry"] = 98;

    string s("Jordan");
    m1[s] = 90;

    cout << m1["Kobe"] << endl;
    cout << m1["Jordan"] << endl;
    cout << m1["Durant"] << endl; //不存在这个key,就显示0

    m1.erase("Curry");//通过关键字来删除
    showmap(m1);
    m1.insert(pair<string, int>("Harris", 89)); //也可以通过insert函数来实现增加元素
    showmap(m1);
    m1.clear(); //清空全部


    system("pause");
    return 0;
}

这里写图片描述


unordered_map

https://blog.csdn.net/ddkxddkx/article/details/6555754

在开发过程中,键值对型容器使用频率可以说是比较多的,当前C++中有两种该类型容器,map与unordered_map。这两种容器在不同场景下的作用是不同的,应用得当对优化性能有不小的帮助。

map是基于红黑树实现。红黑树作为一种自平衡二叉树,保障了良好的最坏情况运行时间,即它可以做到在O(log n)时间内完成查找,插入和删除,在对单次时间敏感的场景下比较建议使用map做为容器。比如实时应用,可以保证最坏情况的运行时间也在预期之内。
另红黑树是一种二叉查找树,二叉查找树一个重要的性质是有序,且中序遍历时取出的元素是有序的。对于一些需要用到有序性的应用场景,应使用map。
unordered_map是基于hash_table实现,一般是由一个大vector,vector元素节点可挂接链表来解决冲突来实现。hash_table最大的优点,就是把数据的存储和查找消耗的时间大大降低,几乎可以看成是常数时间;而代价仅仅是消耗比较多的内存。然而在当前可利用内存越来越多的情况下,用空间换时间的做法是值得的。
值得注意的是,在使用unordered_map设置合适的hash方法,可以获得良好的性能。

在需要有序性或者对单次查询有时间要求的应用场景下,应使用map,其余情况应使unordered_map

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> m;
        for(int i=0; i<nums.size(); i++)
        {
            if(m.find(target-nums[i]) != m.end())
            {
                return {m[target-nums[i]], i};
            }
            m[nums[i]] = i;
        }
        return {};
    }
};

###C\C++中计时、延时函数
头文件: #include <time.h>

#include <iostream>
#include <time.h>
void timeConsume()
{
    double start,stop,durationTime;
    start = clock();
    /*

    ...代码片段

    */
    stop = clock();

    durationTime = ((double)(stop-start))/CLK_TCK;
    cout << "程序耗时:" << durationTime << " s" << endl;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值