STL有6大组件:
重点:容器、迭代器和算法
简单介绍:适配器、分配器和仿函数
容器(container):
- 系统帮我们封装好的数据结构(数组array、vector)、链表(单向链表、双向链表)、栈(stack)、队列(queue)、树(map/set)、hash表(hash_map)
- 每种结构都能装任意类型结构,比如数组可以装int、char、对象等(这就是泛型编程的思想,同一个结构可以装多种类型,也是模板的作用)
- 主要是数据结构的操作,增删改查数据库。
算法(algorithm):
- 系统已经帮我们写好了的算法(排序、交换、替换等)
- 一个算法可以适用于多个容器,(比如排序sort,可以给数组排序,也可以给链表排序)。这也是泛型编程的思想,节省了大量的代码,不然我们的每个容器里都要定义自己的排序。
迭代器(iteration):
- 连接容器和算法(连接器)
使用STL的好处:
- 节省开发时间,增加开发效率
- 高移植性:STL是C++标准库,所有的C++编译器都支持
- 高性能:每个容器的操作、算法的实现都是经过几代大师的修改、优化
1. 容器类
1. string
string是专门字符串操作的一个类,很强大。
char 与string区别*:
char*就是指向字符数组首地址的指针,然后系统提供了一个string.h, 这个头文件声明了很多字符串操作函数(strlen、strcat、strcmp、strcpy等).
string是一个类,它将以上的内容封装到一起,使得字符串的操作更灵活,方式更多,管理更合理。
我们在使用string这个类的时候不用考虑内存的分配与释放,不用担心越界崩溃问题,因为前人在封装string的时候,已经把几乎所有的情况都考虑到并处理了。
1.1 属性:
容量(capacity()):对于VS,默认容量大小,少于15个则申请15个容量,当多余15个时,以后每多余容量以16个为单位申请空间。对于VC,默认容量大小,少于31个则申请31个容量,当多余31个时,以后每多余容量以32个为单位申请空间。
容量(reserve()):修改容量,不能变小,只能变大。调用完它之后,容量大小为15+N*16。
length(): 字符串长度,和size()是一样的
size(): 字符串个数,和length()是一样的
resize(n):重新设置字符个数,容量不变
1.2 输出
- 输出全部: << 对象 c_str()(c_str()返回的是指向字符串首地址的指针)
- 输出单个字符:1. [ ] 下标运算,越界会崩溃。 2. at(n) 越界会抛出一个异常。
1.3 修改
- 修改指定元素:[ ] 下标运算,at( ) 运算
- 中间插入
- 尾部插入
- 重新赋值
- 删除指定元素
1.4. 操作函数
- 比较 compare
- 复制 copy
- 查找子串
- 返回子串
- 交换
1.5 string迭代器:
定义一个string对象元素的指针,本质相当于一个char*的指针。定义之后用法和指针是一样的,可以通过迭代器遍历string类的元素,通过迭代器赋值。
迭代器 :现阶段string类型的迭代器用法和指针是一样的,容器实际有用之处在于可以和算法链接,它适用于所有的容器,即一个通用类型的指针,或者类似叫智能指针。
迭代器失效:当string对象又申请了一段空间的时候,原来的迭代器失效。
string成员函数涉及到迭代器的:
iterator begin( )
iterator end( );
append( )
删除
iterator erase(iterator pos)
iterator erase(iterator start, iterator end);
插入
void insert() // 具体使用方法见下面程序
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
// #pragma warning(disable:XXXX)// 可以不显示相应的警告信息,xxxx为警告码
// 定义
void strDefine()
{
// 创建对象
string str;
//const char* str1 = str.c_str(); //
string str1(5, 'a');
cout << str1.c_str() << endl;
string str2("abcdefg");
cout << str2.c_str() << endl;
string str3("abcdef", 3);
cout << str3.c_str() << endl;
string str4("abcdef", 1, 3);
cout << str4.c_str() << endl;
string str5(str4);
cout << str5.c_str() << endl;
}
// 属性
void strProperty()
{
string str;
cout << str.capacity() << endl;
string str1(3,'a');
cout << "str1.size:" << str1.size() << endl;
cout << "str1.length:" << str1.length() << endl;
cout << str1.capacity() << endl;
string str2("abcdefghi");
str2.resize(3);
cout << "str2.size:" << str2.size() << endl;
cout << "str2.length:" << str2.length() << endl;
cout << str2.capacity() << endl;
cout << str2 << endl;
string str3(str2);
cout << str3.capacity() << endl;
}
// 输出
void strCout()
{
string str("abcdefghigk");
cout << str << endl; // 注:str不是字符串,是一个对象,能够直接输出是因为string类里面重载了输出
cout << str.c_str() << endl; // 当创建string对象str的时候,将字符串"abcdefghigk"传入该类,并有一个指针指向这个
// 字符串的首地址,c_str()函数的作用就是返回这个指针,即字符串的首地址
try
{
cout << str.at(29) << endl;
}
catch (...)
{
cout << "out of range" << endl;
}
}
// 修改
void strChange()
{
// 修改指定元素
string str("abcdefgh");
str[2] = 's';
str.at(1) = 'z';
cout << str << endl;
// 中间插入
string str1(3,'w');
str.insert(2, str1);
cout << str << endl;
str.insert(6, "ppppp");
cout << str << endl;
str.insert(3, str1, 0, 2); // 在str字符串的下标为3的位置开始插入str1中下标从0到2位置的字符(左闭右开)。
cout << str << endl;
str.insert(3, 3, 'q'); // 在str字符串的下标为3的位置开始插入3个q
cout << str << endl;
// 尾部插入
string str2("abcdefg");
str2 += str;
str2 += "ppp";
cout << str2 << endl;
str2.append(str);
cout << str2 << endl;
str2.append("opqres",2); // 将字符串前两个字符连接到str2字符串后面
cout << str2 << endl;
// 重新赋值(覆盖以前的)
string str3("abcdefg");
str3 = "def";
cout << str3 << endl;
str3 = str;
cout << str3 << endl;
cin >> str3;
cout << str3 << endl;
str3.assign("asjfla", 3); // 将字符串前3个字符赋值给str3
cout << str3 << endl;
// 删除
str3.erase(2, 5); // 删除str3字符串从第二个到第五个元素
cout << str3 << endl;
str3.erase(0, str3.length());// 删除str3字符串全部元素
cout << str3 << endl;
}
// 其他函数
void strFunction()
{
string str1("acabcdefg");
string str2("abqc");
// 比较
cout << (str1 > str2) << endl; // 从头比较每一个字符,如果是真返回1,否则返回0
cout << str1.compare("aaf") << endl; // 如果str1>字符串则返回1,相等则返回0,小于则返回-1
cout << str1.compare(1, 2, str2) << endl; // 用str1中1-2的字符和str2比较
char arr[6] = { 0 };
// 复制
str1.copy(arr, 2, 3); // arr是接收复制的数组,第二个数是复制字符个数,第三个数是开始的下标
cout << arr << endl;
// 查找(重点)
cout << (int)str1.find(str2, 0) << endl; // 查找str1字符串是否包含str2,第二个数是从str1中开始查找的下标,如果找到
// 返回str1中包含的子串开始的下标,如果没找到则返回-1的补码,强转成int类型即为-1
cout << (int)str1.find("abc", 0) << endl;// 和上面的意义相同
// 返回子串
cout << str1.substr(3, 5) << endl; // 返回str1中从下标3开始的五个字符
// 交换子串
str1.swap(str2);
cout << str1 << endl;
cout << str2 << endl;
cout << str1 + str2 << endl; // 可以加对象
cout << "sjkafhas" + str2 << endl;// 也可以加子串
}
// 迭代器
void funIterator()
{
string str("abcdefgh");
// 定义string容器的一个迭代器
string::iterator ite; // 就相当于一个指向string对象元素的指针,本质是一个char*类型的指针
//char* p = str.c_str();
ite = str.begin(); // begin()和c_str()功能是一样的,返回字符串的首地址
// 通过迭代器输出元素
//for (size_t i = 0; i < str.size(); i++)
//{
// /*cout << *ite << endl;
// ite++;*/
// cout << ite[i] << endl; // 两种输出方式都可以,ite使用方式和指针一样
//}
//for (; ite != str.end(); ite++) // end函数返回的是指向字符串最后一个字符的下一个位置的指针(指向'\0'位置的指针)
//{
// cout << *ite << endl;
//}
通过迭代器赋值
//for (; ite != str.end(); ite++)
//{
// *ite = 'a' ;
// cout << *ite << endl;
//}
//ite[-2] = 'w'; // 通过下标运算也可以修改代码
//cout << str << endl;
cout << str.capacity() << endl;
str.append(11, 'b');
cout << str.capacity() << endl; // 当迭代器又申请了一段空间后,原来的迭代器失效,需要再次使用begin函数
ite = str.begin();
ite[3] = 'z';
}
// string迭代器函数
void testStringIterator()
{
string str1("abc");
string str2("def");
// 尾部插入
str1.append(str2);
cout << str1 << endl;
str1.append(str2.begin()+1,str2.end());
cout << str1 << endl;
// 删除元素
str1.erase(2,5); // 删除第2-5个元素
str1.erase(str1.begin() + 2); // 删除指定位置元素
cout << str1 << endl;
// 插入元素
string str3("zzzzzzz");
string str4("aaaaaaa");
str4.insert(str4.begin() + 2, str3.begin() + 2, str3.begin() + 4); // 在str4的第二个位置开始插入str3字符串第2-4位置的字符
cout << str4 << endl;
}
//
void fun(char c) // 参数的类型是根据容器元素的类型决定的
{
cout << c << ' ' ;
}
// string与算法
void funForeach()
{
string str("qwert");
for_each(str.begin(), str.end(), fun); // 前两个参数是遍历的开始与结尾,第三个参数是执行的操作
cout << endl;
sort(str.begin(), str.end()); // 默认从小到大排序
for_each(str.begin(), str.end(), fun);
cout << endl;
sort(str.begin(), str.end(),greater<char>());// 加上第三个参数就是从大到小排序(greater头文件是functional)
for_each(str.begin(), str.end(), fun);
}
int main()
{
// 定义
//strDefine();
// 属性
//strProperty();
// 输出
//strCout();
// 修改
//strChange();
// 其他函数
//strFunction();
// 迭代器
//funIterator();
// string迭代器的使用
//testStringIterator();
// string与算法
funForeach();
return 0;
}
2. vector 数组
STL中的数组就是vector,
向量:内存分配原理跟我们的string是一样的,是连续的空间,空间不够用时,会重新申请一个更大的连续空间,同时原迭代器失效。
头文件:#include<vector>
定义向量的对象:vector<参数列表> vec
参数列表是容器数据的类型,可以是基本的数据类型,比如结构体、指针对象等等。
注: 对于容器中的元素增、改、查只能是对于存在的元素,否则程序会崩溃。用下标运算只能查找和修改已存在的元素,不能进行赋值,如果要添加新的元素只能通过append等函数操作。
vector容器的初始容量capacity()定义对象的时候初始化几个就是几个,无参数就初始化为0;
如果添加元素后空间不够了,则添加现有容量的一半,现有十个就添加五个,现有十三个就添加六个,奇数向下取整。根据编译器的不同,在VC6.0中容量不够时每次增加现有容量的一倍。。
resize和reserve的区别: 比如一个500ml的瓶子,里面装了300ml的水,那么500就是capacity,300就是size;capacity是容量大小,不一定可以访问,而size是实际有的元素,可以直接访问的。reserve只能修改capacity大小,而resize既可以修改capacity大小也可以修改size大小。
2.1 增删改查之增
尾部添加:push_back()函数。
中间添加:
(1)在指定迭代器的位置添加一个数据
(2)在某个迭代器的某个位置添加num个值为value的元素
(3)在某个迭代器的某个位置加入另一个向量中的一段
数组尾部添加的效率非常高,因为它不考虑重新增加新的空间,但是再中间添加的效率非常低。
2.2 增删改查之删
(1)删除最后一个元素:pop_back()
(2)删除指定位置元素
(3)删除某一段元素
2.3 增删改查之改
下标运算可以修改元素
复制函数assign,这个函数会清除以前的元素重新赋值。
具体的实现代码如下所示:
#include<iostream>
#include<vector>
#include<ctime>
#include<algorithm>
using namespace std;
// 定义vector容器
void vecConstructure() // 构造函数
{
vector<int> vec1;
vector<int> vec2(5); // 注意:定义vector容器容量的时候用小括号( ),并且系统将其初始化为0
for (int i = 0; i < 5; i++)
{
cout << vec2[i] << endl;
}
vector<int> vec3(5, 12); // 初始化为5个12
for (int i = 0; i < 5; i++)
{
cout << vec3[i] << endl;
}
vector<char> vec4(5, 'a'); // 初始化为5个a
for (int i = 0; i < 5; i++)
{
cout << vec4[i] << endl;
}
vector<char> vec5(vec4); // 用一个vector对象初始化另一个vector对象,类似于拷贝构造函数,注意类型一定相同
for (int i = 0; i < 5; i++)
{
cout << vec5[i] << endl;
}
// 定义迭代器
vector<int>::iterator vec;
vector<int>::iterator vec6 = vec3.begin(); // 头迭代器
vector<int>::iterator vec7 = vec3.end(); // 尾迭代器
vector<int> vec8(vec6, vec7);
for (int i = 0; i < 5; i++)
{
cout << vec8[i] << endl;
}
}
// 定义vector容量
void vecCapacity()
{
vector<int> vec1;
cout << vec1.capacity() << endl;
vector<int> vec2(5);
cout << vec2.capacity() << endl;
vec2.push_back(1); // 5 + 5/2 = 7
cout << vec2.capacity() << endl;
vec2.push_back(1); // 7个容量够用
vec2.push_back(1); // 7 + 7/2 = 10
cout << vec2.capacity() << endl;
vec2.reserve(12); // reverse修改容量大小,只能变大不能变小,与string不同的是string的reverse函数
// 每次增加16个空间,而vector的reverse修改成多少就是多少(比原有空间大才有效)
cout << vec2.capacity() << endl;
vector<int> vec3(10);
vector<int>::iterator vec; //
vec = vec3.begin();
cout << vec[2] << endl;
vec3.reserve(12); // 扩大空间后迭代器失效
//vec = vec3.begin();
cout << vec[2] << endl;
}
// 函数测试
void testFun()
{
vector<int> vec(4);
vec.reserve(10); // 修改容量,初始化空间的元素默认赋值为0,用reverse函数扩大的空间没有值
cout << vec.size() << endl; // size函数输出的是有效元素个数
cout << vec.capacity() << endl; // 输出容器容量大小
cout << vec[2] << endl;
vector<int> vec1(5);
vec1.resize(12); // 从新设置元素个数,容量不变。
cout << vec1.size() << endl; // size函数输出的是有效元素个数
cout << vec1.capacity() << endl; // 输出容器容量大小
cout << vec1.empty(); // 判断对象是否为空,如果是空则返回1,如果不为空则返回0;
}
void fun(int a)
{
cout << a << ' ';
}
// vector的输出
void vecOut()
{
vector<int> vec;
for (int i = 0; i < 10; i++)
{
vec.push_back(i); // push_back就是在对象后面添加一个元素i
}
// 输出连续元素
for (int i = 0; i < 10; i++)
{
cout << vec[i] << ' ' << endl;
cout << vec.at(i) << ' ' << endl;
}
// 输出最后一个元素
cout << vec.back() << ' ' << endl;
// 用迭代器输出
vector<int>::iterator ite = vec.begin();
for (ite; ite != vec.end(); ite++)
{
cout << *ite << ' '; // 迭代器iterator使用方法类似指针
}
cout << endl;
// for_each输出
for_each(vec.begin(), vec.end(), fun);
}
// vector元素的增加与删除
void vecAdd()
{
vector<int> vec;
for (int i = 0; i < 10; i++)
{
vec.push_back(i); // 注意:back函数是输出最后一个元素,push_back函数是在尾部添加元素,pop_back函数是在尾部删除元素
}
vec.insert(vec.begin() + 2, 12); // 在某个位插入元素
vector<int>::iterator vec1;
vec1 = vec.begin(); // 迭代器一定要初始化
for (vec1; vec1 != vec.end(); vec1++)
{
cout << *vec1 << ' ';
}
cout << endl;
vec.insert(vec.begin() + 2, 5, 12); // 在某个位置插入多个元素
vec1 = vec.begin(); // 迭代器元素增加后一定要再次初始化
for (vec1; vec1 != vec.end(); vec1++)
{
cout << *vec1 << ' ';
}
cout << endl;
vector<int> vec2(5, 1); // 将vec2初始化为5个1
vec.insert(vec.begin() + 3, vec2.begin(), vec2.begin() + 3);
vec.pop_back(); // 删除最后一个元素
vec1 = vec.begin(); // 迭代器元素增加后一定要再次初始化
for (vec1; vec1 != vec.end(); vec1++)
{
cout << *vec1 << ' ';
}
cout << endl;
vector<int> vec3;
for (int i = 0; i < 10; i++)
{
vec3.push_back(i);
}
vec3.erase(vec3.begin() + 2); // 删除指定位置元素
vector<int>::iterator v1;
v1 = vec3.begin(); // 迭代器元素个数变化后一定要再次初始化
for (v1; v1 != vec3.end(); v1++)
{
cout << *v1 << ' ';
}
cout << endl;
vec3.erase(vec3.begin() + 2, vec3.begin() + 6); // 删除某一段元素
v1 = vec3.begin(); // 迭代器元素个数变化后一定要再次初始化
for (v1; v1 != vec3.end(); v1++)
{
cout << *v1 << ' ';
}
// 交换两个对象的元素
vec.swap(vec3);
}
// vector函数测试
void vecFunction()
{
vector<int> vec;
vec.push_back(5);
vec.push_back(2);
vec.push_back(12);
vec.push_back(3);
vec.push_back(1);
vec.push_back(8);
vec.push_back(6);
vector<int>::iterator vec1;
vec1 = vec.begin();
for (vec1; vec1 != vec.end(); vec1++)
{
cout << *vec1 << ' ';
}
cout << endl;
sort(vec.begin(), vec.end()); // 默认排序从小到大
sort(vec.begin(), vec.end(), greater<int>()); // 加上第三个参数排序从大到小
vec1 = vec.begin();
for (vec1; vec1 != vec.end(); vec1++)
{
cout << *vec1 << ' ';
}
cout << endl;
// 随机打乱顺序函数
vector<int> vec2;
for (int i = 0; i < 6; i++)
{
vec2.push_back(i);
}
for_each(vec2.begin(), vec2.end(), fun);
cout << endl;
srand((unsigned int)time(0));
random_shuffle(vec2.begin(), vec2.end()); // 随机打乱顺序,和随机函数类似,我们需要设置一个种子
for_each(vec2.begin(), vec2.end(), fun);
}
int main()
{
// 定义vector容器
//vecConstructure();
// 定义vector容量
//vecCapacity();
// 函数测试
//testFun();
// vector输出
//vecOut();
// vector的增加
//vecAdd();
// vector函数测试
vecFunction();
return 0;
}
3. 链表
双向链表: 比单向链表多了个指向前方的指针。
与vector的区别: vector是数组,随机访问快,尾部添加元素快,不支持快速插入和删除,插入和删除操作速度非常慢。list随机访问慢,支持快速插入和删除。
定义list的对象: list<参数列表> ls;
参数列表可以是指针、结构体…,这里我们程序用结构体演示。
构造函数:
- 定义多个元素时,系统默认初始化为0
- 用一个list对象给另一个list对象初始化
- 用一个list的一段给另一个list初始化(注意list是链表,不支持通过下标偏移移动指针,只支持自加)
定义list的迭代器:
形式:list<参数列表>::iterator ite; 需要注意的是和string相比,list迭代器多了参数列表,string没有参数列表。其实本质就是参数列表类型的指针。
list的迭代器不能进行加法运算,但是可以自加。
属性:
没有容量的概念,链表对空间利用率很高,需要一个节点就创建一个节点。
大小:用size()来输出元素个数;resize()来重新设置元素个数;用empty()可以判断是否有元素。
list的增删改查:
(1)list的增删改查之查:
- 输出循环:输出全部可以用for_each()。输出单个元素可以通过back()返回最后一个元素,通过front()返回第一个元素。
(2)list的增删改查之增
- 头部添加:push_front()
- 尾部添加:push_back()
- 中间添加:在指定迭代器位置添加一个数据;在指定迭代器位置添加一段值为value的数据;在指定迭代器位置添加另一个迭代器的一段。
(3)list的增删改查之删
- 头部删除:pop_front()
- 尾部删除:pop_back()
- 删除指定元素:iterator erase(iterator loc)可以删除单个元素,iterator erase(iterator start, iterator end)删除一段元素。
- 删除所有元素:clear()函数。
- remove()函数可以删除所有跟参数相同的元素。
- unique()函数可以删除链表中重复的元素
(4)list的增删改查之改
- assign()函数可以用来给list列表赋值n个相同的值,也可以用另一个链表的一段给list链表赋值(注: 用assign给list赋值的时候会先把原来所有的元素删除)。
- 也可以直接用 = 将一个链表赋值给另一个链表
- swap()函数用于交换两个链表的值
- reverse()函数用于将list中的元素倒转
- sort()函数用于将list中的元素排序(注: 如果参数是结构体类型则无法直接排序,需要我们在结构体中进行重载)
- merge()函数可以将两个链表合并,注意合并前两个链表必须是有序的,并且需要重载比较运算符(< 或 >),如果合并之前两个链表是从大到小排列的,则结构体中需要重载 < ,否则需要重载 > 。
- find()是查找函数,找到后返回值是一个迭代器(当指针使用即可),注意,如果find函数没有找到相关值得话,迭代器返回无效,输出迭代器相关的元素会崩溃。
上述函数的具体实现方法如下:
#include<iostream>
#include<list>
#include<algorithm>
using namespace std;
struct Node
{
int a;
char c;
//Node(int d,char e)
//{
// this->a = d;
// this->c = e;
//}
};
struct Node1
{
int a;
char c;
Node1(int d,char e)
{
this->a = d;
this->c = e;
}
bool operator == (const Node1& p)
{
if (p.a == this->a && p.c == this->c)
{
return true;
}
return false;
}
bool operator < (const Node1& p)
{
if (p.a < this->a)
{
return true;
}
return false;
}
};
void fun(Node& d) // 引用类似于一个对象
{
cout << d.a << ' ' << (int)d.c << endl;
}
void fun1(Node1& d) // 引用类似于一个对象
{
cout << d.a << ' ' << (int)d.c << endl;
}
// 定义list容器
void listDefine()
{
list<struct Node> ls1(6); // 定义一个list对象
for_each(ls1.begin(), ls1.end(), fun);
cout << endl;
Node node = { 12,'a' };
list<struct Node> ls2(6, node); // 第一个参数是元素的个数,第二个是元素的对象
for_each(ls2.begin(), ls2.end(), fun);
cout << endl;
list<struct Node> ls3(ls2); // 用一个list为另一个list初始化
for_each(ls3.begin(), ls3.end(), fun);
cout << endl;
list<struct Node>::iterator ite;
ite = ls2.begin(); // list是链表,链表不是连续的空间,地址偏移多个没有意义,只支持自加
ite++;
ite++;
list<struct Node> ls4(ite, ls2.end()); // 用另一个对象中的一段给list对象初始化
for_each(ls4.begin(), ls4.end(), fun);
cout << endl;
ite = ls1.begin();
ite++;
//ite = ite + 2; // list是链表,链表不是连续的空间,地址偏移两个没有意义
}
// list函数测试
void listFunction()
{
Node node = { 12,'a' }; // 定义结构体元素的值
list<Node> ls(6, node); // 定义6个节点
for_each(ls.begin(), ls.end(), fun);
cout << ls.size() << endl;
ls.resize(10); // 如果修改的比原来小则减少ls中的元素,如果修改的比原来多则将扩充的初始化为0
for_each(ls.begin(), ls.end(), fun);
cout << ls.size() << endl;
ls.empty();
}
// list的输出和增加
void listOutAdd()
{
Node node = { 12, 'a' };
//Node node (12, 'a' );
list<Node> ls1(6, node);
//cout << ls1.back().a << ' ' << ls1.back().c << endl; // 输出最后一个元素
//cout << ls1.front().a << ' ' << ls1.front().c << endl; // 输出第一个元素
//for_each(ls1.begin(), ls1.end(), fun);
list<Node> ls2;
Node no = { 16,'c' };
ls2.push_front(no); // 头部添加
ls2.push_back(no); // 尾部添加
no = { 12,'p' };
ls2.push_front(no); // 头部添加
for_each(ls2.begin(), ls2.end(), fun);
cout << endl;
no = { 22,'z' };
list<Node>::iterator ite;
ite = ls2.begin();
ite++;
ls2.insert(ite, no); // 在第二个位置插入节点
for_each(ls2.begin(), ls2.end(), fun);
ite = ls2.begin();
ite++;
ite++;
ls2.insert(ite, 3, no); // 在第三个位置插入3个节点
cout << endl;
for_each(ls2.begin(), ls2.end(), fun);
}
// list的删除和修改
void listDelChange()
{
list<Node1> ls;
ls.push_back(Node1(18, 'k')); // 为结构体添加构造函数后,可以用这种方法为list对象添加元素
ls.push_front(Node1(19, 'k'));
ls.push_back(Node1(20, 'k'));
ls.push_back(Node1(18, 'k'));
ls.push_front(Node1(36, 'k'));
ls.push_front(Node1(18, 'k'));
ls.push_back(Node1(20, 'k'));
ls.push_back(Node1(18, 'k'));
ls.push_front(Node1(36, 'k'));
ls.push_front(Node1(18, 'k'));
//for_each(ls.begin(), ls.end(), fun1);
//cout << endl;
//ls.pop_back(); // 头删除
//ls.pop_front(); // 尾删除
//for_each(ls.begin(), ls.end(), fun1);
//cout << endl;
//list<Node1>::iterator ite;
//ite = ls.begin();
//ite++;
//ls.erase(ite); // 删除第二个元素
//ls.erase(--ls.end()); // 删除最后一个元素,end()函数指向的是最后一个元素的下一个位置
//for_each(ls.begin(), ls.end(), fun1);
//cout << endl;
//ite = ls.begin();
//ite++;
//ls.erase(ite, --ls.end()); // 删除一段元素
//for_each(ls.begin(), ls.end(), fun1);
//cout << endl;
//ls.clear(); // 删除所有元素
ls.remove(Node1(18, 'k')); // 涉及到结构体类型的比较一定哟啊函数重载
//for_each(ls.begin(), ls.end(), fun1);
cout << endl;
ls.push_back(Node1(18, 'k'));
ls.push_front(Node1(18, 'k'));
ls.unique(); // 删除list中重复的元素
//for_each(ls.begin(), ls.end(), fun1);
ls.assign(3, Node1(1, 'o')); // assign()函数用来赋值list列表,直接将原来的list清空,赋值新的
for_each(ls.begin(), ls.end(), fun1);
cout << endl;
list<Node1> ls1;
ls1.push_back(Node1(3, 'a'));
ls1.push_back(Node1(3, 'a'));
ls1.push_back(Node1(3, 'a'));
ls1.push_back(Node1(3, 'a'));
ls1.push_back(Node1(3, 'a'));
ls1.push_back(Node1(3, 'a'));
ls1.push_back(Node1(3, 'a'));
list<Node1>::iterator ite;
ite = ls1.begin();
ite++;
ite++;
ls.assign(ite,ls1.end()); // assign()函数,用一个链表的一段给另一个链表赋值(注;赋值前把原有链表清空)
for_each(ls.begin(), ls.end(), fun1);
}
// list的合并
void listMerge()
{
list<Node1> ls1;
ls1.push_back(Node1(1, 'a'));
ls1.push_back(Node1(5, 'a'));
ls1.push_back(Node1(3, 'a'));
ls1.push_back(Node1(4, 'a'));
ls1.push_back(Node1(2, 'a'));
ls1.push_back(Node1(6, 'a'));
list<Node1> ls2;
ls2.push_back(Node1(7, 'a'));
ls2.push_back(Node1(8, 'a'));
ls2.push_back(Node1(9, 'a'));
ls2.push_back(Node1(10, 'a'));
ls2.push_back(Node1(11, 'a'));
ls2.push_back(Node1(12, 'a'));
ls1.swap(ls2); // 交换两个list的内容
ls1.swap(ls2);
for_each (ls1.begin(), ls1.end(), fun1);
cout << endl;
for_each (ls2.begin(), ls2.end(), fun1);
ls1.reverse(); // 将list中的元素倒转
cout << endl;
for_each(ls1.begin(), ls1.end(), fun1);
ls1.sort(); // 因为排序的是结构体类型,系统无法直接比较,因此我们需要在结构体中进行重载
cout << endl;
for_each(ls1.begin(), ls1.end(), fun1);
ls1.sort();
ls2.sort();
ls1.merge(ls2); // 合并的两个链表一定是有序的,如果合并之前两个链表是从大到小的则需要重载< ,反之需要重载 >
cout << endl;
for_each(ls1.begin(), ls1.end(), fun1);
cout << endl;
for_each(ls2.begin(), ls2.end(), fun1);
}
// 查找函数
void listFind()
{
list<Node1> ls1;
ls1.push_back(Node1(1, 'a'));
ls1.push_back(Node1(2, 'a'));
ls1.push_back(Node1(3, 'a'));
ls1.push_back(Node1(4, 'a'));
ls1.push_back(Node1(5, 'a'));
ls1.push_back(Node1(6, 'a'));
ls1.push_back(Node1(7, 'a'));
list<Node1>::iterator ite = find(ls1.begin(), ls1.end(), Node1(3, 'a')); // find是一个算法,不需要对象调用,find函数返回的是一个迭代器
cout << ite->a << ' ' << ite->c << endl; // ite相当于一个指针,调用元素时需要用->
// 注: 如果find函数没有找到相关元素,则迭代器返回无效,输出的话程序会崩溃
}
int main()
{
// 定义list容器
//listDefine();
// list函数测试
//listFunction();
// list的输出和增加
//listOutAdd();
// list的删除和修改
//listDelChange();
// list的合并
//listMerge();
// 查找函数
listFind();
return 0;
}
4. 双端队列
- 内存比较
1. vector:连续的内存空间,类似于数组
2. list:离散的内存空间,类似于链表
3. deque:段连续空间(每一段512字节) - 功能比较
1. vector:插入效率低,随机访问效率高,不支持头添加,支持尾添加。
2. list:插入效率高,随机访问效率低,支持头添加和尾添加。
3. deque:随机插入、删除效率不高,支持随机访问(比vector慢因为要做堆跳转),迭代器结构复杂,会降低访问效率;支持头添加和尾添加。 - 使用选择
1. 随机访问频率高的用vector
2. 需要经常插入删除的用list
3. 需要随机访问+头添加的用deque,deque比vector多了一个头添加,比list多了一个随机访问,(deque支持下标访问和at()访问,但是效率不如vector高。支持内部插入和删除操作,但是效率不如list高)。 - 函数对比
1. 对比vector没有 capacity()和reserve()
2. 多了push_front() ,push_back() 和 pop_front() , pop_back() ;
3. 其他的和其他函数一样