文章目录
C++_STL
容器
包括string类,顺序容器,容器适配器,关联容器
string
创建与初始化
string a="adwdad";
string a[4] = {"twenty", "thirty", "forty", "fifty"};
//下面两行的写法是错的!!!
string b[4] = {'twenty', 'thirty', 'sdfse', 'dawd'};
string a='adwdad';
char s='a';
char s="a"; //这种写法是错的
char s[]="abcd"; //这个是字符串,以'\0'结束的,用strlen(s)是4,'\0'不计,但是它是有五个字符的
char s1[]={'a','b','c','d'}; //这个是字符数组,没有'\0',用strlen(s1)是不确定的,它有四个字符
char s='a';
操作
//获取字符串长度
int L =str.length();
int S =str.size(); //L=S 基本一样
//str去除元素
str.erase(pos,len); //str.erase(0,1); //删除pos开始的lin个字符
str.erase(a,b); //删除迭代器a和b标记范围内所有的元素,返回一个迭代器,指向被删除元素段后面的第一个元素
//string翻转
reverse(str.begin(),str.end());
//string插入元素
str.insert(pos,s2); //在下标为pos的元素之前插入string对象s2的副本
str.insert(pos,n,c); //在下标为pos的元素之前插入n个字符c
//子串
str.substr(pos,n); //返回一个string类型的字符串,它包含s中从下标pos开始的n个字符
str.substr(pos); //返回一个string类型的字符串,它包含从下标pos开始到s末为的所有字符
//查找
str.find(args); //在s中查找args的第一次出现
str.find(char c='a'); //在s中查找字符的第一次出现
str.find(string substr='abdb'); //在s中查找子字符串的第一次出现
str.rfind(args); //在s中查找args的最后一次出现
str.find_first_of(args); //在s中查args的任意字符的第一次出现
str.find_last_of(args); //在s中查找args的任意字符的最后一次出现
str.find_first_not_of(args); //在s中查找第一个不属于args的字符
str.find_last_not_of(args); //在s中查找最后一个不属于args的字符
//arg的各种情况:
c,pos //在s中,从下标pos标记的位置开始,查找字符c、pos的默认值是0
s2,pos //在s中,从下标pos标记的位置开始,查找string对象s2,pos的默认值为0
cp,pos //在s中从下标pos标记的位置开始,查找cp所指向的c风格的以空格结束的字符串。pos默认值为0
cp,pos,n //在s中,从下标pos标记的位置开始,查找指针cp所指向数组的前n个字符,pos和n都没有默认值
//返回的是元素下标:eg
size_t nLoc = str.find(args);
if (nLoc != string::npos) //未找到返回-1
cout << "nLoc is: " << nLoc << endl;
//比较compare
s.compare(s2); //比较s和s2
s.compare(pos1,n1,s2); //让s中从pos下标位置开始的n1个字符与s2做比较
s.compare(pos1,n1,s2,pos2,n2); //让s中从pos1下标位置开始的n1个字符与s2中从pos2下标位置开始的n2个字符做比较
s.compare(cp); //比较s和cp所指向的以空字符结束的字符串
s.compare(pos1,n1,cp); //让s从pos1下标位置开始的n1个字符与cp所指向的字符串做比较
s.compare(pos1,n1,cp,n2); //让s中从pos1下标位置开始的n1个字符与cp所指向字符串的前n2个字符做比较
//结果:
s1.compare(args);
//a.正数,此时s1大于args所代表的string对象
//b.负数,此时s1小于args所代表的string对象
//c.0,此时s1恰好等于args所代表的string对象
//追加append
s1 += s2;
s1.append(s2);
s1.append(s2, 5, 5); //取s2的第5个开始的5个字符追加给s1
s1.append(3, '!'); //s1尾部叠加3个!
常用代码块
//int转string
int a;
string str = to_string(static_cast<long long>(a));
//string转int
a=atoi(str.c_str());
//遍历string可以这样写,balabala
for(char c in s){
if(c=='a'){}
}
//字符范围判断相关代码块
for (size_t i = 0; i < str.size(); i++)
{
if( str[i]>='a' && str[i]<='z' ) //a to z
if( str[i]>='A' && str[i]<='Z' )
if( (str[i]>='a' && str[i]<='z') || (str[i]>='A' && str[i]<='Z') )
if( str[i]>='0' && str[i]<='9' )
}
//不等式
'a'<=str[i]<='z'//这样写是错的
str[i]>='a' && str[i]<='z' //这样才是正确语法
//等式
if(str[i]=' ') //这个写法错误
if(str[i]==' ') //这样才是正确语法
vector
特性
- 底层为连续数组,顺序结构存储
- 自动内存管理,动态改变长度,随着元素的添加和删除而增大与缩小
- 可以快速随机访问任意位置元素O(1)
- 尾部添加和删除元素的时间是固定的O(1)
- 头部或中间插入和删除元素的复杂度为O(n),需要移动其他元素
头文件
#include <vector>
定义与初始化
vector<int> Excel;
vector<int> Excel = {1,2,3,4};
vector<vector<int>> Excel = {{1,2},{1,2,3,4}};
vector<int> v(7,3); //指定值初始化,v被初始化为包含7个值为3的int
vector<int> v(5);
itoa(v.begin(), v.end, 2) //v初始化为{2,3,4,5,6}
操作
方法 | 说明 |
---|---|
T front() | 返回首元素的引用 |
back | 返回尾元素的引用 |
begin | 返回指向首元素的随机存取迭代器 |
end | 返回指向尾元素的下一个位置的随机存取迭代器 |
push_back | 表尾巴添加元素 |
emplace_back | 表尾巴添加元素(传入构造函数的参数) |
pop_back | 删除尾元素 |
insert | 插入 |
erase | 删除 |
size() | 元素个数 |
clear | 清空 |
empty | 判空 |
resize | |
swap | |
assign | |
//添加元素
Excel.push_back(nums);
vector<pair <int, string>> v;
v.emplace_back(3, "abc");
//去除元素
int nPosition = i;//元素脚标
Excel.erase(Excel.begin()+nPosition);
Excel.erase(Excel.end()-1); //注意::最后一个元素是Deriction.end()-1!
常用代码块
//--查找find
//查找的重载
class error
{
public:
string file_name;
int line;
int count;
/vector 的find函数,当vector中为自定义类型时,需要重载==操作,如下语句
bool operator == (const error & obj) const //重载 “==” 操作符,函数最后的 const 别忘了,否则会报错。(详见:http://www.cnblogs.com/SZxiaochun/p/7731900.html)
{
return file_name == obj.file_name && line == obj.line; //具体匹配条件,自己设定
}
};
//迭代器查找
error error_in;
vector<error> errorlist;
//注意!!!!vector的find函数的使用方法不是v.find()!!!!
vector<error>::iterator result = find( errorlist.begin( ), errorlist.end( ), error_in );
if ( result == errorlist.end( ) ) //没找到
errorlist.push_back(error_in);
else //查找到
{
int nPosition = distance(errorlist.begin(), result);
errorlist[nPosition].count++;
}
//--排序sort
//排序
sort(Excel.begin(),Excel.end());
//自定义类型的排序重载
//方式1
class Words
{
public:
string strwords;
int letter[26]={ 0 };
///自定义类型时,需要重载<操作,如下语句
bool operator < (const Words & obj) const //重载 “<” 操作符,函数最后的 const 别忘了,否则会报错。(详见:http://www.cnblogs.com/SZxiaochun/p/7731900.html)
{
return strwords < obj.strwords; //具体匹配条件,自己设定
}
};
sort(Excel.begin(),Excel.end());
//方式2
//或者自定义函数
bool cmp(Words a,Words b)
{
if(a.zfc<b.zfc)
return true;
else if(a.zfc==b.zfc)
return a.a<b.a;
else
return false;
}
sort(Deriction.begin(),Deriction.end()); //注意::字典一定要排序
sort(Deriction.begin(),Deriction.end().cmp);
//方式1
//其他方式
struct tree
{
string zfc;
int a;
//重载 “<” 操作符
bool operator<(const tree &a)
{
//自定义条件
if(zfc<a.zfc)
return true;
else if(zfc==a.zfc)
return this->a<a.a;
else
return false;
}
};
//方式2
//或者自定义函数
bool cmp(tree a,tree b)
{
if(a.zfc<b.zfc)
return true;
else if(a.zfc==b.zfc)
{
return a.a<b.a;
}
return false;
}
int main()
{
tree t[3];
t[0].zfc="hh";
t[0].a=1;
t[1].zfc="xx";
t[1].a=2;
t[2].zfc="hh";
t[2].a=0;
sort(t,t+3); //使用sort可以重载元素类型的比较符号
sort(t,t+3,cmp); //也可以自己写新的排序函数
}
//最大元素
max_element() //返回范围内最大元素的迭代器
vector<int>::iterator max_e = max_element(candies.begin(), candies.end());
int max_num = *max_e;
stack
特性
- 底层默认vector
- 不允许随机访问栈元素
- 不允许遍历队列,只允许访问栈顶元素
头文件
#include <stack>
创建
stack <int> stk;
//初始化
stack <int> stk = ;
操作
方法 | 说明 |
---|---|
bool empty()const | 判空 |
size_type size()const | 元素个数 |
T& top() | 返回栈顶元素的引用 |
void push(const T& x) | 栈顶插入元素 |
void pop() | 删除栈顶元素 |
stk.push(7); //入栈
stk.pop(); //出栈
stk.top(); //访问栈顶元素
stk.empty(); //判空
stk.size(); //元素个数
queue
头文件
#include <queue>
创建
queue <int> q;
//初始化
queue <int> q =;
操作
q.push(7); //入队
q.pop(); //出队列
q.front(); //访问队首元素
q.back(); //访问队尾元素
q.empty(); //判空
q.size(); //返回队中元素个数
dqueue(双端队列)
特性
- 头部或尾部添加和删除元素的时间是固定的O(1)
- 支持随机访问,可以访问任意位置元素,且比vector稍慢
- 中间插入和删除元素的复杂度为O(n),且比vector稍慢
queue(队列)
特性
- 底层默认为dqueue
- 不允许随机访问元素
- 不允许遍历队列,只允许访问队首和队尾元素
头文件
#include<queue>
创建
queue<int> q;
操作
方法 | 说明 |
---|---|
bool empty()const | 判空 |
size_type size()const | 元素个数 |
T& front() | 返回队首元素的引用 |
T& back() | 返回队尾元素的引用 |
void push(const T& x) | 队尾插入元素 |
void pop() | 删除队首元素 |
priority_queue
特性
- 底层默认为vector
- priority_queue支持的操作与queue一样
- priority_queue最大的元素被移动到队首,可以自定义比较大小的函数greater()
头文件
#include<queue>
创建
priority_queue<int> pq1;
priority_queue<int> pq1(greater<int>); //自定义比较大小的函数greater()
list(双向链表)
特性
- 访问元素较慢O(n),不支持随机访问
- 任意位置插入和删除的时间固定O(1),而vector为O(n)
头文件
#include <list>
创建
list<int> c0; //空链表
//初始化
list<int> c0{1,2,3,4,5};
list<int> c1(3); //建一个含三个默认值是0的元素的链表
list<int> c2(5,2); //建一个含五个元素的链表,值都是2
list<int> c4(c2); //建一个c2的copy链表
list<int> c5(c1.begin(),c1.end()); c5含c1一个区域的元素[_First, _Last)。
操作
c.begin(); //返回指向链表第一个元素的迭代器
c.end(); //返回指向链表最后一个元素之后的迭代器
1 list<int> a1{1,2,3,4,5};
2 list<int>::iterator it;
3 it = a1.begin();
4 cout << *it << endl;
c.rbegin() //返回逆向链表的第一个元素,即c链表的最后一个数据。
c.rend() //返回逆向链表的最后一个元素的下一个位置,即c链表的第一个数据再往前的位置。
c.front() //返回链表c的第一个元素。
c.back() //返回链表c的最后一个元素。
c.empty() //判断链表是否为空。
c.size() //返回链表c中实际元素的个数。
c.insert(pos,num) //在pos位置插入元素num。
c.insert(pos,n,num) //在pos位置插入n个元素num。
c.insert(pos,beg,end) //在pos位置插入区间为[beg,end)的元素。
c.erase(pos) //删除pos位置的元素。
c.push_back(num) //在末尾增加一个元素。
c.pop_back() //删除末尾的元素。
c.push_front(num) //在开始位置增加一个元素。
c.pop_front(); //删除第一个元素。
c.reverse(); //反转链表
c.unique(); //删除相邻的元素
c.sort() 将链表排序,默认升序
c.sort(comp) 自定义回调函数实现自定义排序
1 list<int> a1{1,3,2,5,4};
2 a1.sort();
3 list<int>::iterator it;
4 cout << "sort():";
5 for(it = a1.begin();it!=a1.end();it++){
6 cout << *it << " ";
7 }
8 cout << endl;
9
10 a1.sort([](int n1,int n2){return n1>n2;});
11 cout << "sort(function point):";
12 for(it = a1.begin();it!=a1.end();it++){
13 cout << *it << " ";
14 }
15 cout << endl;
自定义链表-struct结构
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
class Solution {
public:
void deleteNode(ListNode* node) {
node->val = (node->next)->val;
node->next = node->next->next;
}
};
map
特性
- 查找非常迅速 o(1)
- 支持随机访问
- 支持迭代器遍历
- 注意:map遍历的时候会自动根据key的大小排序!并不代表插入顺序!
头文件
#include<map>
创建
map<typename1, typename2> mp;
map<string, int> mp;
操作
//访问元素
map<string, int> mp;
mp["apple"] = 15;
mp["bnanana"] = 16;
//方式一:直接访问
cout<<mp["bnanana"]<<endl;
//方式二:.at()访问,更安全
cout<<mp.at("bnanana")<<endl;
//方式三:通过迭代器访问
//map 可以使用 itr->first 来访问键, itr->second 来访问值。
for(map<char, int>::iterator itr = mp.begin(); itr != mp.end(); itr++)
cout<< itr -> first
<< itr -> second<<endl;
//查找
find()//返回迭代器
map<string, int>::iterator itr = mp.find("bnanana");
cout<< itr->first << itr->second<<endl;
//删除
erase()
map<string, int>::iterator itr = mp.find("bnanana");
mp.erase(itr); //删除该迭代器所指元素
mp.erase(mp.begin(), itr); //删除最开始到itr所指元素前的所有元素,前闭后开
mp.erase(itr, mp.end()); //删除it之后的所有元素
mp.erase(mp.begin()); //删除最开始的元素
mp.erase(--mp.end()); //删除最后面的元素
//清空
mp.clear();
//大小
mp.size();
unordered_map
头文件
#include<unordered_map>
特性
- 支持遍历
- 注意:内部哈希表,遍历顺序并不代表插入顺序
map: map内部实现了一个红黑树(红黑树是非严格平衡二叉搜索树,而AVL是严格平衡二叉搜索树),红黑树具有自动排序的功能,因此map内部的所有元素都是有序的,红黑树的每一个节点都代表着map的一个元素。因此,对于map进行的查找,删除,添加等一系列的操作都相当于是对红黑树进行的操作。map中的元素是按照二叉搜索树(又名二叉查找树、二叉排序树,特点就是左子树上所有节点的键值都小于根节点的键值,右子树所有节点的键值都大于根节点的键值)存储的,使用中序遍历可将键值按照从小到大遍历出来。
unordered_map: unordered_map内部实现了一个哈希表(也叫散列表,通过把关键码值映射到Hash表中一个位置来访问记录,查找的时间复杂度可达到O(1),其在海量数据处理中有着广泛应用)。因此,其元素的排列顺序是无序的。
map:
优点:
有序性,这是map结构最大的优点,其元素的有序性在很多应用中都会简化很多的操作
红黑树,内部实现一个红黑书使得map的很多操作在lgn的时间复杂度下就可以实现,因此效率非常的高
缺点: 空间占用率高,因为map内部实现了红黑树,虽然提高了运行效率,但是因为每一个节点都需要额外保存父节点、孩子节点和红/黑性质,使得每一个节点都占用大量的空间
适用处:对于那些有顺序要求的问题,用map会更高效一些
unordered_map:
优点: 因为内部实现了哈希表,因此其查找速度非常的快
缺点: 哈希表的建立比较耗费时间
适用处:对于查找问题,unordered_map会更加高效一些,因此遇到查找问题,常会考虑一下用unordered_map
总结:
内存占有率的问题就转化成红黑树 VS hash表 , 还是unorder_map占用的内存要高。
但是unordered_map执行效率要比map高很多
对于unordered_map或unordered_set容器,其遍历顺序与创建该容器时输入的顺序不一定相同,因为遍历是按照哈希表从前往后依次遍历的
树
图
算法
头文件
#include <algorithm>
函数
去重
unique()
unique(A.begin(),A.end())
//unique将相邻的重复元素移动到末尾,并不是直接删除,
//注意"相邻的",所以要先进行排序,保证重复元素相邻
//unique() 的返回值就是被后置元素的“首地址”(迭代器)
重载运算符
operator==
operator!=
operator<
operator<=
operator>
operator>=
算法思想–递归
/*
递归问题
3个瓶子换1瓶水+1个空瓶子,两个瓶子换1瓶水+0个空瓶子,1个瓶子换0瓶水。
f(1) = 0
f(2) = 1
f(3) = 1
f(4) = f(2)+1 //4个瓶子,其中3个可以换1瓶水+1个空瓶,所以是f(2)+1
f(5) = f(3)+1 //3个瓶子换1瓶水+1个空瓶,所以是f(3)+1
...
f(n) = f(n-2)+1 */
int f(int n)
{
if(n==1) return 0;
if(n==2) return 1;
return f(n-2)+1;
}
int main()
{
int n;
while(cin >> n)
{
if(n==0)
break;
cout<<f(n)<<endl;
}
return 0;
}