STL常用容器用法
STL容器就是将运用最广泛的一些数据结构实现出来
常用的数据结构:数组, 链表,树, 栈, 队列, 集合, 映射表 等
这些容器分为序列式容器和关联式容器两种:
| 序列式容器 | 强调值的排序,序列式容器中的每个元素均有固定的位置 | 
| 关联式容器 | 二叉树结构,各元素之间没有严格的物理上的顺序关系 | 
String容器
Stirng 本质上是C++风格的字符串,是一个类。
string和char * 区别:
- char * 是一个指针
- string是一个类,类内部封装了char*,管理这个字符串,是一个char*型的容器。
构造函数原型:
- string();//创建一个空的字符串 例如: string str; 无参构造
 - string(const char* s);//使用字符串s初始化 有参构造
- string(const string& str);//使用一个string对象初始化另一个string对象 拷贝构造
- string(int n, char c);//使用n个字符c初始化 重载 有参构造
赋值的函数原型:
- string& operator=(const char* s);//char*类型字符串 赋值给当前的字符串
- string& operator=(const string &s);//把字符串s赋给当前的字符串
- string& operator=(char c);//字符赋值给当前的字符串
- string& assign(const char *s);//把字符串s赋给当前的字符串
- string& assign(const char *s, int n);//把字符串s的前n个字符赋给当前的字符串
- string& assign(const string &s);//把字符串s赋给当前字符串
- string& assign(int n, char c);//用n个字符c赋给当前字符串
string字符串拼接函数原型:
- string& operator+=(const char* str);//重载+=操作符
- string& operator+=(const char c);//重载+=操作符
- string& operator+=(const string& str);//重载+=操作符
- string& append(const char *s);//把字符串s连接到当前字符串结尾
- string& append(const char *s, int n);//把字符串s的前n个字符连接到当前字符串结尾
- string& append(const string &s);//同operator+=(const string& str)
- string& append(const string &s, int pos, int n);//字符串s中从pos开始的n个字符连接到字符串结尾
	string str1 = "aaaaa1111";
	string str2(10, 'b');
	str2.append(str1,4,3);
        //bbbbbbbbbba11
string查找替换函数原型:
- int find(const string& str, int pos = 0) const;//查找str第一次出现位置,从pos开始查找
- int find(const char* s, int pos = 0) const;//查找s第一次出现位置,从pos开始查找
- int find(const char* s, int pos, int n) const;//从pos位置查找 s的前n个字符 第一次位置
- int find(const char c, int pos = 0) const;//查找字符c第一次出现位置
- int rfind(const string& str, int pos = npos) const;//查找str最后一次位置,从pos开始查找
- int rfind(const char* s, int pos = npos) const;//查找s最后一次出现位置,从pos开始查找
- int rfind(const char* s, int pos, int n) const;//从pos查找s的前n个字符最后一次位置
- int rfind(const char c, int pos = 0) const;//查找字符c最后一次出现位置
- string& replace(int pos, int n, const string& str);//替换从pos开始n个字符为字符串str
- string& replace(int pos, int n,const char* s);//替换从pos开始的n个字符为字符串s
	string str1= "abcdefghijkabcdefghijk";
	
	cout << str1.find("de") << endl;//
	cout << str1.find("df") << endl;
	cout << str1.find("efg",1,2) << endl;
	//rfind形参与find一致 从右往左寻找字符串
	cout << str1.rfind("de") << endl;
	cout << str1.replace(3, 5, "111") << endl;
     //3
     //4294967295
     //4
     //14
     //abc111ijkabcdefghijk
