常见数据结构
1 万能头文件
#include <bits/stdc++.h>
2 algorithm算法
2.1 头文件
#include <algorithm>
2.2 常用方法
//1、find:查找指定范围内的值,若没找到则返回end()
void test_find() {
vector<int> vc = {1, 3, 14, 15, 6};
vector<int>::iterator i = std::find(vc.begin(), vc.end(), 3);
std::cout << *i;
}
//2、cout:统计范围内出现的次数
void test_count() {
vector<int> vc = {1, 3, 3, 15, 15, 8};
int it = count(vc.begin(), vc.end(), 8);
std::cout << it << std::endl;
}
//3、swap:交换两个对象的值
v.swap(v1);
//4、reverse:指定范围反转
void test_reverse() {
vector<int> vc = {1, 3, 2, 9, 15, 8};
reverse(vc.begin(), vc.end());
for(auto& i:vc){
std::cout << i << " ";
}
}
//5、sort:排序
sort(v.begin(),v.end()); // 默认升序:
sort(v.begin(),v.end(),[](int a, int b) { //降序则调用:
return a > b});
struct Node //结构体重载,进行比较
{
int u, v;
bool operator < (const Node & m)const
{
//以u为第一关键字,v为第二关键字排序
return u == m.u ? v < m.v : u < m.u;
}
}
std::vector<std::pair<int, std::string>> vec = {{1, "a"}, {2, "b"}, {1, "c"}};
std::sort(vec.begin(), vec.end(), [](const auto& a, const auto& b) {
if(a.first != b.first) return a.first > b.first;
return a.second < b.second;
});
//6、set_union:求两个序列的并集
void test_set_union() {
vector<int> vc = {1, 3, 2, 9, 15, 8};
vector<int> vc2 = {1, 3, 2, 19, 11, 20};
vector<int> vc1;
vc1.resize(vc.size() + vc2.size());
auto rt = set_union(vc.begin(), vc.end(), vc2.begin(), vc2.end(), vc1.begin());
vc1.resize(rt-vc1.begin());
for(auto& i:vc1){
std::cout << i << " ";
}
}
1 3 2 9 15 8 19 11 20
//7、set_intersection:求两个集合的交集
void test_set_intersection () {
std::vector<int> vc = {1, 3, 2, 9, 15, 8};
std::vector<int> vc2 = {1, 3, 2, 19, 11, 20};
std::vector<int> vc1;
vc1.resize(vc.size() + vc2.size());
auto rt = std::set_intersection (vc.begin(), vc.end(), vc2.begin(), vc2.end(), vc1.begin());
vc1.resize(rt-vc1.begin());
for(auto& i:vc1){
std::cout << i << " ";
}
}
1 3 2
//8、max、min判断最大最小值
min(4, 1);
max(4, 1);
//9、minmax_element:获取序列中的最大最小元素
vector<int> vc = {1, 3, 2, 9, 15, 8};
auto rt = minmax_element(vc.begin(), vc.end());
std::cout << *rt.first << ", " << *rt.second << std::endl;
//10、next_permutation:全排列
vector<int> vc = {1, 3, 2};
sort(vc.begin(), vc.end());
cout << "全排列\n";
do {
std::cout << vc[0] << ' ' << vc[1] << ' ' << vc[2] << '\n';
} while ( std::next_permutation(vc.begin(), vc.end()));
全排列
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
//11、lower_bound:查找有序序列中第一个不小于给定值的元素。
vector<int> v = {1, 2, 4, 4, 5, 6};
auto it = std::lower_bound(v.begin(), v.end(), 4);
if (it != v.end()) std::cout << "Lower bound of 4: " << *it; // 输出:Lower bound of 4: 4
//12、upper_bound:查找有序序列中第一个大于给定值的元素。
vector<int> v = {1, 2, 4, 4, 5, 6};
auto it = std::upper_bound(v.begin(), v.end(), 4);
if (it != v.end()) cout << "Upper bound of 4: " << *it; // 输出:Upper bound of 4: 5
//12、binary_search:在有序序列中查找给定值。
vector<int> v = {1, 2, 3, 4, 5};
if (binary_search(v.begin(), v.end(), 3)) {
cout << "3 is found in the vector"; // 输出:3 is found in the vector
} else {
cout << "3 is not found in the vector";
}
//13、equal_range:在有序序列中查找等于给定值的元素的范围。
vector<int> v = {1, 2, 4, 4, 5, 6};
auto range = std::equal_range(v.begin(), v.end(), 4);
auto first = range.first;
auto last = range.second;
std::cout << "The equal range of 4: ";
for (auto it = first; it != last; ++it) std::cout << *it << ' '; // 输出:The equal range of 4: 4 4
3 vector
3.1 头文件
#include <vector>
3.2 构造vector容器
vector<int> v1; //构造空容器
vector<int> v2(10, 1); //构造一个int类型的容器,并初始化了10个1
vector<int> v3(v2); //将容器v2拷贝给v3
vector<int> v4(v3.begin(), v3.end()); // 迭代器拷贝构造
3.3 元素访问
//1、下标访问————v1[0]
vector<int> v1;
v1[0];
for(auto& x : v1) cout << x << ; //输出v1数组数据
//2、迭代器遍历————*it
vector<int>::iterator it;
for(it = v1.begin(); it != v1.end(); it++) {
cout << *it << endl; //输出v1数组数据
}
//3、at下标访问————at(i) at(i)得到编号i位置的数据,在函数越界时抛出一个异常
v1.at(i);
3.4 vector 操作
//1、元素个数
v.size();
//2、最大容量
v.capacity();
//3、改变元素个数
v.resize(n); //将v的大小改为n,新元素的值为该type的默认值
v.resize(n, value); //将v的大小改为n,并将新元素的值设为value
1.假设所给值小于当前容器size则size缩小至所给值
2.假设所给值大于当前容器size值并且小于capacity,size扩大至所给值,增加的数用0去补充
3.假设所给值大于当前容器size值并且大于capacity值,先扩容,之后再重复规则2
//4、改变容器容量
v.reserve(n);
1.假设所给值小于当前容量值什么都不会发生改变
2.假如所给值大于当前容容量值扩容至所给值
//5、判断是否为空
v.empty(); //1为空,0为非空
//6、清空容器
v.clear();
//7、插入元素
v.push_back(value);
v.emplace_back(value); //比push_back少了一次拷贝构造函数的调用,从而提高插入效率
1. push_back() 向容器尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素);
2. 而 emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程;
//8、删除最后一个值
v.pop_back();
//9、返回第一个元素
v.front();
//10、返回最后一个元素
v.back();
//11、插入一个元素
v.insert(v.begin(), value) //在迭代器位置前插入一个元素value
v.insert(v.begin(), n, value); //在迭代器位置前插入n个value
//12、删除一个元素
v.erase(v.begin()); //删除迭代器位置的内容
v.erase(v.begin(), v.begin() + 2); //删除迭代器位置区间的内容(左闭右开)
//13、查找元素值
vector<int>::iterator pos = find(v.begin(), v.end(), value);
//在迭代器位置区间查找value值,返回值为值所在迭代器位置
//14、交换两个容器的值
v.swap(v1);
//15、正向迭代器
v.begin() //第一个元素
v.end()
//16、反向迭代器
v.rbegin() //最后一个元素
v.rend() //
//17、迭代器失效
vector<int>::iterator it = find(v.begin(), v.end(), 2);
v.erase(v.begin());//删除头元素
it = find(v.begin(), v.end(), 2);//it迭代器失效,重新获取一下
1. 在使用迭代器之前对整个容器进行了增删改操作,这就是引起迭代器失效的原因
2.解决的方法也很简单,就是使用迭代器之前重新赋值一下就行。
4 list
4.1 头文件
#include <list> //底层实现是双向链表
4.2 定义链表
- 单向链表
//Definition for singly-linked list.
struct ListNode {
int val;
ListNode *next;
ListNode() : val(0), next(nullptr) {}
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode *next) : val(x), next(next) {}
};
class Solution {
public:
ListNode* sortList(ListNode* head) {
}
};
- 双向链表
//Definition for douplex-linked list.
struct ListNode {
int val;
ListNode *prev;
ListNode *next;
ListNode() : val(0), next(nullptr) {}
ListNode(int x) : val(x), prev(nullptr), next(nullptr) {}
ListNode(int x, ListNode *prev, ListNode *next) : val(x), prev(next), next(next) {}
};
class Solution {
public:
ListNode* sortList(ListNode* head) {
}
};
4.3 构造list容器
list<int> l; //声明一个空链表
list<int> l{1,2,3}; //声明一个含指定元素的链表
list<int> l(n); //声明一个含n个元素的链表并全部初始化为0
list<int> l(n, m); //声明一个含n个元素的链表并全部初始化为m
list<int> l(a,b) ; //声明一个链表并初始化为区间[a,b]中的元素,a、b是迭代器
4.4 元素访问
//删除链表重复元素2
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if (!head) {
return head;
}
ListNode* dummy = new ListNode(0, head);
ListNode* cur = dummy;
while (cur->next && cur->next->next) {
if (cur->next->val == cur->next->next->val) {
int x = cur->next->val;
while (cur->next && cur->next->val == x) {
cur->next = cur->next->next;
}
}
else {
cur = cur->next;
}
}
return dummy->next;
}
};
4.5 list 操作
增:
1、l.insert() 插入一个元素到list中
auto pos = ++L.begin();
// 在pos前插入值为4的元素
L.insert(pos, 4);
// 在pos前插入5个值为5的元素
L.insert(pos, 5, 5);
// 在pos前插入[v.begin(), v.end)区间中的元素
L.insert(pos, v.begin(), v.end());
2、l.push_back() 在list的末尾添加一个元素
l.push_back(i);
2.1、l.emplace_back() //不会创建临时元素,效率更高
l.emplace_back(i); //
l.emplace_back(); //不带参数会插入一个空元素
3、l.push_front() 在list的头部添加一个元素
L.push_front(i);
删:
1、l.clear() 清空list的所有元素
// 将l2中的元素清空
l2.clear();
2、l.erase() 删除一个元素
// 删除pos位置上的元素
L.erase(pos);
// 删除list中[begin, end)区间中的元素,即删除list中的所有元素
L.erase(L.begin(), L.end());
3、l.erase(l.begin(),l.end()) 将l从begin()到end()之间的元素删除。
4、l.pop_back() 删除最后一个元素
5、l.pop_front() 删除第一个元素
6、l.remove() 从list删除指定元素
l.remove(x);//删除与x相等的元素
7、l.remove_if() 按指定条件删除元素
l.remove_if(func); //func是函数名,容器元素依次传进去,删除返回true的元素
bool func(const string& value){
if(value == "last")
return true;
return false;
}
8、l.unique() 删除list中重复的元素
改:
1、l.resize() 改变list的大小
l.resize(2);
l.resize(10, b);
2、l.reverse() 把list的元素倒转
查:
1、l.front() 返回第一个元素
2、l.back() 返回最后一个元素
3、l.empty() 若list是空的则返回true,否则返回false
4、l.max_size() 返回list能容纳的最大元素数量
5、l.size() 返回list中的元素个数
其他操作:
1、l.assign() 给list赋值
l.assign(5,100); //将100赋值给前5个元素
l2.assign(l1.begin(), l1.end()); //将l1的元素赋值给l2
int arr []={10, 20, 30, 40};
list3.assign(arr, arr+4);
2、l.get_allocator() 返回list的配置器
3、l.merge() 合并两个list
list1.merge(list2)//将l2中的元素合并到l1中,并且l1和l2必须已经排号顺序,将一个有序list容器加入另一个有序list容器中,且两个list容器必须都为逆序或顺序,这是很容易不注意导致报错的主要原因。
4、l.sort() 给list排序
l.sort(); //默认升序排列
l.sort([](int a, int b) {//降序排列
return a > b;
});
5、l.splice() 合并两个list
list1.splice(it1, list2)//将list2中的所有元素拷贝到list1中。在list1中的起始位置是it1.复制结束后,list2将为空。
list1.splice(it1, list2, it2)//将list2中的元素,从it2开始,剪切到list1的it1起始的地方
list1.splice(it1, list2, it2begin, it2end)
6、l.swap() 交换两个list
// 交换l1和l2中的元素
l1.swap(l2);
迭代器:
1、begin()/end():返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器
list<int>::iterator it = l.begin()
2、rbegin()/rend():返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的 reverse_iterator,即begin位置;
list<int>::reverse_iterator it = l.rbegin()
3、l.begin() 返回指向第一个元素的迭代器
4、l.end() 返回末尾的迭代器
5、l.rbegin() 返回指向第一个元素的逆向迭代器
6、l.rend() 指向list末尾的逆向迭代器
5 queue 队列
queue 容器是一种先进先出(First In First Out, FIFO)的数据结构,且有两个出口。
5.1 头文件
#inlcude <queue>
5.2 构造queue
//1、默认初始化一个空的整数队列
queue<int> q1;
//2、使用初始化列表初始化队列
queue<int> q2 = {1, 2, 3, 4, 5};
//3、使用q1来初始化q3
queue<int> q3(q1);
//4、初始化一个存储vector的队列
queue<std::vector<int>> q;
q.emplace(1, 2, 3); // 直接在队列中构造一个vector<int>{1, 2, 3}
//5、默认初始化一个空的整数队列
queue<int> q;
q.push(1); // 添加元素1到队列尾部
//6、使用q1来移动初始化q2,现在q1是空的,q2是{1, 2, 3}
queue<int> q2(move(q1));
//7、定义一个储存数据类型为结构体类型的queue容器q4
queue<结构体类型> q4;
5.3 元素访问
ftont() //1、返回队列中的第一个元素
back() //2、返回队列中最后一个元素
pop() //3、删除队列第一个元素
5.4 queue操作
ftont() //1、返回队列中的第一个元素
back() //2、返回队列中最后一个元素
push() //3、在队尾插入一个元素
emplace() //4、用传给 emplace() 的参数调用 T 的构造函数,在 queue 的尾部生成对象。
pop() //5、删除队列第一个元素
size() //6、返回队列中元素个数
empty() //7、如果队列空则返回true
swap() q2.swap(q1); //8、将当前queue中的元素与参数queue中的元素交换
//9、运算符 = != > >= < <=
// [==]当两个队列front()内容一致,返回true
// [!=]当两个队列元素front()不相等,返回true
// [>]左边队列的front()的元素大于右边队列pop的元素,则返回true.
//10、迭代器没有意义,只允许队首出队、队尾入队
//11、queue遍历
while(!q.empty())
{
cout<<q.front()<<" ";
q.pop();
}
6 deque双端队列
-
deque(全称为"double-ended queue",双端队列),它是一种数据结构,允许在队列的两端进行插入和删除操作。
-
deque的特点是可以在队列的两端进行元素的操作,并且可以高效地在队列的任意位置进行元素的插入和删除操作。
-
deque几乎涵盖了queue(队列)、stack(堆栈)、vector(向量 )等的全部用法,功能非常的强大。
-
deque 通常实现为一组独立区块,第一区块朝某方向扩展,最末区块朝另一方向扩展
-
deque 内部有个中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据
-
中控器维护的是每个缓冲区的地址,使得使用 deque 时像一片连续的内存空间
-
deque 容器的迭代器也是支持随机访问的
6.1 头文件
#include <deque>
6.2 构造vector容器
//deque的定义
deque<int> d1; //1、定义一个储存数据类型为int的双端队列d1
deque<结构体类型> d2; //2、定义一个储存数据类型为结构体类型的双端队列d4
deque<int> d3[N]; //3、定义一个储存数据类型为int的双端队列数组d5
deque<int> d1(d.begin(), d.end()); //4、将d容器开始迭代器到结束迭代器之间的数据元素,拷贝给d1容器
deque<int> d2(10, 100); //5、将10个100拷贝给d2容器
deque<int> d3(d2); //6、拷贝构造
6.3 元素访问
1、通过迭代器iterator遍历
for(it=d.begin();it!=d.end();it++)
{
cout<<*it<<" ";
}
2、通过下标遍历
for(int i=0;i<d.size();i++)
{
cout<<d[i]<<" ";
}
3、通过foreach遍历
for(int it:d)
{
cout<<it<<" ";
}
6.4 deque操作
//数据获取
front() //返回队列头部元素的引用。
back() //返回队列尾部元素的引用。
at(idx) //返回索引idx所指的数据
d.[idx] //返回下标idx所指的数据
//插入删除元素
push_back(elem) //在队列的尾部插入元素。
emplace_back(elem) //与push_back()的作用一样
push_front(elem) //在队列的头部插入元素。
emplace_front(elem) //与push_front()的作用一样
pop_back() //删除队列尾部的元素。
pop_front() //删除队列头部的元素。
insert(pos, elem) //在pos位置前插入一个elem元素的拷贝,返回新数据位置
insert(pos, n, elem)//在iterator 位置 pos 之前方插入 n 个 elem 拷贝,并返回第一个新元素的位置
insert(pos, beg, end)//在 iterator 位置 pos 之前方插入区间 [beg, end) 内所有元素的一份拷贝,并返回第一个新元素的位置
insert(pos, initlist)//在 iterator 位置 pos 之前方插入初值列 initlist 内所有元素的一份拷贝,并返回第一个新元素的位置
erase(pos) //移除 iterator 位置 pos 上的元素,返回下一元素的位置
erase(beg, end) //移除 [beg, end) 区间内的所有元素,返回下一元素的位置
clear() //清空队列中的所有元素。
//大小操作
empty() //判断队列是否为空。
size() //返回队列中元素的个数。
resize() //重置元素数量
resize(num) //(如果 size() 变大,多出来的新元素都需以 default 构造函数完成初始化)
resize(num,elem)//(如果 size() 变大,多出来的新元素都是 elem 的拷贝)
//算法
d1.swap(d2) //置换 d1 和 d2 的数据 swap(c1, c2) 置换 c1 和 c2 的数据
sort(d.begin(), d.end());//对d队列进行排序
//迭代器
begin() //返回头位置的迭代器
end() //返回尾+1位置的迭代器
rbegin() //返回逆头位置的迭代器
rend() //返回逆尾-1位置的迭代器
for(it=d.begin();it!=d.end();it++) //通过迭代器iterator遍历
{
cout<<*it<<" ";
}
for(int i=0;i<d.size();i++) //通过下标遍历
{
cout<<d[i]<<" ";
}
for(int it:d) //通过foreach遍历
{
cout<<it<<" ";
}
//运算符
c1 == c2 返回 c1 是否等于 c2(对每个元素调用==)
c1 != c2 返回 c1 是否不等于 c2(相当于!(c1==c2))
c1 < c2 返回 c1 是否小于 c2
c1 > c2 返回 c1 是否大于 c2(相当于c2<c1)
c1 <= c2 返回 c1 是否小于等于 c2(相当于!(c2<c1))
c1 >= c2 返回 c1 是否大于等于 c2(相当于! (c1<c2))
7 priority_queue优先队列
- 优先队列是一种容器适配器,采用了
堆
这样的数据结构,保证了第一个元素总是整个优先队列中最大的(或最小的)元素
。 - 优先队列默认使用vector作为底层存储数据的容器,在vector上使用了堆算法将vector中的元素构造成堆的结构,所以其实我们就可以把它当作堆,凡是
需要用堆的位置,都可以考虑优先队列
。 - 默认为大堆
- 不允许随机访问,只能访问队列首部元素,也只能对首部元素进行出队
7.1 头文件
#include <queue>
7.2 构造priority_queue
priority_queue<int> q; //储存int型数据
priority_queue<double> q; //储存double型数据
priority_queue<string> q; //储存string型数据
priority_queue<结构体名> q; //储存结构体或者类
7.3 元素访问
top() //返回优先队列中有最高优先级的元素
pop() //删除第一个元素
push() //加入一个元素
emplace() //加入一个元素,并自动创建内存
7.4 priority_queue操作
top() //返回优先队列中有最高优先级的元素
pop() //删除第一个元素
push() //加入一个元素
emplace() //加入一个元素,并自动创建内存
size() //返回优先队列中拥有的元素的个数
empty() //如果优先队列为空,则返回真
7.5 大、小顶堆切换
less<储存的数据类型> //即使用大顶堆
greater<储存的数据类型> //即是用小顶堆
priority_queue<储存的类型,vector<储存的类型>,顶堆的类型> 容器名
- 使用大顶堆
priority_queue<int,vector<int>,less<int>> q;//储存int型数据
priority_queue<double,vector<double>,less<double>> q;//储存double型数据
priority_queue<string,vector<string>,less<string>> q;//储存string型数据
priority_queue<结构体名,vector<结构体名>,less<结构体名>> q;//储存结构体或者类
- 使用小顶堆
priority_queue<int,vector<int>,greater<int>> q;//储存int型数据
priority_queue<double,vector<double>,greater<double>> q;//储存double型数据
priority_queue<string,vector<string>,greater<string>> q;//储存string型数据
priority_queue<结构体名,vector<结构体名>,greater<结构体名>> q;//储存结构体或者类
- 自定义比较函数
//小于号是大根堆,大于号是小根堆
bool cmp(vector<int>&a,vector<int>&b){
return a[0]>b[0];
}
priority_queue<vector<int>,vector<vector<int>>,decltype(&cmp)> q(cmp);//小顶堆
//小于号是大根堆,大于号是小根堆
auto cmp = [&nums1, &nums2](const pair<int, int> & a, const pair<int, int> & b) {
return nums1[a.first] + nums2[a.second] > nums1[b.first] + nums2[b.second];
};
priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(cmp)> pq(cmp);
- 结构体重载运算符定义
//新建一个结构体,通过重载运算符改变顶堆的排序
//自己写的结构体是没有比较大小功能的
//当然也可以在原本的结构体里面重载运算符
struct test{ //定义一个结构体test
int val;
test(int v){ //构造函数
this->val=v;
}
bool operator > (const test t)const{ //重载运算符 >
return val>t.val;
}
bool operator < (const test t)const{ //重载运算符 <
return val<t.val;
}
};
- 自定义结构体重载括号运算符
//1. 多时候,我们不应该重载结构体的运算符
//像数据结构vector,它有它的基本运算方法,我们不应该重载它的运算符。
//2. 此时,我们就应该自定义结构体替代less<>和greater<>,通过重载括号符就可以更改比较规则
struct cmp{
bool operator () (const test t1,const test t2)const{ //重载括号运算符
return t1.val < t2.val;//小于号是大根堆,大于号是小根堆
}
};
priority_queue<test, vector<test>, cmp> q;//自定义一个优先级队列q
8 stack
- tack 是一种
先进后出
(First In Last Out,FILO)的数据结构,它只有一个出口
- 栈中只有顶端的元素才可以被外界使用,因此栈不允许有遍历行为 (遍历:非质变算法,在运算期间,不改变容器中的元素)
- 栈中进入数据称为 — 入栈 push
- 栈中弹出数据称为 — 出栈 pop
- 生活中的栈: 子弹夹,先装进弹夹的子弹,最后被打出来
8.1 头文件
#include <stack> //底层实现是双向链表
8.2 构造stact容器
// stack 采用模板类实现, stack 对象的默认构造形式
stack<T> stk;
1、stack<int> myStack;
2、std::stack<int> myStack;
// 使用push方法初始化stack
myStack.push(1);
3、std::vector<int> myVector = {1, 2, 3, 4, 5};
std::stack<int> myStack(myVector.begin(), myVector.end());
//拷贝构造函数
stack(const stack &stk);
8.3 元素访问
top() //返回一个栈顶元素的引用。如果栈为空,不能用该函数访问,否则报错。
push(elem) //向堆栈顶部添加元素
emplace() //用传入的参数调用构造函数,在栈顶生成对象。
pop() //弹出栈顶元素。
8.4 stact操作
top() //返回一个栈顶元素的引用。如果栈为空,不能用该函数访问,否则报错。
push(elem) //向堆栈顶部添加元素
emplace() //用传入的参数调用构造函数,在栈顶生成对象。
pop() //弹出栈顶元素。
size() //返回栈中元素的个数。
empty() //在栈中没有元素的情况下返回 true。
swap(stack<T> & other_stack) //将当前栈中的元素和参数中的元素交换。参数所包含元素的类型必须和当前栈的相同。对于 stack 对象有一个特例化的全局函数 swap() 可以使用。
9 map
- map是C++中STL中的一个
关联容器
,以键值对
来存储数据,数据类型自己定义。它的内部数据结构是红黑树
,所有它是有默认排列顺序
的 - 每一对中的第一个值称之为
关键字(key)
,每个关键字只能在map中出现一次
;第二个称之为该关键字的对应值
- 第一个参数是键的数据类型
- 第二个参数是值的数据类型
- 第三个参数是
排序规则
,不写的话就按照默认的排序规则
,也就是按照键的升序
- 举例:map<int,int> mp; 定义了一个叫mp的map类型,并且键值都是int类型
9.0 pairl类型
- 了解pair 的标准库类型
- pair类型是在文件 utility 中定义
- pair是将2个数据组合成一组数据,
- 当需要将2个数据组合时就可以使用pair
- 如STL中的map就是将key和value放在一起来保存。
- 当一个函数需要返回2个数据的时候可以使用pair。
- 当需要将2个数据组合时就可以使用pair
- pair的实现是一个
结构体
,主要的两个成员变量是first
,second
- 因为是使用struct不是class,所以可以直接使用pair的成员变量。
9.0.1 pair类型的定义和初始化
- pair类型包含了两个数据值,通常有以下的一些定义和初始化的一些方法:
pair<T1, T2> p; //定义了一个空的pair对象p,T1和T2的成员都进行了值初始化
pair<T1, T2> p(v1, v2); //p是一个成员类型为T1和T2的pair; first和second成员分别用v1和v2进行初始化。
pair<T1, T2> p = {v1, v2} //等价于p(v1, v2)
make_pair(v1, v2) //以v1和v2值创建的一个新的pair对象
9.0.2 pair操作
p.first //返回p的名为 first 的(公有)数据成员
p.second //返回p的名为second的(公有)数据成员
p1 relop p2 //关系运算符 (<、>、<= 、>=) 按字典序定义。例如,当 p1.first < p2.first 或 !(p2.first < p1.first) && p1.second < p2.second 成立时, p1 < p2 为 true。关系运算利用元素的 < 运算符来实现
p1 == p2 //当 first 和 second 成员分别相等时,两个pair相等
p1 != p2 //若不能达到以上要求,则不相等
pair<int, string> p1(0, "Hello");
printf("%d, %s\n", p1.first, p1.second.c_str());
pair<int, string> p2 = make_pair(1, "World");
printf("%d, %s\n", p2.first, p2.second.c_str());
9.1 头文件
#include <map>
9.2 构造map容器
1、map<string, int> myMap;
2、map<string, int> myMap = {
{"apple", 1},
{"banana", 2},
{"cherry", 3}
};
3、insert
map<string, int> myMap;
myMap.insert(make_pair("apple", 1));
myMap.insert(pair<string, int>("banana", 2));
myMap.insert({"cherry", 3}); // C++11及以后版本支持这种写法
4、复制构造函数
map<string, int> anotherMap = {
{"apple", 1},
{"banana", 2}
};
map<string, int> myMap(anotherMap); // 通过复制构造函数初始化
5、赋值构造函数
map<string, int> anotherMap = {
{"apple", 1},
{"banana", 2}
};
map<string, int> myMap;
myMap = anotherMap; // 通过赋值运算符初始化
6、默认升序
map<int, int> m;
7、改为降序排列
map<int, int, compare> m2;
class compare//一个名叫compare的类
{
public://在public下的作用域在类内类外都可以进行访问
bool operator()(int m, int n)const//注意要在后面加const
{
return m > n;//降序排列返回的就两个值中的较大值
}
};
9.3 元素访问
9.4 map操作
###############插入操作insert###################
1、使用[ ]进行单个插入
map<int, string> ID_Name;
// 如果已经存在键值2015,则会作赋值修改操作,如果没有则插入
ID_Name[2015] = "Tom";
2、使用insert进行单个和多个插入
// 插入单个值
mymap.insert(pair<char, int>('a', 100));
//返回插入位置以及是否插入成功
pair<map<char, int>::iterator, bool> ret;
ret = mymap.insert(pair<char, int>('z', 500));insert函数返回一个pair,其中first成员是指向插入元素的迭代器,second成员是一个布尔值,表示是否成功插入(如果键已存在,则插入失败,second为false)。
//指定位置插入
map<char, int>::iterator it = mymap.begin();
mymap.insert(it, pair<char, int>('b', 300)); //效率更高,插入操作不会改变已存在元素的顺序,但你可以指定一个迭代器作为插入位置的提示。这可以提高插入的效率,尤其是在插入大量连续元素时。注意,这只是一个提示,map仍然会保持其排序属性
//范围多值插入
map<char, int> anothermap;
anothermap.insert(mymap.begin(), mymap.find('c'));//anothermap将插入mymap中从begin()到find('c')(不包括find('c')指向的元素)范围内的所有元素。
// 列表形式插入
anothermap.insert({ { 'd', 100 }, {'e', 200} });//使用初始化列表语法来一次性插入多个元素
###############取值操作###################
map<int, string> ID_Name;
1、//ID_Name中没有关键字2016,使用[]取值会导致插入
//因此,下面语句不会报错,但打印结果为空
cout<<ID_Name[2016].c_str()<<endl;
2、//使用at会进行关键字检查,因此下面语句会报错
ID_Name.at(2016) = "Bob";
##############容量操作###################
bool empty();// 查询map是否为空
size_t size();// 查询map中键值对的数量
size_t max_size();// 查询map所能包含的最大键值对数量,和系统和应用库有关。// 此外,这并不意味着用户一定可以存这么多,很可能还没达到就已经开辟内存失败了
size_t count( const Key& key ) const; 查询关键字为key的元素的个数,在map里结果非0即1
##############删除操作操作###################
iterator erase( iterator pos )// 删除迭代器指向位置的键值对,并返回一个指向下一元素的迭代器
iterator erase( const_iterator first, const_iterator last );// 删除一定范围内的元素,并返回一个指向下一元素的迭代器
size_t erase( const key_type& key );// 根据Key来进行删除, 返回删除的元素数量,在map里结果非0即1
void clear();// 清空map,清空后的size为0
##############交换操作###################
void swap( map& other );// 就是两个map的内容互换
##############查找操作###################
// 关键字查询,找到则返回指向该关键字的迭代器,否则返回指向end的迭代器
// 根据map的类型,返回的迭代器为 iterator 或者 const_iterator
iterator find (const key_type& k);
const_iterator find (const key_type& k) const;
##############操作符操作###################
operator: == != < <= > >= 注意 对于==运算符, 只有键值对以及顺序完全相等才算成立。
##############迭代器操作###################
1、共有八个获取迭代器的函数:* begin, end, rbegin,rend* 以及对应的 * cbegin, cend, crbegin,crend*。
2、二者的区别在于,后者一定返回 const_iterator,而前者则根据map的类型返回iterator 或者 const_iterator。const情况下,不允许对值进行修改。如下面代码所示:
map<int,int>::iterator it;
map<int,int> mmap;
const map<int,int> const_mmap;
it = mmap.begin(); //iterator
mmap.cbegin(); //const_iterator
const_mmap.begin(); //const_iterator
const_mmap.cbegin(); //const_iterator
set<int> st;
for (int i = 10; i >0 ;i--)
{
st.insert(i);
}
set<int>::iterator it;
for (it = st.begin(); it != st.end(); it++)
{
cout << *it <<" ";
}
cout << endl;
if ((it = st.find(2)) != st.end())
{
cout << "find " << *it << endl;//输出为2
}
10 unordered_map
- unordered_map是一个将key和value关联起来的容器,它可以高效的根据单个key值查找对应的value。
- key值应该是唯一的,
key和value
的数据类型可以不相同。 - unordered_map存储元素时是
没有顺序
的,只是根据key的哈希值,将元素存在指定位置,所以根据key查找
单个value时非常高效,平均可以在常数时间内
完成。 - unordered_map查询单个key的时候效率比map高,但是要查询某一范围内的key值时比map效率低。
- 可以使用[]操作符来访问key值对应的value值
10.0 map和unordered_map区别
- 运行效率方面:unordered_map最高,而map效率较低但 提供了稳定效率和有序的序列。
- 占用内存方面:map内存占用略低,unordered_map内存占用略高,而且是线性成比例的。
- map: map内部实现了一个红黑树,该结构具有自动排序的功能,因此map内部的所有元素都是有序的,红黑树的每一个节点都代表着map的一个元素,因此,对于map进行的查找,删除,添加等一系列的操作都相当于是对红黑树进行这样的操作,故红黑树的效率决定了map的效率。
- unordered_map: unordered_map内部实现了一个哈希表,因此其元素的排列顺序是杂乱的,无序的
map
- 优点:
有序性
,这是map结构最大的优点,其元素的有序性在很多应用中都会简化很多的操作。
红黑树,内部实现一个红黑书使得map的很多操作在lgn的时间复杂度下就可以实现,因此效率非常的高。 - 缺点:
空间占用率高
,因为map内部实现了红黑树,虽然提高了运行效率,但是因为每一个节点都需要额外保存父节点,孩子节点以及红/黑性质,使得每一个节点都占用大量的空间 - 适用处:对于那些有顺序要求的问题,用map会更高效一些。
unordered_map
- 优点:内部实现了哈希表加粗样式,因此其
查找速度是常量级别的
。 - 缺点:哈希表的
建立比较耗费时间
- 适用处:对于查找问题,unordered_map会更加高效一些,因此遇到查找问题,常会考虑一下用unordered_map
10.1 头文件
#include <unordered_map>
10.2 构造vector容器
unordered_map<int, string> umap;
10.3 元素访问
10.4 unordered_map操作
- unordered_map与map的用法基本一直,最大的区别在于
map的key是有序的,而unordered_map的key为无序
void f2() {
map<int, string> commap;
unordered_map<int, string> umap;
commap[1]="a"; commap[3]="c"; commap[2]="b"; commap[4]="d";
umap[1]="aa"; umap[3]="cc"; umap[4]="dd"; umap[2]="bb";
for(auto node: commap) {
cout<<node.first<<": "<<node.second<<endl;
}
for(auto node: umap) {
cout<<node.first<<": "<<node.second<<endl;
}
}
1: a
2: b
3: c
4: d
2: bb
4: dd
3: cc
1: aa
###############插入操作insert###################
1、使用[ ]进行单个插入
map<int, string> ID_Name;
// 如果已经存在键值2015,则会作赋值修改操作,如果没有则插入
ID_Name[2015] = "Tom";
2、使用insert进行单个和多个插入
// 插入单个值
mymap.insert(pair<char, int>('a', 100));
//返回插入位置以及是否插入成功
pair<map<char, int>::iterator, bool> ret;
ret = mymap.insert(pair<char, int>('z', 500));insert函数返回一个pair,其中first成员是指向插入元素的迭代器,second成员是一个布尔值,表示是否成功插入(如果键已存在,则插入失败,second为false)。
//指定位置插入
map<char, int>::iterator it = mymap.begin();
mymap.insert(it, pair<char, int>('b', 300)); //效率更高,插入操作不会改变已存在元素的顺序,但你可以指定一个迭代器作为插入位置的提示。这可以提高插入的效率,尤其是在插入大量连续元素时。注意,这只是一个提示,map仍然会保持其排序属性
//范围多值插入
map<char, int> anothermap;
anothermap.insert(mymap.begin(), mymap.find('c'));//anothermap将插入mymap中从begin()到find('c')(不包括find('c')指向的元素)范围内的所有元素。
// 列表形式插入
anothermap.insert({ { 'd', 100 }, {'e', 200} });//使用初始化列表语法来一次性插入多个元素
###############取值操作###################
map<int, string> ID_Name;
1、//ID_Name中没有关键字2016,使用[]取值会导致插入
//因此,下面语句不会报错,但打印结果为空
cout<<ID_Name[2016].c_str()<<endl;
2、//使用at会进行关键字检查,因此下面语句会报错
ID_Name.at(2016) = "Bob";
##############容量操作###################
bool empty();// 查询map是否为空
size_t size();// 查询map中键值对的数量
size_t max_size();// 查询map所能包含的最大键值对数量,和系统和应用库有关。// 此外,这并不意味着用户一定可以存这么多,很可能还没达到就已经开辟内存失败了
size_t count( const Key& key ) const; 查询关键字为key的元素的个数,在map里结果非0即1
##############删除操作操作###################
iterator erase( iterator pos )// 删除迭代器指向位置的键值对,并返回一个指向下一元素的迭代器
iterator erase( const_iterator first, const_iterator last );// 删除一定范围内的元素,并返回一个指向下一元素的迭代器
size_t erase( const key_type& key );// 根据Key来进行删除, 返回删除的元素数量,在map里结果非0即1
void clear();// 清空map,清空后的size为0
##############交换操作###################
void swap( map& other );// 就是两个map的内容互换
##############查找操作###################
// 关键字查询,找到则返回指向该关键字的迭代器,否则返回指向end的迭代器
// 根据map的类型,返回的迭代器为 iterator 或者 const_iterator
iterator find (const key_type& k);
const_iterator find (const key_type& k) const;
##############操作符操作###################
operator: == != < <= > >= 注意 对于==运算符, 只有键值对以及顺序完全相等才算成立。
##############迭代器操作###################
1、共有八个获取迭代器的函数:* begin, end, rbegin,rend* 以及对应的 * cbegin, cend, crbegin,crend*。
2、二者的区别在于,后者一定返回 const_iterator,而前者则根据map的类型返回iterator 或者 const_iterator。const情况下,不允许对值进行修改。如下面代码所示:
map<int,int>::iterator it;
map<int,int> mmap;
const map<int,int> const_mmap;
it = mmap.begin(); //iterator
mmap.cbegin(); //const_iterator
const_mmap.begin(); //const_iterator
const_mmap.cbegin(); //const_iterator
set<int> st;
for (int i = 10; i >0 ;i--)
{
st.insert(i);
}
set<int>::iterator it;
for (it = st.begin(); it != st.end(); it++)
{
cout << *it <<" ";
}
cout << endl;
if ((it = st.find(2)) != st.end())
{
cout << "find " << *it << endl;//输出为2
}
11 set
- set关联式容器
- 集合(set)是一个内部自动
有序
且不含重复元素的容器
,默认按升序排列
,它可以在需要删除重复元素的情况下大放异彩,节省时间,减少思维量。 - set 就是
关键字的简单集合
,当只是想知道一个值是否存在时,set 是最有用的。set 内部采用的是一种非常高效的平衡检索二叉树:红黑树
(Red-Black Tree),也称为 RB 树,RB 树的统计性能要好于一般平衡二叉树。在 set 中每个元素的值都唯一,而且系统能根据元素的值自动进行排序,set 中元素的值不能直接被改变。- map 中的元素是一些关键字 — 值(key–value)对:关键字起到索引的作用,值则表示与索引相关联的数据。
- 在 set 中每个元素
只包含一个关键字,不存储特定值关联
:set 支持高效的关键字查询操作 —— 检查一个给定关键字是否在 set 中。
- 标准库提供 set 关联容器分为:
- 按关键字有序保存元素:set(关键字即值,即只保存关键字的容器)、multiset(关键字可重复出现的 set);
- 无序集合:unordered_set(用哈希函数组织的 set)、unordered_multiset(用哈希函数组织的 set,关键字可以重复出现)。
11.1 头文件
#inlcude <set>
11.2 构造set容器
默认元素从小到大自动排序
- 手动从小到大排序 less
- set<int, less> se2{ 9, 5, 2, 7 };
- 手动从大到小排序 greater
- set<int, greater> se3{ 9, 5, 2, 7 };
1、 set<int> name;
set<double> name;
set<char> name;
set<struct node> name;
set<set<int>> name;
set<int> arr[10];
2、 set<int> anotherSet = {1, 2, 3};
3、 set<int> mySet(anotherSet); // 通过复制构造函数初始化
4、 std::set<int> mySet;
mySet = anotherSet; // 通过赋值运算符初始化
11.3 元素访问
11.4 set操作
0、= 直接赋值
1、set<int>::iterator it; //set 只能通过迭代器(iterator)访问:
2、查找元素位置:
find(value) //返回 set 中 value 所对应的迭代器,即 value 的指针(地址),如果没找到则返回 end()
3、删除单个元素:
st.erase(it) //其中 it 为所需要删除元素的迭代器。时间复杂度为 O(1),可以结合 find() 函数来使用。
st.erase(value) //其中 value 为所需要删除元素的值。时间复杂度为 O(logN),N 为 set 内的元素个数。
st.erase(iteratorBegin, iteratorEnd) //删除一个区间内的所有元素,取 [iteratorBegin, iteratorEnd)
5、equal_range():返回一对迭代器,分别表示第一个大于或等于给定关键值的元素和第一个大于给定关键值的元素。这个返回值是一个 pair 类型,第一个是键的 lower_bound,第二个是键的 upper_bound。如果这一对定位器中哪个返回失败,则返回 end()
pr = s.equal_range(3);
cout << "第一个大于等于3的数是:"<< *pr.first << endl;
cout << "第一个大于3的数是:"<< *pr.second <<endl;
//增删改查
s.find(value); // 返回一个指向被查找到元素的迭代器
s.count(value); // 返回某个值元素的个数,用于判断set中是否有该元素
s.insert(value); // 在集合中插入元素
s.erase(it); // 删除集合中的元素
s.size(); // 返回集合中元素的数目
s.empty(); // 如果集合为空,返回true,否则返回false
s.clear(); // 清除所有元素
//迭代器
s.begin(); // 返回指向第一个元素的迭代器
s.end(); // 返回指向最后一个元素的后一个位置的迭代器
s.rbegin(); // 返回set反转后的开始指针(即原来的end-1)
s.rend(); // 返回set反转后的结束指针(即原来的begin-1)
//算法
s.equal_range(value); // 返回集合中与给定值相等的上下限的两个迭代器
s.upper_bound(value); // 返回大于某个值元素的迭代器
s.lower_bound(value); // 返回指向大于(或等于)某值的第一个元素的迭代器
s.max_size(); // 返回集合能容纳的元素的最大限值
s.swap(s1); // 交换两个集合变量
12 unordered_set
- unordered_set 容器,可直译为“
无序
set 容器”,即 unordered_set 容器和 set 容器很像,唯一的区别就在于 set 容器会自行对存储的数据进行排序,而 unordered_set 容器不会- 不再以键值对的形式存储数据,而是直接
存储数据的值
; - 容器内部存储的各个元素的
值都互不相等
,且不能被修改。 - 不会对内部存储的数据进行排序(这和该容器底层采用
哈希表
结构存储数据有关);
- 不再以键值对的形式存储数据,而是直接
12.1 头文件
#include <unordered_set>
12.2 构造unordered_set容器
1、 unordered_set<int> name;
unordered_set<double> name;
unordered_set<char> name;
unordered_set<struct node> name;
unordered_set<unordered_set<int>> name;
unordered_set<int> arr[10];
2、 unordered_set<int> anotherSet = {1, 2, 3};
3、 unordered_set<int> mySet(anotherSet); // 通过复制构造函数初始化
4、 unordered_set<int> mySet;
mySet = anotherSet; // 通过赋值运算符初始化
5、 myUnorderedSet.insert(1);
12.3 元素访问
12.4 unordered_set操作
0、= 直接赋值
1、set<int>::iterator it; //set 只能通过迭代器(iterator)访问:
2、查找元素位置:
find(value) //返回 set 中 value 所对应的迭代器,即 value 的指针(地址),如果没找到则返回 end()
3、删除单个元素:
st.erase(it) //其中 it 为所需要删除元素的迭代器。时间复杂度为 O(1),可以结合 find() 函数来使用。
st.erase(value) //其中 value 为所需要删除元素的值。时间复杂度为 O(logN),N 为 set 内的元素个数。
st.erase(iteratorBegin, iteratorEnd) //删除一个区间内的所有元素,取 [iteratorBegin, iteratorEnd)
5、equal_range():返回一对迭代器,分别表示第一个大于或等于给定关键值的元素和第一个大于给定关键值的元素。这个返回值是一个 pair 类型,第一个是键的 lower_bound,第二个是键的 upper_bound。如果这一对定位器中哪个返回失败,则返回 end()
pr = s.equal_range(3);
cout << "第一个大于等于3的数是:"<< *pr.first << endl;
cout << "第一个大于3的数是:"<< *pr.second <<endl;
//增删改查
s.find(value); // 返回一个指向被查找到元素的迭代器
s.count(value); // 返回某个值元素的个数,用于判断set中是否有该元素
s.insert(value); // 在集合中插入元素
s.erase(it); // 删除集合中的元素
s.size(); // 返回集合中元素的数目
s.empty(); // 如果集合为空,返回true,否则返回false
s.clear(); // 清除所有元素
//迭代器
s.begin(); // 返回指向第一个元素的迭代器
s.end(); // 返回指向最后一个元素的后一个位置的迭代器
s.rbegin(); // 返回set反转后的开始指针(即原来的end-1)
s.rend(); // 返回set反转后的结束指针(即原来的begin-1)
//算法
s.equal_range(value); // 返回集合中与给定值相等的上下限的两个迭代器
s.upper_bound(value); // 返回大于某个值元素的迭代器
s.lower_bound(value); // 返回指向大于(或等于)某值的第一个元素的迭代器
s.max_size(); // 返回集合能容纳的元素的最大限值
s.swap(s1); // 交换两个集合变量
13 二叉树结构体
//Definition for a binary tree node.
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
14 链表结构体
//Definition for singly-linked list.
struct ListNode {
int val;
ListNode *next;
ListNode() : val(0), next(nullptr) {}
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode *next) : val(x), next(next) {}
};
15 string字符串处理
- 在C++中,处理字符串的类被封装在string类中,其处理较复杂字符串函数操作简于C(cstring为C风格string类头文件,对应类为CString)
15.1 头文件
#include <string>
12.2 构造string
1、string s1(); // si = ""
2、string s2("Hello"); // s2 = "Hello"
3、string s3(4, 'K'); // s3 = "KKKK"
4、string s4("12345", 1, 3); //s4 = "234",即 "12345" 的从下标 1 开始,长度为 3 的子串
12.3 string对象赋值
1、string s1;
2、s1 = "Hello"; // s1 = "Hello"
3、s2 = 'K'; // s2 = "K”
string 类还有 assign 成员函数,可以用来对 string 对象赋值。assign 成员函数返回对象自身的引用
1、string s1("12345"), s2;
2、s3.assign(s1); // s3 = s1
3、s2.assign(s1, 1, 2); // s2 = "23",即 s1 的子串(1, 2)
4、s2.assign(4, 'K'); // s2 = "KKKK"
5、s2.assign("abcde", 2, 3); // s2 = "cde",即 "abcde" 的子串(2, 3)
12.4 string操作
string s;
- 计算字符串长度
int n = s.size()
int n = s.length()
- 字符串拼接(string 类还有 append 成员函数,可以用来向字符串后面添加内容。append 成员函数返回对象自身的引用。)
string s1("123"), s2("abc");
s1.append(s2); // s1 = "123abc"
s1.append(s2, 1, 2); // s1 = "123abcbc"
s1.append(3, 'K'); // s1 = "123abcbcKKK"
s1.append("ABCDE", 2, 3); // s1 = "123abcbcKKKCDE",添加 "ABCDE" 的子串(2, 3)
- 字符串比较
1、 可以用 <、<=、==、!=、>=、> 运算符比较 string 对象
2、string 类还有 compare 成员函数,可用于比较字符串。compare 成员函数有以下返回值:
小于 0 表示当前的字符串小;
等于 0 表示两个字符串相等;
大于 0 表示另一个字符串小。
// "aBcd" 和 "Abcd"比较------ a > A
cout << "A.compare(B):" << A.compare(B)<< endl; // 结果:1
// "cd" 和 "Abcd"比较------- c > A
cout << "A.compare(2, 3, B):" <<A.compare(2, 3, B)<< endl; // 结果:1
// "cd" 和 "cd"比较
cout << "A.compare(2, 3, B, 2, 3):" << A.compare(2, 3, B, 2, 3) << endl; // 结果:0
// 由结果看出来:0表示下标,3表示长度
// "123" 和 "123"比较
cout << "C.compare(0, 3, D, 0, 3)" <<C.compare(0, 3, D, 0, 3) << endl; // 结果:0
- string子串
1、substr 成员函数可以用于求子串 (n, m)
string substr(int n = 0, int m = string::npos) const
string s1 = "this is ok";
string s2 = s1.substr(2, 4); // s2 = "is i"
s2 = s1.substr(2); // s2 = "is is ok"
- 交换字符串内容
string s1("West”), s2("East");
s1.swap(s2); // s1 = "East",s2 = "West"
- 查找子串和字符
0、string 类有一些查找子串和字符的成员函数,它们的返回值都是子串或字符在 string 对象字符串中的位置(即下标)。
如果查不到,则返回 string::npos。string::npos 是在 string 类中定义的一个静态常量,指示未找到任何位置。
找到了,返回第一个元素的索引位置
1、find:从前往后查找子串或字符出现的位置。
2、rfind:从后往前查找子串或字符出现的位置。
3、find_first_of:从前往后查找何处出现另一个字符串中包含的字符。例如:
s1.find_first_of(“abc”); //查找s1中第一次出现"abc"中任一字符的位置
5、find_last_of:从后往前查找何处出现另一个字符串中包含的字符。
6、find_first_not_of:从前往后查找何处出现另一个字符串中没有包含的字符。
7、find_last_not_of:从后往前查找何处出现另一个字符串中没有包含的字符。
string s1("Source Code");
int n;
if ((n = s1.find('u')) != string::npos) //查找 u 出现的位置
cout << "1) " << n << "," << s1.substr(n) << endl;
//输出 l)2,urce Code
if ((n = s1.find("Source", 3)) == string::npos)
//从下标3开始查找"Source",找不到
cout << "2) " << "Not Found" << endl; //输出 2) Not Found
if ((n = s1.find("Co")) != string::npos)
//查找子串"Co"。能找到,返回"Co"的位置
cout << "3) " << n << ", " << s1.substr(n) << endl;
//输出 3) 7, Code
if ((n = s1.find_first_of("ceo")) != string::npos)
//查找第一次出现或 'c'、'e'或'o'的位置
cout << "4) " << n << ", " << s1.substr(n) << endl;
//输出 4) l, ource Code
if ((n = s1.find_last_of('e')) != string::npos)
//查找最后一个 'e' 的位置
cout << "5) " << n << ", " << s1.substr(n) << endl; //输出 5) 10, e
if ((n = s1.find_first_not_of("eou", 1)) != string::npos)
//从下标1开始查找第一次出现非 'e'、'o' 或 'u' 字符的位置
cout << "6) " << n << ", " << s1.substr(n) << endl;
//输出 6) 3, rce Code
- 替换子串
replace 成员函数可以对 string 对象中的子串进行替换,返回值为对象自身的引用
string s1("Real Steel");
s1.replace(1, 3, "123456", 2, 4); //用 "123456" 的子串(2,4) 替换 s1 的子串(1,3)
cout << s1 << endl; //输出 R3456 Steel
string s2("Harry Potter");
s2.replace(2, 3, 5, '0'); //用 5 个 '0' 替换子串(2,3)
cout << s2 << endl; //输出 HaOOOOO Potter
int n = s2.find("OOOOO"); //查找子串 "00000" 的位置,n=2
s2.replace(n, 5, "XXX"); //将子串(n,5)替换为"XXX"
cout << s2 < < endl; //输出 HaXXX Potter
- 删除子串
erase 成员函数可以删除 string 对象中的子串,返回值为对象自身的引用。
string s1("Real Steel");
s1.erase(1, 3); //删除子串(1, 3),此后 s1 = "R Steel"
s1.erase(5); //删除下标5及其后面的所有字符,此后 s1 = "R Ste"
- 插入字符串
insert 成员函数可以在 string 对象中插入另一个字符串,返回值为对象自身的引用。
string s1("Limitless"), s2("00");
s1.insert(2, "123"); //在下标 2 处插入字符串"123",s1 = "Li123mitless"
s1.insert(3, s2); //在下标 2 处插入 s2 , s1 = "Li10023mitless"
s1.insert(3, 5, 'X'); //在下标 3 处插入 5 个 'X',s1 = "Li1XXXXX0023mitless"
- 字符串转int、double、long
string str = “3456”;
int a = stoi(str);
cout << a << endl
strtod(p, ppend) //从字符串 p 中转换 double 类型数值,并将后续的字符串指针存储到 ppend 指向的 char* 类型存储。
strtol(p, ppend, base) //从字符串 p 中转换 long 类型整型数值,base 显式设置转换的整型进制,设置为 0 以根据特定格式判断所用进制,0x, 0X 前缀以解释为十六进制格式整型,0 前缀以解释为八进制格式整型
atoi(p) //字符串转换到 int 整型
atof(p) //字符串转换到 double 符点数
atol(p) //字符串转换到 long 整型
- int转字符串
int a = 345;
string str = to_string(a);
cout <<str << endl
- 迭代器操作字符串
string s("afgcbed");
string::iterator p = find(s.begin(), s.end(), 'c');
if (p!= s.end())
cout << p - s.begin() << endl; //输出 3
sort(s.begin(), s.end());
cout << s << endl; //输出 abcdefg
next_permutation(s.begin(), s.end());
cout << s << endl; //输出 abcdegf
- 字符检查
isalpha() 检查是否为字母字符
isupper() 检查是否为大写字母字符
islower() 检查是否为小写字母字符
isdigit() 检查是否为数字
isxdigit() 检查是否为十六进制数字表示的有效字符
isspace() 检查是否为空格类型字符
iscntrl() 检查是否为控制字符
ispunct() 检查是否为标点符号
isalnum() 检查是否为字母和数字
isprint() 检查是否是可打印字符
isgraph() 检查是否是图形字符,等效于 isalnum() | ispunct()
- 大小写字符转换
string s = "ABCDEFG";
//下标
s[i] = tolower(s[i]); //转换成小写字母
s[i] = toupper(s[i]); //转换成大写字母
for( int i = 0; i < s.size(); i++ )
{
s[i] = tolower(s[i]);
}
//迭代器
*it1 = tolower(*it1); //转换成小写字母
*it1 = toupper(*it1); //转换成大写字母
for (auto it1 = str.begin(); it1 != str.end(); it1++)
{
*it1 = tolower(*it1);
}
//STL算法
transform(s.begin(),s.end(),s.begin(),::tolower);
transform(s.begin(),s.end(),s.begin(),::toupper);