目录
pair
string
queue
priority_queue:优先队列
stack
deque--双端队列
set -- multiset
map -- multimap
无序关联容器
bitset:压位
上篇vector指路
stl : vector
我们之前用到过一次,当时对他有了基本的了解,这里再回顾一下就好。内容不是很多。
pair用来存储两个相关联的数据,且这两个数据的数据类型可以不同。常说pair用来存储键值对。其使用时需要引头文件 #include
pair的实现是一个结构体,第一个元素由 .first 进行索引,第二个元素使用 .second 索引。(注意这里使用时后面不用加括号()哦 )
pair的创建
pair p;
p={2,"ax"};
pairq={1,2};
pairr(1,1);
auto s=make_pair('c',"sdv");
//make_pair函数的主要优点是它可以自动推断出pair的类型,不需要显式地指定pair的类型
pair> t;
//嵌套的 pair 可以用来组织和表示具有层次结构的数据,其中不同层次的信息被封装在不同的元素中
注意写的格式,哪里是() 哪里是{ } 区分清楚
p.first;//获取第一个元素
p.second;//获取第二个元素
当两个pair要进行比较的时候,一般是按照字典序,以第一个元素优先比较,当两个pair的第一个元素相同时,才会比较第二个元素。
string也算比较常用了。其头文件是 #include
string c;
c.size();
c.empty();
c.length();//和size一样
c+='c';
c+="vf";
//如果我们是以string创建字符串(也就相当于一个字符数组),用+=运算符来读入下一个字符,实际上是字符/字符串的连接
//这可以类比为逐个字符读取并将其连接到字符串的过程,因为每次读取一个字符,都将其追加到字符串的末尾
//c++中的substr()函数,第一个参数是起始位置,第二个参数是子串长度。 但大部分其它语言中第二个参数表示截至位置
c.substr(1,2);//返回第1-2个字符
c.substr(1);//直接返回从第一个字符开始的整个子串
c.substr(1,5);
//如果我们第二个参数的位置写出的数字要大于整个字符串的长度,那么会输出从第一个参数所对数字的位置开始,到字符串的末尾
cout<
常用的就都写在代码里了。
当然它也包含 size()计算字符串大小(不包含末尾结束符 '\0' ) / clear()清空字符串 / empty() 判断字符串是否为空
但是c语言中的sizeof会包含 '\0' ,因为C风格的字符串是一个字符数组,数组的最后一个元素是结束标志\0。sizeof运算符返回的是整个数组(包括\0)所占用的字节数。
注意区分:strcmp 也是我们比较字符串常用的函数。这里要分清楚,在c++中使用该函数,要引头文件 #include
队列遵循先进先出
queue q;//创建了一个元素都为int类型的队列
q.size();
q.empty();
q.back();//返回队列末尾元素
q.pop();//弹出队头元素
q.push(1);//向队尾插入元素
q.front();//返回队头元素
//queue没有自带的clear() 清空队列元素的函数
//如果想要清空
q=queue();
注意它没有自带的clear()函数
其头文件是#include
其是靠堆来实现的。需要回顾的可以看一下
数据结构:堆
要注意的是,优先队列的堆:默认是大根堆。
没有自带的clear()函数
优先队列的创建
priority_queue q;//创建了一个元素是int类型的优先队列,即大根堆
//如果想要创建小根堆,可以在插入元素的时候,插入原数的负数形式
q.push(-x);
//也可以
priority_queue,greater> q;//注意引vector的头文件
//这样所创建的就是一个小根堆
注意其头文件也是 #include
常用函数
q.empty();
q.size();
q.pop();//弹出堆顶
q.push(1);//插入一个元素,会自动插入到符合堆的性质的位置
q.top();//返回堆顶元素
栈遵循先进后出
stack s;
s.pop();//弹出栈顶元素
s.push(1);//向栈顶插入一个元素
s.top();//返回栈顶元素
s.size();
s.empty();
头文件是 #include
没有内置clear() 函数
其实是一个加强版的vector。很多函数都包含,但是由于速度实在太慢,不太常用。
deque d;
d.size();
d.empty();
d.clear();//清空队列
d.back();//返回最后一个元素
d.front();//返回队首元素
d.pop_back();//弹出最后一个元素
d.push_back(1);//在队尾插入一个元素
d.pop_front();//弹出队首元素
d.push_front(2);//在队头插入元素
d[1];//支持数组形式的随机寻址
//支持迭代器
d.begin();
d.end();
头文件 #include
这里强调一下随即寻址d[1] ,vector也是支持随机寻址的。
使用时头文件 #include
set s;//如果我们向set中插入一个重复的元素,那程序会忽略掉这个操作
multiset q;//multiset中可以有重复元素
s.size();
s.empty();
s.clear();
s.insert(1);//插入元素
s.insert(2);
s.erase(1);
//这里我们写的是删除值为1的元素,那么会删除所有值为1的元素
//如果我们传入的参数写的是该元素的迭代器,那么会删除之后并回下一个元素的迭代器
//注意如果要删除一个范围内的元素的erase函数,那么应该使用参数是两个迭代器,而不是数字
s.erase(s.begin(),s.begin()++);//删除该区间内的元素,并返回下一个元素的迭代器
s.upper_bound(1);//返回第一个>1的元素的迭代器
s.lower_bound(1);//返回第一个>=1的元素的迭代器
s.count(1);//返回某个元素的个数
s.find(1);//查找一个数,如果这个数存在,就返回其迭代器,若不存在返回set.end()
s.begin();
s.end();
关于迭代器:我们上篇里说过:
这里补充迭代器可以进行的操作,这里 set 和 multiset 以及下面要说的 map 和 multimap 的迭代器都是可以进行 ++ -- 操作的
这和上面的set组合基本函数都差不多。这里不在重复写了。
头文件是 #include
map和multimap是两种常用的关联式容器,它们都是基于键值对的数据结构,这两种容器的主要区别在于,map不允许两个元素有相同的键值,而multimap允许键值重复
set组合和map组合的区别
也就是他们的区别是一个是存储两个值(要使用pair),一个只存储一个值。
map的创建以及键值的映射关系
//类似于数组,但是map这样的寻址时间复杂度是O(logn) 而数组是O(1)
//map的第一个参数是“键” 第二个参数是“值”
map a;
a[1]=2;
map b;
b["cd"]=1;
map c;
c[2]="fe";
函数
a.size();
a.empty();
a.clear();
a.find(10);//查找以该元素为“键”的键值对的迭代器
a.insert(pair(10,10));//函数参数是pair类型的键值对
a.count(10);//参数是以该值为“键”的元素个数
a.erase(a.begin());//参数是一个迭代器
//同样也有两个迭代器参数删除一整个区间元素的写法,返回的是下一个元素的迭代器
a.upper_bound(10);
a.lower_bound(10);//对于这两个函数参数是“键”,返回迭代器
unordered_set
unordered_map
unordered_multiset
unordered_multimap
这四个名字很直接,就是表示是无序的。
无序容器的底层实现是哈希表,因此它们的插入、删除和查找操作的平均时间复杂度都是O(1)。然而,由于它们是无序的,所以不支持lower_bound()、upper_bound()函数,也不支持迭代器的++和--操作。
bitset 是一个位集合的模板类,用于表示固定大小的位序列。bitset 以非常高效的方式存储位信息,每个位都只占用一个比特(0或1),并且支持各种位运算操作。“压位”通常指的是使用 bitset 进行位操作。
我们可以把它看作是一个数组,只不过这个数组的每个元素只能是0或1(即二进制位)。
这是洛谷上一篇关于bitset的文章,感觉写的很详细,可以看一下
扶苏的bitset浅谈
常用函数
bitset<1000> b;
b.any();//判断是否至少有一个1
b.none();//判断是否全为0
b.count();//返回1的个数
b.set();//把所有位置置1
b.set(1);//将第一个位置置1
b.set(k,v);//将第k位置为v
b.reset();//把所有位置置0
b.reset(1);//将第一个位置置0
b.flip();//将所有位取反~
b.flip(k);//把第k位取反
我们上面所提到的容器,<>尖括号内部写的都是元素类型,但是bitset<>尖括号里写的是元素个数。
bitset有一些好处:
1. 空间上的优化:bitset中的每个元素只占1 bit,相当于一个char元素所占空间的八分之一。这使得bitset在处理大量二进制数据时,比使用bool数组或其他数据结构更加节省空间。
2.位运算上的便利:bitset重载了许多二进制运算符,如&、|、^、~ 、>>、<< 等使其支持类似于整数类型的位运算操作。而且可以处理任意长度的二进制数据,而整数类型的长度是固定的。这里主要是指编程的便利性和代码的可读性,而不是运行时的性能优化。
3含有很多内置函数:方便各种操作。
例如,假设要实现一个功能,需要跟踪1000个不同的选项,每个选项都可以打开或关闭。可以使用一个bitset来表示这些选项的状态,其中每一位代表一个选项,1表示选项打开,0表示选项关闭。这样,就可以使用一个bitset对象来代替1000个单独的bool变量。
这里给出bitset快速进行二进制与十进制的转换的代码
#include
#include
using namespace std;
int main() {
bitset<8> bs1(42); // 将整数42转换为二进制
cout << bs1 << endl; // 输出 "00101010"
bitset<8> bs2("11010101"); // 将二进制字符串转换为bitset
cout << bs2.to_ulong() << endl; // 输出 "213"
//to_ulong()成员函数可以将bitset对象转换为一个无符号长整型数
return 0;
}
好了,第二部分数据结构结束了。
有问题欢迎指出!一起加油!!!