文章目录
前言
- 参考黑马 c++ 笔记
- 案例代码都亲自测试过
Q&A
- 记录 使用过程中常犯错误:
- 容器中 size() 函数:unsigned int size();用作计算时:
(int)v.size() - 1
;最好转成有符号型,防止size = 0
时,计算结果为正无穷(坑)。
一、STL介绍
1.1 STL 诞生
- 长久以来,软件界一直希望建立一种可重复利用的东西
- C++的 面向对象 和 泛型编程思想,目的就是复用性的提升
- 大多情况下,数据结构和算法都未能有一套标准导致被迫从事大量重复工作
- 为了建立 数据结构和算法的一套标准,诞生了STL
1.2 STL 基本概念
- STL(Standard Template Library,标准模板库)
- STL从广义上分为:容器(container)、算法(algorithm)、迭代器(iterator)
- 容器和算法之间通过迭代器进行无缝连接。
- STL几乎所有的代码都采用了模板类或者模板函数
1.3 STL 六大组件
STL大体分为六大组件:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器
- 容器:各种数据结构,如vector、list、deque、set、map等用来存放数据
- 算法:各种常用的算法,如sort、 find、 copy、 for_ each 等
- 迭代器:扮演了容器与算法之间的 胶合剂。
- 仿函数:行为类似函数,可作为算法的某种策略。
- 适配器::一种用来修饰容器或者仿函数或迭代器接口的东西。
- 空间配置器:负责空间的配置与管理。
1.4 STL 容器、算法、迭代器
1.4.1 容器:置物之所也
STL容器就是将运用最广泛的一些数据结构实现出来
常用的数据结构:数组、链表、树、栈、队列、集合、映射表 等
容器分为序列式容器和关联式容器两种:
- 序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置
- 关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系
1.4.2 算法:解决某个具体问题
有限的步骤,解决逻辑或数学上的问题,这一门学科我们叫做算法(Algorithms)
算法分为:质变算法 和 非质变算法:
-
质变算法:是指运算过程中 会更改区间内的元素的内容。例如拷贝、替换、删除 等
-
非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值 等
1.4.3 迭代器:容器和算法之间粘合剂
提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。
每个容器都有自己专属的迭代器 eg: vector::iterator it = v.begin()
迭代器使用非常类似于指针,初学阶段我们可以先理解迭代器为指针。
种类 | 功能 | 支持运算 |
---|---|---|
输入迭代器 | 对数据的只读访问 | 只读,支持++、==、! = |
输出迭代器 | 对数据的只写访问 | 只写,支持++ |
前向迭代器 | 读写操作,并能向前推进迭代器 | 读写,支持++、==、! = |
双向迭代器 | 读写操作,并能向前和向后操作 | 读写,支持++、– |
随机访问迭代器 | 读写操作,可以以跳跃的方式访问任意数据,功能最强的迭代器 | 读写,支持++、–、[n]、 -n、<、<=、>、>= |
常用的容器中迭代器种类为 双向迭代器 和 随机访问迭代器
1.4.3 容器算法迭代器初识
代码1:利用迭代器遍历容器的三种方法
#include <vector>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
// for_each 的回调函数
// int 类型与vector一样
void myprint(int val)
{
cout << val << endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
vector<int> v;
v.push_back(12);
v.push_back(13);
v.push_back(14);
v.push_back(15);
// 方法一
vector<int>::iterator it_begin = v.begin(); // 指向第一个位置
vector<int>::iterator it_end = v.end(); // 指向最后一个元素的下一个元素 -> 取其值无意义
while(it_begin != it_end)
{
cout << *it_begin << endl;
it_begin++;
}
// 方法二
for(vector<int>::iterator it=v.begin();it!=v.end();it++)
{
cout << *it << endl;
}
// 方法三:依赖<algorithm>标准算法头文件
for_each(v.begin(),v.end(),myprint);
system("pause");
return 0;
}
代码2:迭代器访问数组成员
vector<person>::iterator it = v.begin();
- it:以指向person对象的指针形式 进行访问 eg:it->name;
- *it:以 person对象形式 进行访问 eg:it.name;
#include <vector>
#include <string>
#include <algorithm>
#include <iostream>
#include <ostream>
using namespace std;
// 自定义数据类型
class person
{
public:
person(string name="null",int age=18):name_(name),age_(age){}
string name_;
int age_;
};
void test()
{
// 存放对象本身
vector<person> v;
person p1("xiaohong",22);
person p2("xiaohua",23);
person p3("xiaolan",24);
person p4("xiaoqi",22);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
for(vector<person>::iterator it = v.begin();it!=v.end();it++)
{
cout << (*it).name_ <<" + "<< it->age_ << endl;
}
}
void test1()
{
// 存放对象指针
vector<person*> v;
person p1("xiaohong",22);
person p2("xiaohua",23);
person p3("xiaolan",24);
person p4("xiaoqi",22);
v.push_back(&p1);
v.push_back(&p2);
v.push_back(&p3);
v.push_back(&p4);
for(vector<person*>::iterator it = v.begin();it!=v.end();it++)
{
cout << (*it)->name_ <<" - "<< (*it)->age_ << endl;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
test();
test1();
system("pause");
return 0;
}
代码3:容器嵌套容器 - 相当于多维数组
void test2()
{
vector<vector<int>> big;
vector<int> small;
vector<int> small2;
vector<int> small3;
vector<int> small4;
for(int i = 0;i < 4;i++)
{
small.push_back(i + 1);
small2.push_back(i + 2);
small3.push_back(i + 3);
small4.push_back(i + 4);
}
big.push_back(small);
big.push_back(small2);
big.push_back(small3);
big.push_back(small4);
for(vector<vector<int>>::iterator it=big.begin();it!=big.end();it++)
{
for(vector<int>::iterator it2=(*it).begin();it2!=(*it).end();it2++)
{
cout << *it2 << " ";
}
cout << endl;
}
}
二、常用容器
2.1 string 容器
2.1.1 本质
string是C++风格的字符串,而string本质上是一个类
2.1.2 特点
-
string 类内部封装了很多成员方法 例如:查找find,拷贝copy,删除delete,替换replace,插入insert
-
string 管理 char* 所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责
【Tip:string 和 char* 区别】
- char* 是一个指针
- string 是一 个类,类内部封装了char*,管理这个字符串,是一个char* 型 的容器。
2.1.3 代码
转成 C 字符串 (char*)
const char* c_str ( ) const;
转成 c 格式的字符串,以 \0
结尾,由 char*
指针指向。
int main ()
{
char * cstr, *p;
string str ("Please split this phrase into tokens");
cstr = new char [str.size()+1]; //'\0'
strcpy (cstr, str.c_str());
// cstr now contains a c-string copy of str
p=strtok (cstr," ");
while (p!=NULL)
{
cout << p << endl;;
p=strtok(NULL," ");
}
delete[] cstr;
return 0;
}
string 迭代器
- string::iterator it1 = str.begin();
- string::reverse_iterator it2 = str.rbegin();
构造函数原型
string();
//创建一个空的字符串 例如: string str;string(const char* s);
//使用字符串 s 初始化string(const string& str);
//使用一个string对象初始化另一个string对象string(int n, char C);
//使用n个字符c初始化
int _tmain(int argc, _TCHAR* argv[])
{
string s1;
string s2 = "hello";
string s3 = s2;
string s4(9,'0');
cout << s1 << endl << s2 << endl << s3 << endl << s4 << endl;
system("pause");
return 0;
}
赋值操作函数
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 赋给当前字符串
int _tmain(int argc, _TCHAR* argv[])
{
string s1,s2;
s1 = "hello";
s2 = s1;
s1 = 'D';
s1.assign("Hello",3);
s1.assign(s2);
s2.assign(8,'A');
cout << s1 << endl << s2 << endl;
system("pause");
return 0;
}
字符串拼接函数
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个字符连接到字符串结尾
int _tmain(int argc, _TCHAR* argv[])
{
string s1,s2;
s1 += "hello";
s1 += 'D';
s2 += s1;
s1.append("word!");
s1.append("who are you!",7);
s2.append(s1);
s1.append(s2,3,5);
cout << s1 << endl << s2 << endl;
system("pause");
return 0;
}
字符串 查找和替换 函数
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个字符为字符串strstring& replace(int pos, int n,const char* s);
//替换从pos开始的n个字符为字符串s
int _tmain(int argc, _TCHAR* argv[])
{
string s1,s2;
int num;
s1 = "What's your Name? Are you OK?";
s2 = "you";
num = s1.find(s2,2); // num = s1.find(s2);
num = s1.find("you",2); // num = s1.find("you");
num = s1.find("your",2,3);
num = s1.find('s',2); // num = s1.find('s');
num = s1.rfind("you",2); // num = s1.rfind("you");
num = s1.rfind(s2,2); // num = s1.rfind(s2);
num = s1.rfind("your",2,3);
num = s1.rfind('s',2); // num = s1.rfind('s');
string s3 = "her";
s3 = s1.replace(7,4,s3);
s3 = s1.replace(7,4,"her");
cout << s1 << endl << num << endl;
system("pause");
return 0;
}
总结:
find
查找是从左往后,rfind
从右往左- find 找到字符串后返回查找的第一个字符位置,
找不到返回 -1
- replace在替换时,要指定从哪个位置起,多少个字符,替换成什么样的字符串
字符串 比较 函数
比较方式:
- 字符串比较是 按字符的ASCIl码 进行对比
-
int compare(const string &s) const;
// 与字符串s比较 -
int compare(const char *s) const;|
// 与字符串s比较
=
返回 0
>
返回 1
<
返回 -1
int _tmain(int argc, _TCHAR* argv[])
{
string s1 = "hello",s2 = "Hello";
string s3 = s1;
int num;
num = s1.compare(s2);
//num = s1.compare(s3);
cout << num << endl;
system("pause");
return 0;
}
总结:字符串对比主要是用于 比较两个字符串是否相等,判断谁大谁小的意义并不是很大。
字符串 存取 函数
string 中 单个字符存取 方式有两种
char& operator[](int n);
//通过 [ ] 方式取字符char&pat(int n);
//通过 at 方法获取字符
int _tmain(int argc, _TCHAR* argv[])
{
string s1 = "hello";
char c1 = s1.at(1);
for(int i = 0;i < s1.size();i++)
cout << s1[i] << " ";
s1[0] = 'H';
s1.at(1) = 'E';
cout << s1 << endl;
system("pause");
return 0;
}
字符串 插入、删除 函数
string& insert(int pos, const char* s);
//插入字符串string& insert(int pos, const string& str);
//插入字符串string& insert(int pos, int n, char c);
//在指定位置插入 n 个字符 cstring& erase(int pos, int n = npos);
/删除从 pos 开始的 n 个字符
int _tmain(int argc, _TCHAR* argv[])
{
string s1 = "Hello";
string s2 = "?? Tom: ";
s1.insert(5,",word!");
s1.insert(0,s2);
s1.erase(0,3);
cout << s1 << endl;
system("pause");
return 0;
}
总结:插入和删除的起始下标都是从 0 开始
字符串 子串 函数
原型:
std:string substr(const size_t_Off = 0U, const size_t_Count = 4294967295U) const
简写:
string substr(int pos =0, int n = npos) const;
// 返回由 pos 开始的 n 个字符组成的字符串
int _tmain(int argc, _TCHAR* argv[])
{
string s1 = "wujieem@qq.com";
string s2 = s1.substr(0,s1.find('@'));
string s3 = s1.substr(1); // 第二个参数可省略
cout << s2 << endl;
system("pause");
return 0;
}
2.2 vector 容器
2.2.1 本质
vector数据结构和数组非常相似,也称为 单端数组
vector 与普通数组区别:
- 不同之处在于 数组是静态空间,而 vector 可以动态扩展
动态扩展:
- 并不是在原空间之后续接新空间,而是 找更大的内存空间,然后将原数据拷贝新空间,释放原空间
2.2.2 特点
vector 容器的迭代器是支持 随机访问的迭代器
2.2.3 代码
构造函数原型
vector<T> v;
//采用模板实现类实现,默认构造函数vector(v. begin(), v.end());
//将 v [begin(), end()) (前闭后开) 区间中的元素拷贝给本身。vector(n, elem);
//构造函数将n个elem拷贝给本身。vector(const vector &vec);
//拷贝构造函数。新的 vector容量不一定相同
void printVector(vector<int> &v)
{
for(vector<int>::iterator it = v.begin();it != v.end();it++)
{
cout << *it;
}
cout << endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
vector<int> v1;
for(int i=0;i<10;i++)
v1.push_back(i);
printVector(v1);
// 迭代器初始化数组
vector<int> v2(v1.begin(),v1.end()-1);
printVector(v2);
// 用 m 个 相同元素初始化数组
vector<int> v3(5,189);
printVector(v3);
vector<vector<bool>> visited(3, vector<bool>(5, 0));
vector<int> v4(v1);
printVector(v4);
system("pause");
return 0;
}
赋值操作函数
vector& operator=(const vector &vec);
//重载等号操作符assign(beg, end);
//将[beg, end)区间中的数据拷贝赋值给本身。assign(n, elem);
//将n个elem拷贝赋值给本身。
void printVector(vector<int> &v)
{
for(vector<int>::iterator it = v.begin();it != v.end();it++)
{
cout << *it;
}
cout << endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
vector<int> v1;
for(int i=0;i<10;i++)
v1.push_back(i);
//printVector(v1);
vector<int> v2;
v2 = v1;
printVector(v2);
v2.assign(v1.begin(),v1.end()-2);
printVector(v2);
v2.assign(3,996);
printVector(v2);
system("pause");
return 0;
}
反向迭代器
v.rbegin()
v.rend()
可以反向遍历数组。
#include <iostream>
#include <vector>
using namespace std;
void main()
{
vector<int> res;
res.push_back(10);
res.push_back(20);
res.push_back(30);
res.push_back(40);
// 1. 反向迭代器遍历
for(vector<int>::reverse_iterator it = res.rbegin();
it != res.rend();it++)
cout << *it << endl;
// 2. 反向迭代器赋值
vector<int> res2(res.rbegin(), res.rend());
}
emplace 系列函数(emplace_back)
#include<iostream>
#include<vector>
#include<string>
using namespace std;
class Person{
private:
string _name; int _age;
public:
Person(string name,int age)
{
this->_age = age;
this->_name = name;
}
};
void main()
{
vector<Person> arr;
// 直接传入参数 - 没有临时对象构造
arr.emplace_back("wujiw",23);
// 先构造一个临时对象,再将该对象`拷贝`到数组
arr.push_back(Person("wujiw", 23));
}
容量、大小 操作函数
empty();
//判断容器是否为空capacity();
//容器的容量unsigned int size();
//返回容器中元素的个数resizp(int num);
//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除。resize(int num, elem);
//重新指定容器的长度为num;若容器变长,则以elem值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除。
int _tmain(int argc, _TCHAR* argv[])
{
vector<int> v1;
for(int i=0;i<10;i++)
v1.push_back(i);
cout << v1.empty() << endl; // 0
cout << v1.capacity() << endl; // 13
cout << v1.size() << endl; // 10
v1.resize(15,8); // 默认用 0 填充
printVector(v1);
v1.resize(5);
printVector(v1);
system("pause");
return 0;
}
插入、删除 操作函数
push_ back(ele);
//尾部插入元素elepop_ back();
/删除最后一个元素insert(const_iterator pos, ele);
//迭代器指向位置pos插入元素eleinsert(const_iterator pos, int count ,ele);
//迭代器指向位置pos插入count个元素eleerase(const_iterator pos);
//删除迭代器指向的元素erase(const_iterator start, const_iterator end);
//删除迭代器从start到end之间的元素clear();
//删除容器中所有元素
int _tmain(int argc, _TCHAR* argv[])
{
vector<int> v1;
//for(int i=0;i<10;i++)
//v1.push_back(i);
v1.push_back(1);
v1.push_back(2);
v1.pop_back();
v1.insert(v1.begin(),3);
printVector(v1);
v1.insert(v1.end(),3,4);
printVector(v1);
v1.erase(v1.begin());
printVector(v1);
v1.erase(v1.end()-1,v1.end());
printVector(v1);
v1.clear();
printVector(v1);
system("pause");
return 0;
}
数据存取 操作函数
-
at(int idx);
//返回索引 idx 所指的数据 -
operator[ ];
//返回索引 idx 所指的数据 -
front();
//返回容器中第一个数据元素 -
back();
//返回容器中最后一个数据元素
int _tmain(int argc, _TCHAR* argv[])
{
vector<int> v1;
for(int i=0;i<10;i++)
v1.push_back(i);
v1[3] = 8;
v1.at(4) = 9;
cout << v1.at(3) << endl;
cout << v1[4] << endl;
cout << v1.front() << endl;
cout << v1.back() << endl;
system("pause");
return 0;
}
互换容器 操作函数
swap(dec);
// 将 vec 与 本身的元素互换
int _tmain(int argc, _TCHAR* argv[])
{
vector<int> v1;
for(int i=0;i<10;i++)
v1.push_back(i);
printVector(v1);
vector<int> v2;
for(int i=0;i<10;i++)
v2.push_back(i+10);
printVector(v2);
v1.swap(v2);
printVector(v1);
printVector(v2);
system("pause");
return 0;
}
用 “ swap + 匿名对象 ” 收缩内存:
int _tmain(int argc, _TCHAR* argv[])
{
vector<int> v1;
for(int i=0;i<1000;i++)
v1.push_back(i);
cout << v1.capacity() << " " << v1.size() <<endl; // 1066 100
v1.resize(3);
cout << v1.capacity() << " " << v1.size() <<endl; // 1066 3
// 收缩内存
vector<int>(v1).swap(v1); // 此处 匿名对象容量、大小为 3 , 3
cout << v1.capacity() << " " << v1.size() <<endl; // 3 3
system("pause");
return 0;
}
【注】匿名对象只在当前行有效;当前行结束就释放。
预留空间 操作函数
reserve(int len);
// 容器预留 len 个元素长度,预留位置不初始化,元素不可访问。
问题:
int _tmain(int argc, _TCHAR* argv[])
{
vector<int> v1;
int *p = NULL;
int num = 0;
for(int i=0;i<10000;i++)
{
v1.push_back(i);
if(p != &v1[0]){
p = &v1[0];
num++; // 重新分配地址次数
}
}
cout << num << endl;// 不预留空间24次 - 开销很大
system("pause");
return 0;
}
解决:
vector<int> v1;
v1.reserve(10000); // 预留空间
···
cout << num << endl;// 1次
总结: 如果数据量较大,可以一开始利用 reserve 预留空间
2.3 deque 容器
2.3.1 认识
双端数组,可以对头端进行插入删除操作
deque与vector区别:
vector
对于头部的插入删除效率低,数据量越大,效率越低deque
相对而言,对头部的插入删除速度回比vector
快vector
访问元素时的速度会比deque
快。这和两者内部实现有关【vector 是一段连续空间】
2.3.2 特点
deque
内部有个中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据。
中控器维护的是每个缓冲区的地址 [ 访问时先找到段缓冲区的地址,再找到具体数据(慢) ],使得使用 deque
时像一片连续的内存空间
PS:deque 容器的迭代器也是支持随机访问的。
2.3.3 代码
构造函数操作函数
deque<T> deqT;
//默认构造形式deque(beg, end);
//构造函数将[beg, end)区间中的元素拷贝给本身。deque(n, lelem);
//构造函数将n个elem拷贝给本身。deque(const deque &deq);
//拷贝构造函数
void printDeque(const deque<int> &v)
{ // const deque 要用 const_iterator 访问
for(deque<int>::const_iterator it = v.begin();it != v.end();it++)
{
cout << *it << " ";
}
cout << endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
deque<int> d1;
for(int i=0;i<10;i++)
{d1.push_back(i);}
printDeque(d1);
deque<int> d2(d1.begin(),d1.end()-1);
printDeque(d2);
deque<int> d3(4,19);
printDeque(d3);
deque<int> d4(d2);
printDeque(d4);
system("pause");
return 0;
}
注意: const deque 要用 const_iterator 访问。
const deque &v;
deque::const_iterator it;
赋值操作函数
deque& operator=(const deque &deq);
// 重载等号操作符assign(beg,end);
// 将[beg, end)区间中的数据拷贝赋值给本身。assign(n, elem);
// 将n个elem拷贝赋值给本身。
int _tmain(int argc, _TCHAR* argv[])
{
deque<int> d1;
for(int i=0;i<10;i++)
{d1.push_back(i);}
printDeque(d1);
deque<int> d2;
d2 = d1;
printDeque(d2);
d2.assign(d1.begin(),d1.end()-2);
printDeque(d2);
d2.assign(3,19);
printDeque(d2);
system("pause");
return 0;
}
总结:赋值 和 vector一样
大小操作函数
deque.empty();
// 判断容器是否为空deque.size();
// 返回容器中元素的个数deque.resize(num);
// 重新指定容器的长度为num,若容器变长,则以默认值填充新位置。
// 如果容器变短,则末尾超出容器长度的元素被删除。deque.resize(num, elem);
// 重新指定容器的长度为num,若容器变长,则以elem值填充新位置。
// 如果容器变短,则末尾超出容器长度的元素被删除。
【区别】没有容量的概念,不够了就去开辟,只要将地址添加到中控器中。
int _tmain(int argc, _TCHAR* argv[])
{
deque<int> d1;
for(int i=0;i<10;i++)
{d1.push_back(i);}
printDeque(d1);
cout << d1.empty() << endl;
cout << d1.size() << endl;
d1.resize(20);
printDeque(d1);
d1.resize(5);
printDeque(d1);
system("pause");
return 0;
}
插入、删除 操作函数
两端插入操作:
push_back(elem);
//在容器尾部添加一个数据push_front(elem);
//在容器头部插入一个数据pop_back();
//删除容器最后一个数据pop_front();
//删除容器第一个数据
指定位置操作:
insert(pos,elem);
//在pos位置插入一个elem元素的拷贝,返回新数据的位置。insert(pos,n,elem);
//在pos位置插入n个elem数据,无返回值。insert(pos, beg, end);
//在pos位置插入 [beg,end) 区间的数据,无返回值。clear(p);
//清空容器的所有数据erase(beg, end);
//删除 [beg,end) 区间的数据,返回下一个数据的位置。erase(pos);
//删除pos位置的数据,返回下一个数据的位置。
总结:插入和删除提供的位置是迭代器。
数据存取 操作函数
at(int idx);
//返回索引 idx 所指的数据operator[];
//返回索引 idx 所指的数据front();
//返回容器中第一个数据元素back();
//返回容器中最后一个数据元素
数据排序 操作函数
sort(iterltor beg, iterator end);
//对 beg 和 end 区间内元素进行排序
int _tmain(int argc, _TCHAR* argv[])
{
deque<int> d1;
d1.push_back(2);
d1.push_back(1);
d1.push_back(4);
d1.push_back(3);
d1.push_back(5);
printDeque(d1);
// 排序
sort(d1.begin(),d1.end());
printDeque(d1);
system("pause");
return 0;
}
对于 支持随机访问的迭代器 的容器,都可以利用sort算法直接对其进行排序。
2.4 案例:评委打分
#include <iostream>
using namespace std;
#include <vector>
#include <algorithm>
#include <string>
#include <deque>
#include <ctime>
class student {
public:
student(string name,int score){
this->name_ = name;
this->score_ = score;
}
string name_;
int score_;
};
// 为 vector 创建 student 对象
void createStudent(vector<student> &vs) {
string num = "ABCDE";
string name = "Student";
int score = 0; //初始分数 0
for (int i = 0; i < 5; i++) {
student s1(name+num[i],score);
vs.push_back(s1);
}
}
// 打印 student 内容
void printvector(const vector<student> &v)
{
for (vector<student>::const_iterator it = v.begin(); it != v.end(); it++)
{
cout << it->name_ <<" "<< it->score_ <<endl;
}
}
void setscore(vector<student> &vs) {
// 为每个学生打分、求平均分
for (int j = 0; j < vs.size(); j++)
{
deque<int> scores;
int all_num = 0;
// 10个评委打分
for (int i = 0; i < 10; i++)
{
int score = rand() % 41 + 60; // 60 - 100;
scores.push_back(score);
}
// 从小到大排序
sort(scores.begin(), scores.end());
for (deque<int>::iterator it = scores.begin(); it != scores.end(); it++)
{
cout << *it << " ";
}cout << endl;
// 去除最高、最低分
scores.pop_back();
scores.pop_front();
// 求和
for (deque<int>::iterator it = scores.begin(); it != scores.end(); it++)
{
all_num += *it;
//cout << score << endl;
}
// 设置平均分
vs[j].score_ = all_num/scores.size();
}
}
int main()
{
// 随机数种子
srand((unsigned int)time(NULL));
// 创建5个学生对象
vector<student> vs;
createStudent(vs);
setscore(vs); // 打分
printvector(vs);
return 0;
}
2.5 stack 容器
2.5.1 初始
stack 是一种 先进后出(First In Last Out,FILO) 的数据结构,它只有一个出口
2.5.2 特点
- 栈中只有顶端的元素才可以被外界使用,因此栈不允许有遍历行为
2.5.3 代码
构造函数
stack<T> stk;
//stack采用模板类实现,stack对象的默认构造形 式stack(const stack &stk);
//拷贝构造函数
赋值操作
stack& operator=(const stack &stk);
//重载等号操作符
数据存取
push(elem);
//向栈顶添加元素pop();
//从栈顶移除第一个元素top();
//返回栈顶元素
大小操作
empty();
//判断堆栈是否为空size();
//返回栈的大小
#include <stack>
void main()
{
stack<int> s1,s2;
s1.push(100);
s1.push(200);
s1.push(300);
s1.push(400);
s2 = s1;
cout << s1.size() << endl;
while (!s2.empty()) {
cout << s2.top() << endl;
s2.pop();
}
}
2.6 queue 容器
2.6.1 初识
Queue 是一种 先进先出(First In First Out,FIFO) 的数据结构,它有两个出口
2.6.2 特点
- 队列容器允许从一端新增元素,从另一端移除元素
- 队列中只有队头和队尾才可以被外界使用,因此队列不允许有遍历行为
2.6.3 代码
构造函数
queue<T> que;
//queue采用模板类实现,queue对象的默认构造形式queue(const queue &que);
//拷贝构造函数
赋值操作
queue& operator= (const queue &que);
//重载等号操作符
数据存取:
push(elem);
//往队尾添加元素pop();
//从队头移除第一个元素back();
//返回最后一一个元素front();
//返回第一个元素
大小操作:empty();
//判断队列是否为空size();
//返回队列的大小.
#include <queue>
void main()
{
queue<int> s1,s2;
s1.push(100);
s1.push(200);
s1.push(300);
s1.push(400);
s2 = s1;
cout << s1.size() << endl;
while (!s2.empty()) {
cout << s2.front() << " " << s2.back() << endl;
s2.pop();
}
}
2.7 list 容器(链表)
2.7.1 初识
链表(list) 是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的
-
链表的组成:链表由一系列结点组成
-
结点的组成:一个是存储数据元素的 数据域,另一个是存储下一个结点地址的 指针域
STL 中的链表是一个 双向循环链表
2.7.2 特点
由于链表的存储方式并不是连续的内存空间(不能通过++等随机访问),因此链表 list 中的迭代器只支持前移和后移(pre\next 指针访问),属于 双向迭代器。
优点:
- 采用动态存储分配,不会造成内存浪费和溢出
- 链表执行插入和删除操作快,修改指针即可,不需要移动大量元素
缺点:
-
容器遍历速度,没有数组快(指针域,有寻址等过程)
-
占用空间比数组大
-
链表灵活,但是空间 (指针域) 和时间 (遍历) 额外耗费较大。
List有一个重要的性质, 插入操作和删除操作都不会造成原有 list 迭代器的失效,这在vector是不成立的。
总结:STL中 List 和 vector 是两个最常被使用的容器,各有优缺点。
2.8 优先队列 priority_queue
-
在 优先队列中,元素被赋予优先级。优先队列具有 最高级先出(first in, largest out) 的行为特征。
-
优先队列 具有队列的所有特性,包括队列的 基本操作,只是 在这基础上添加了内部的一个排序,它 本质是一个堆实现的。
2.8.1 原型
#include<queue>
priority_queue<Type, Container, Functional>
-
Type 就是 数据类型
-
Container 就是 容器类型
Container 必须是用数组实现的容器,比如 vector,deque 等等,但 不能用 list。STL里面 默认用的是 vector
-
Functional 就是 比较的方式
2.8.2 使用方法
// 升序队列,小顶堆
priority_queue <int,vector<int>,greater<int> > q;
// 降序队列,大顶堆
priority_queue <int,vector<int>,less<int> > q;
- greater 和 less 是 std 实现的两个 仿函数。
2.8.3 内部方法
top 访问队头元素
empty 队列是否为空
size 返回队列内元素个数
push 插入元素到队尾 (并排序)
emplace 原地构造一个元素并插入队列
pop 弹出队头元素
swap 交换内容
2.8.4 用 pair 做优先队列元素
- pair 的比较规则:先比较第一个元素,第一个相等比较第二个
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
int main()
{
priority_queue < pair<int, int> > a;
pair<int, int> b(1, 2);
pair<int, int> c(1, 3);
pair<int, int> d(2, 5);
a.push(d);
a.push(c);
a.push(b);
while (!a.empty())
{
cout << a.top().first << ' ' << a.top().second << '\n';
a.pop();
}
}
ret:
2 5
1 3
1 2
2.8.5 自定义类型做优先队列元素
#include <iostream>
#include <queue>
using namespace std;
// 方法 1
struct tmp1 // 运算符重载 <
{
int x;
tmp1(int a) {x = a;}
bool operator< (const tmp1& a) const
{
return x < a.x; //大顶堆
}
};
//方法2
struct tmp2 //重写仿函数
{
bool operator() (tmp1 a, tmp1 b)
{
return a.x < b.x; //大顶堆
}
};
int main()
{
tmp1 a(1);
tmp1 b(2);
tmp1 c(3);
priority_queue<tmp1> d;
d.push(b);
d.push(c);
d.push(a);
while (!d.empty())
{
cout << d.top().x << '\n';
d.pop();
}
cout << endl;
priority_queue<tmp1, vector<tmp1>, tmp2> f;
f.push(b);
f.push(c);
f.push(a);
while (!f.empty())
{
cout << f.top().x << '\n';
f.pop();
}
}
3
2
1
3
2
1
2.9 array
- array 静态数组大小在定义时固定,位于 栈上。
#include<iostream>
#include<array>
//#include<tuple>
using namespace std;
int main()
{
std::array<double, 3> dbnew = { 10.1,10.2,10.3 };
for (int i = 0;i < 3;i++)
{
cout << dbnew[i] << endl;
}
}
2.10 tuple
- tuple 静态数组,包含不同的类型,类型固定后必须赋值为相应的类型
#include<iostream>
#include<tuple>
#include<string>
using namespace std;
void main()
{
tuple<string, int, double > mytuple("Guodong",18,99.9);
cout<<get<0>(mytuple)<<' '<< get<1>(mytuple) <<' '<< get<2>(mytuple);
cin.get();
}