调试的时候出现了问题 即直接输出find()没有找到结果时 返回4294967295,明显是因为find返回值为无符号型整数
查看相关实现string.find在未找到时会返回string::npos
定义为
static const size_t npos = -1;
size_t类型是无符号整数类型
因此调用时需要注意
int pos =find("df");//进行转化为带符号型
string字符串比较函数原型:
- int compare(const string &s) const;//与字符串s比较
- int compare(const char *s) const;//与字符串s比较
比较方式:
- 字符串比较是按字符的ASCII码进行对比
= 返回 0
> 返回 1
< 返回 -1
字符函数原型:
- char& operator[](int n);//通过[]方式取字符
- char& at(int n);//通过at方法获取字符
string插入和删除函数原型:
- string& insert(int pos, const char* s);//插入字符串
- string& insert(int pos, const string& str);//插入字符串
- string& insert(int pos, int n, char c);//在指定位置插入n个字符c
- string& erase(int pos, int n = npos);//删除从Pos开始的n个字符
string子串函数原型:
- string substr(int pos = 0, int n = npos) const;//返回由pos开始的n个字符组成的字符串
vector容器
- vector数据结构和数组非常相似,也称为单端数组
- vector与普通数组不同之处在于数组是静态空间,而vector可以动态扩展
- 动态扩展并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间
- vector容器的迭代器是支持随机访问的迭代器
** vector创建函数原型:**
- vector<T> v;//采用模板实现类实现,默认构造函数
- vector(v.begin(), v.end());//将v[begin(), end())区间中的元素拷贝给本身。
- vector(n, elem);//构造函数将n个elem拷贝给本身。
- vector(const vector &vec);//拷贝构造函数。
vector赋值函数原型:
- vector& operator=(const vector &vec);//重载等号操作符
- assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身。(传入迭代器)
- assign(n, elem);//将n个elem拷贝赋值给本身。
vector容量大小操作 函数原型:
- 
empty();//判断容器是否为空
- 
capacity();//容器的容量
- 
size();//返回容器中元素的个数
- 
resize(int num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。 //如果容器变短,则末尾超出容器长度的元素被删除。 
 //如果容器变长,默认填充0,可重载替换
- 
resize(int num, elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。 //如果容器变短,则末尾超出容器长度的元素被删除 
插入删除函数原型:
- push_back(ele);//尾部插入元素ele
- pop_back();//删除最后一个元素
- insert(const_iterator pos, ele);//迭代器指向位置pos插入元素ele
- insert(const_iterator pos, int count,ele);//迭代器指向位置pos插入count个元素ele
- erase(const_iterator pos);//删除迭代器指向的元素
- erase(const_iterator start, const_iterator end);//删除迭代器从start到end之间的元素
- clear();//删除容器中所有元素
  	for ( vector<int>::iterator it = p.begin(); it != p.end(); it++)// 迭代器遍历
	{
		cout << *it << " ";
	}
	vector<int>::iterator it = p.begin();
        //注意 const vector对应const_iterator迭代器
	while (it != p.end())
	{
		cout << *it << " ";
		it++;
	}
  
- 
iterator,const_iterator作用:遍历容器内的元素,并访问这些元素的值。iterator可以改元素值,但const_iterator不可改。跟C的指针有点像 
 (容器均可以++iter,而vector还可以iter-n, iter+n,n为一整型,iter1-iter2:结果是difference_type类型,表两元素的距离.)
- 
const_iterator 对象可以用于const vector 或非 const vector,它自身的值可以改(可以指向其他元素),但不能改写其指向的元素值. 
- 
const iterator与const_iterator是不一样的:声明一个 const iterator时,必须初始化它。一旦被初始化后,就不能改变它的值,它一旦被初始化后,只能用它来改它指的元素,不能使它指向其他元素。 
数据存取函数原型:
- at(int idx);//返回索引idx所指的数据
- operator[];//返回索引idx所指的数据
- front();//返回容器中第一个数据元素
- back();//返回容器中最后一个数据元素 注意是最后一个- end()-1的指代
容器互换原型:
- swap(vec);// 将vec与本身的元素互换
可以通过swap互换容器实现内存压缩
p.swap(vector<int>(p));//匿名变量交换空间后销毁 
vector<int>(p).swap(p);
vector预留空间
可以减少vector在动态扩展容量时的扩展次数
- reserve(int len);//容器预留len个元素长度,预留位置不初始化,元素不可访问。
        vector<int> v;
	//预留空间
	v.reserve(100000);
        //或者初始化就给定大小
        vector<int> v(100000);
deque容器
双端数组,可以对头端进行插入删除操作
deque与vector区别:
- vector对于头部的插入删除效率低,数据量越大,效率越低
- deque相对而言,对头部的插入删除速度回比vector快
- vector访问元素时的速度会比deque快,这和两者内部实现有关
deque内部工作原理:
deque内部有个中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据
中控器维护的是每个缓冲区的地址,使得使用deque时像一片连续的内存空间

- deque容器的迭代器也是支持随机访问的
deque构造原型: //和vector类似
- deque<T>deqT; //默认构造形式
- deque(beg, end);//构造函数将[beg, end)区间中的元素拷贝给本身。左开右闭
- deque(n, elem);//构造函数将n个elem拷贝给本身。
- deque(const deque &deq);//拷贝构造函数
deque赋值函数原型:
- 
deque& operator=(const deque &deq);//重载等号操作符
- 
assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身。
- 
assign(n, elem);//将n个elem拷贝赋值给本身。
deque大小操作:
- 
deque.empty();//判断容器是否为空
- 
deque.size();//返回容器中元素的个数
- 
deque.resize(num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。 //如果容器变短,则末尾超出容器长度的元素被删除。 
- 
deque.resize(num, elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。 //如果容器变短,则末尾超出容器长度的元素被删除。 
deque无容量的概念
resize()改的是size大小 ,vector的capacity不变
deque插入删除函数原型:
两端插入操作:
- push_back(elem);//在容器尾部添加一个数据
- push_front(elem);//在容器头部插入一个数据
- pop_back();//删除容器最后一个数据
- pop_front();//删除容器第一个数据
相比vector多了前插尾插
指定位置操作:
- 
insert(pos,elem);//在pos位置插入一个elem元素的拷贝,返回新数据的位置。
- 
insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值。
- 
insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值。
- 
clear();//清空容器的所有数据
- 
erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置。
- 
erase(pos);//删除pos位置的数据,返回下一个数据的位置。
deque存取操作: 与vector一致
- at(int idx);//返回索引idx所指的数据
- operator[];//返回索引idx所指的数据
- front();//返回容器中第一个数据元素
- back();//返回容器中最后一个数据元素
排序:
头文件 algorithm
sort(iterator beg, iterator end)  //对beg和end区间内元素进行排序
stack容器
stack是一种先进后出**(First In Last Out,FILO)的数据结构,它只有一个出口
栈中只有顶端的元素才可以被外界使用,因此栈不允许有遍历行为
栈中进入数据称为  --- 入栈  push
栈中弹出数据称为  --- 出栈  pop
构造函数:
- stack<T> stk;//stack采用模板类实现, stack对象的默认构造形式
- stack(const stack &stk);//拷贝构造函数
赋值操作:
- stack& operator=(const stack &stk);//重载等号操作符
数据存取:
- push(elem);//向栈顶添加元素
- pop();//从栈顶移除第一个元素
- top();//返回栈顶元素
大小操作:
- empty();//判断堆栈是否为空
- size();//返回栈的大小
queue容器
Queue是一种先进先出(First In First Out,FIFO)的数据结构,它有两个出口
队列容器允许从一端新增元素,从另一端移除元素
队列中只有队头和队尾才可以被外界使用,因此队列不允许有遍历行为
队列中进数据称为 --- 入队    push
队列中出数据称为 --- 出队    pop
常用接口
构造函数:
- queue<T> que;//queue采用模板类实现,queue对象的默认构造形式
- queue(const queue &que);//拷贝构造函数
赋值操作:
- queue& operator=(const queue &que);//重载等号操作符
数据存取:
- push(elem);//往队尾添加元素
- pop();//从队头移除第一个元素
- back();//返回最后一个元素
- front();//返回第一个元素
大小操作:
- empty();//判断堆栈是否为空
- size();//返回栈的大小
list容器
功能:将数据进行链式存储
链表(list)是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的
链表的组成:链表由一系列结点组成
结点的组成:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域
STL中的链表是一个双向循环链表
由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器
list的优点:
- 采用动态存储分配,不会造成内存浪费和溢出
- 链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素
list的缺点:
- 链表灵活,但是空间(指针域) 和 时间(遍历)额外耗费较大
List有一个重要的性质,插入操作和删除操作都不会造成原有list迭代器的失效,这在vector是不成立的。
常用接口
构造函数:
- list<T> lst;//list采用采用模板类实现,对象的默认构造形式:
- list(beg,end);//构造函数将[beg, end)区间中的元素拷贝给本身。
- list(n,elem);//构造函数将n个elem拷贝给本身。
- list(const list &lst);//拷贝构造函数。
交换赋值函数:
- assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身。
- assign(n, elem);//将n个elem拷贝赋值给本身。
- list& operator=(const list &lst);//重载等号操作符
- swap(lst);//将lst与本身的元素互换。
大小操作:
- 
size();//返回容器中元素的个数
- 
empty();//判断容器是否为空
- 
resize(num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。 //如果容器变短,则末尾超出容器长度的元素被删除。 
- 
resize(num, elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。 //如果容器变短,则末尾超出容器长度的元素被删除。
- 
size();//返回容器中元素的个数
- 
empty();//判断容器是否为空
- 
resize(num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。
 //如果容器变短,则末尾超出容器长度的元素被删除。
- 
resize(num, elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。
 //如果容器变短,则末尾超出容器长度的元素被删除。
插入删除:
- push_back(elem);//在容器尾部加入一个元素
- pop_back();//删除容器中最后一个元素
- push_front(elem);//在容器开头插入一个元素
- pop_front();//从容器开头移除第一个元素
- insert(pos,elem);//在pos位置插elem元素的拷贝,返回新数据的位置。
- insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值。
- insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值。
- clear();//移除容器的所有数据
- erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置。
- erase(pos);//删除pos位置的数据,返回下一个数据的位置。
- remove(elem);//删除容器中所有与elem值匹配的元素。
存取:
- front();//返回第一个元素。
list容器中不可以通过[]或者at方式访问数据
返回第一个元素 --- front
返回最后一个元素 --- back
反转和排序
- reverse();//反转链表
- sort();//链表排序
对于自定义数据类型,必须要指定排序规则,否则编译器不知道如何进行排序
高级排序只是在排序规则上再进行一次逻辑规则制定,并不复杂
class Person {
public:
	Person(string name, int age , int height) {
		m_Name = name;
		m_Age = age;
		m_Height = height;
	}
public:
	string m_Name;  //姓名
	int m_Age;      //年龄
	int m_Height;   //身高
};
bool ComparePerson(Person& p1, Person& p2) {
	if (p1.m_Age == p2.m_Age) {
		return p1.m_Height  > p2.m_Height;
	}
	else
	{
		return  p1.m_Age < p2.m_Age;
	}
}
list<Person> L;
L.sort(ComparePerson); //排序
set/multiset容器
所有元素都会在插入时自动被排序
- set/multiset属于关联式容器,底层结构是用二叉树实现。
set和multiset区别:
- set不允许容器中有重复的元素
- multiset允许容器中有重复的元素
常用接口
构造:
- set<T> st;//默认构造函数:
- set(const set &st);//拷贝构造函数
赋值:
- set& operator=(const set &st);//重载等号操作符
set容器插入数据时用insert
set容器插入数据的数据会自动排序
大小交换:
- size();//返回容器中元素的数目
- empty();//判断容器是否为空
- swap(st);//交换两个集合容器
插入删除:
- insert(elem);//在容器中插入元素。
- clear();//清除所有元素
- erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器。
- erase(beg, end);//删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
- erase(elem);//删除容器中值为elem的元素。
查找统计:
- find(key);//查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();
- count(key);//统计key的元素个数
查找 --- find (返回的是迭代器)
统计 --- count (对于set,结果为0或者1)
set和multiset区别:
- set不可以插入重复数据,而multiset可以
- set插入数据的同时会返回插入结果,表示插入是否成功
- multiset不会检测数据,因此可以插入重复数据
set容器排序:
set容器默认排序规则为从小到大,可以利用仿函数,可以改变排序规则
示例一 set存放内置数据类型
#include <set>
class MyCompare 
{
public:
	bool operator()(int v1, int v2) {
		return v1 > v2;
	}
};
void test01() 
{    
	set<int> s1;
	s1.insert(10);
	s1.insert(40);
	s1.insert(20);
	s1.insert(30);
	s1.insert(50);
	//默认从小到大
	for (set<int>::iterator it = s1.begin(); it != s1.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
	//指定排序规则
	set<int,MyCompare> s2;
	s2.insert(10);
	s2.insert(40);
	s2.insert(20);
	s2.insert(30);
	s2.insert(50);
	for (set<int, MyCompare>::iterator it = s2.begin(); it != s2.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}
int main() {
	test01();
	system("pause");
	return 0;
}
总结:利用仿函数可以指定set容器的排序规则
示例二 set存放自定义数据类型
#include <set>
#include <string>
class Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	string m_Name;
	int m_Age;
};
class comparePerson
{
public:
	bool operator()(const Person& p1, const Person &p2)
	{
		//按照年龄进行排序  降序
		return p1.m_Age > p2.m_Age;
	}
};
void test01()
{
	set<Person, comparePerson> s;
	Person p1("刘备", 23);
	Person p2("关羽", 27);
	Person p3("张飞", 25);
	Person p4("赵云", 21);
	s.insert(p1);
	s.insert(p2);
	s.insert(p3);
	s.insert(p4);
	for (set<Person, comparePerson>::iterator it = s.begin(); it != s.end(); it++)
	{
		cout << "姓名: " << it->m_Name << " 年龄: " << it->m_Age << endl;
	}
}
int main() {
	test01();
	system("pause");
	return 0;
}
map/multimap容器
- 
map中所有元素都是pair 
- 
pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值) 
- 
所有元素都会根据元素的键值自动排序 
- 
map/multimap属于关联式容器,底层结构是用二叉树实现。 
 优点:
- 
可以根据key值快速找到value值 
map和multimap区别:
- map不允许容器中有重复key值元素
- multimap允许容器中有重复key值元素
pair对组创建
- 成对出现的数据,利用对组可以返回两个数据
创建方式:
- pair<type, type> p ( value1, value2 );
- pair<type, type> p = make_pair( value1, value2 );
map函数常用接口
构造:
- 
map<T1, T2> mp;//map默认构造函数:
- 
map(const map &mp);//拷贝构造函数
 赋值:
- 
map& operator=(const map &mp);//重载等号操作符
        map<int,int>m; //默认构造
	m.insert(pair<int, int>(1, 10));
	m.insert(pair<int, int>(2, 20));
	m.insert(pair<int, int>(3, 30));
map中所有元素都是成对出现,插入数据时候要使用对组
大小
- size();//返回容器中元素的数目
- empty();//判断容器是否为空
- swap(st);//交换两个集合容器
插入删除
- insert(elem);//在容器中插入元素。
- clear();//清除所有元素
- erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器。
- erase(beg, end);//删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
- erase(key);//删除容器中值为key的元素。
void printMap(map<int,int>&m)
{
	for (map<int, int>::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << "key = " << it->first << " value = " << it->second << endl;
	}
	cout << endl;
}
四种插入方式
	map<int, int> m;
	//第一种插入方式
	m.insert(pair<int, int>(1, 10));
	//第二种插入方式
	m.insert(make_pair(2, 20));
	//第三种插入方式
	m.insert(map<int, int>::value_type(3, 30));
	//第四种插入方式
	m[4] = 40; 
查找统计
- find(key);//查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();
- count(key);//统计key的元素个数
map排序
map容器默认排序规则为 按照key值进行 从小到大排序,可以利用仿函数,可以改变排序规则(同set)
#include <map>
class MyCompare {
public:
	bool operator()(int v1, int v2) {
		return v1 > v2;
	}
};
void test01() 
{
	//默认从小到大排序
	//利用仿函数实现从大到小排序
	map<int, int, MyCompare> m;
	m.insert(make_pair(1, 10));
	m.insert(make_pair(2, 20));
	m.insert(make_pair(3, 30));
	m.insert(make_pair(4, 40));
	m.insert(make_pair(5, 50));
	for (map<int, int, MyCompare>::iterator it = m.begin(); it != m.end(); it++) {
		cout << "key:" << it->first << " value:" << it->second << endl;
	}
}
int main() {
	test01();
	system("pause");
	return 0;
}
unordered_map
map和unordered_map区别
- map: map内部实现了一个红黑树(红黑树是非严格平衡二叉搜索树,而AVL是严格平衡二叉搜索树),红黑树具有自动排序的功能,因此map内部的所有元素都是有序的,红黑树的每一个节点都代表着map的一个元素。因此,对于map进行的查找,删除,添加等一系列的操作都相当于是对红黑树进行的操作。map中的元素是按照二叉搜索树(又名二叉查找树、二叉排序树,特点就是左子树上所有节点的键值都小于根节点的键值,右子树所有节点的键值都大于根节点的键值)存储的,使用中序遍历可将键值按照从小到大遍历出来。
- unordered_map: unordered_map内部实现了一个哈希表(也叫散列表,通过把关键码值映射到Hash表中一个位置来访问记录,查找的时间复杂度可达到O(1),其在海量数据处理中有着广泛应用)。因此,其元素的排列顺序是无序的。
map优缺点
优点:
- 有序性,这是map结构最大的优点,其元素的有序性在很多应用中都会简化很多的操作。红黑树,内部实现一个红黑书使得map的很多操作在lgn的时间复杂度下就可以实现,因此效率非常的高
缺点:
- 空间占用率高,因为map内部实现了红黑树,虽然提高了运行效率,但是因为每一个节点都需要额外保存父节点、孩子节点和红/黑性质,使得每一个节点都占用大量的空间
unorder_map优缺点
优点:
- 因为内部实现了哈希表,因此其查找速度非常的快
缺点:
- 哈希表的建立比较耗费时间
unordered_map的用法和map是一样的,提供了 insert,size,count等操作,并且里面的元素也是以pair类型来存贮的。但其底层实现是完全不同的。
对于unordered_map或unordered_set容器,其遍历顺序与创建该容器时输入的顺序不一定相同,因为遍历是按照哈希表从前往后依次遍历的
 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号