STL&数据结构&algorithm函数:数据结构 = 定义一种性质并维护这个性质(重要)
string各种:基础语法
更多的使用STL来避免错误操作
all的STL也支持结构体类型
all 的数据结构一定清楚是干嘛的
且实现相同功能的东西,即我们所说的结构(如实现了先进后出结构,即我们常说的栈)
数据结构 = 定义一种性质并维护这个性质(重要)
代码很重要,很多高级数据结构的理解都是由代码来进行更深层次理解的
代码很重要,很多高级数据结构的理解都是由代码来进行更深层次理解的
hash:Hash
简易分块: 对于打不出线段树情况,可以采用简易分块(线段数复杂度优于分块)
可以很少的代码实现:如区间求和,区间+单点求值···的问题,区间数颜色,区间重数等
范围遍历:范围遍历&结构化绑定
一些algorithm函数:algorithm函数
pair< ,>:二元组,可以使用两个类型
使用pair
vector<pair<int, int>> e[maxn];
e[u].emplace_back(v, w);//有一条u到v边权为w的有向边
其等价于.emplack_back({v, w});
tuple元组:三个或三个以上的pair
bitset:只有0/1的数组
可以操控其中每一个比特位,可以左移和右移
bitset<100> s;//其中<>中长度必须是编译时确定的常量
b.set(0, 1), b.set(1, 0);
等价于b[0] = 1, b[1] = 0;//可以操作每一个比特位
b <<= 2, b >>= 3;
自带函数
.count();//统计其中多少位是1
.all();//其中全都是1
.any();//至少有一个1
.none();//是不是都是0
<b[1] =1时,b左1位时,b[0] = 1;
bitset(二进制串)支持位运算操作(与或非,按位取反)
底层是几个unsigned long long实现,动64位底层动两个unsigned long long
10101
01010
按位&:00000
按位|:11111
bitset<64> a, c;//两个集合做位运算
a[2] = 1;
c |= a;
cout << c[2];//输出1,即位运算
时复O(n),但其中会把32/64个比特压到一个int中,所以实际效率O(n/w), w = 32/64
lower_bound()和upper_bound()也可在有序容器中使用,也要考虑地址溢出begin()前和end()都为不合法区间
但可能要使用STL其本身自带的lower/upper_bound()
lower_bound() :查找第一个大于等于x的数
当存在不降序序列时,调用lower_bound()来找第一个大于等于x的数
for(int i = 1; i <= 4; ++ i) {
//lower_bound返回的是地址:如2345578
printf("%d ", lower_bound(a.begin(), a.end(), x));
//*取值
printf("%d ", *lower_bound(a.begin(), a.end(), x));
}
//2 3 3 4
//依次返回2 2 3 4
注意,当lower_bound()查询一个不存在的元素时,返回的是a.end()即尾地址下一个地址/迭代器
auto ex = lower_bound();
if(ex != a.end()) cout << *ex;
else cout << -1 << endl;//要对不存在情况进行处理
lower_bound()查找比x小的最大的数:
因为lower_bound()会查询第一个大于等于,那么其前面一个一定是严格小于,即小于x的最大的数
upper_bound():查找第一个大于x的数
//同lower_bound
//对于2 2 3 4
//依次返回2 3 4 -1
查询下标:(ret当前元素地址 - begin()开始地址)即数在数组里面的下标
对于begin,第一个为a+0, 对x来说则为a + x, 所以可以通过a+x-a+0得到下标
栈,队列,链表:操作不多情况下也可以deque模拟栈和链表
栈经常用于函数递归,以及DFS的实现
队列经常用于BFS的实现
链表用于解决杂项问题(数组下标不是连续)
(栈,队列为空时,查询顶部元素会出错)
栈(Last In First Out):应用:单调栈&表达式树(处理单调问题)
避免p指向当前指针p = 0时,无法判断栈是否为空,这里栈定义:指向下一个待插入位置
stack<类型> s;
.push();
.pop();//删栈顶
.top();//查栈顶
.empty();//空为true,否则为false
.size();//元素个数
点击查看代码
push() {
a[p] = x, ++ p;
}
pop() {
-- p;
}
top() {
return a[p - 1];
}
通过不断弹出栈中元素实现反转序列
队列:a[l,r) 单调队列:滑动窗口&循环队列
l = r = 0;
r-h=当前对内人数
l为当前队头,r为当前队尾的下一个
q.push(q.front());q.pop();操作:使得队列循环
queue<类型> q;
.push();
.pop();//删队首
.front();//查询队首
.back();//查询队尾
.size();
.empty();
点击查看代码
push() {
q[r] = x;
++ r;
}
pop() {
++ l;
}
front() {
return q[l];
}
堆:维护了一个优先队列
优先队列priority_queue(默认大根堆) : 优先队列
优先队列不支持begin,end
双端队列:允许在两端进行插入和删除
deque<int> dq;
.push_back(1);
.push_front(2);
cout << dq.front(); // 输出 2
.pop_front();
.pop_back();
可变长数组vector
vector实践用法:实际用法
局部变量下定义合法空间下支持随机访问:如vector
vector<类型> v;
vector<int> v(N, i);//初始N个为i的元素
v[x];//访问下标为a元素
.emplace_back(x);//同push_back()但更优
.push_back(x);//将x插入末尾
.size();//返回元素个数
.resize(n, m);//调整数组大小为n,n比原来大,则新增部分初始化为m
vector套vector:
vector<vector
链表:结构体数组链表的逻辑
链表里面可以进行排序吗?
rxz:可以考虑对链表进行归并操作(常规操作) ,链表插入排序也是常操
核心是通过next[]数组,存储和看我的下一位是谁
循环链表:循环链表
连接两个单链表:
链表有方向,则找到A链表的结尾元素,把它连到B链表的起始元素

不同问题对应着不同链表,所以手写链表更具灵活性
结构体改进:
点击查看代码
//list
struct Node{
int val, nxt;//存储点值和下一个是谁
};
Node node[100010];
//double list
struct Node{
int val, nxt, pre;//点值,下一个,上一个
};
Node node[100010];
链表初始化与输出
//对于原始有元素1的初始化
node[0].nxt = 1;//相当于head
node[1].pre = 0, node[1].nxt = 0, node[1].val = 1;
//输出
for (int i = node[0].nxt; i != 0; i = node[i].nxt) cout << node[i].val << ' ';
对于约瑟夫问题:不仅可以用队列出队入队实现,也可以用链表
链表支持往后找k个人,且知道我下一个是谁(我是k-1连向k+1,删除第k个)
map(键值对)集合:加强数组,下标更灵活(一定是先找到存在,再输出值)
map也可以处理负数索引,正常需要2*n来使得索引为正x + i
|A|:为集合A中元素个数
语法糖:
map<int,int>::iterator it 是一个迭代器
it -> first// 相当于(*it).first
it -> second// 相当于(*it).second
二分左右
it = lower_bound(x);
查询比我大小:
(-- it)->first即比我小,(it++)->first比我大
要判断是否等于begin()和end();
it->first不是我后面第一个数,而是我的第一个值(pair)
(map内部实现为pair),所以支持first和second
map会按照需求来使用空间,未使用空间不消耗,所以可以使用1e9这种下标(大小上限由其类型决定)
数组:1,2···1e9一共开辟1e9空间大小
map:1,2,1e9一共使用3空间大小
map<int, int> a;
map<string, int> a;
a[x] = y;//将a[x]值设置为y
a.find(x)!=a.end();//判断a[x]是否存在
a.erase(x);//删除a[x]
a.clear;
a.size();//返回map中元素个数
不同于数组插入删除On,查询修改O1,map的插,查,删都为nlogn
对于map数组
map<string, int>q;//q[s] = x;
map<string, int>q[N];//q[i][s] = x;//即第一维是第一个数组,第二维是字符串
map<string,vector<string>>mp;第二维是vector
使用:
for(auto &val : mp) {
//此时val.first为k
for(auto v : val.second){
//此时val.second为vector[k]中存储值
}
}
unordered_map:类同map:离散化
set(单一键)集合:set平衡树
操作时复O(logn)
set<int> st;
.insert(1);//插入
.find(1) != st.end()
.erase(1);//删除
.clear();//清空
for(auto &x : st) cout << x;
multiset(可重复)集合: 类set
当我的集合想有重复元素时,使用multiset
.erase(x);//会将allx都删除
erase()另一种用法是删除某个特定迭代器,当我们想只删除某一个特定的值,找到对应迭代器,删除迭代器即可
当然也要确保x一定出现,当出现erase(.end())则程序RE
.find(x);//返回等于x的迭代器,无x则返回end()
.erase(.find(x));//即可删除某一个特定的值(将位置删掉,而不是将all等于x删掉)
其中multiset的.count(x)的时复为O(logn + 返回值大小):当都为x时,则为O(n)
时间非常糟糕,用其他方法实现:
multiset<piar<int, int>> s;//存数和数出现次数
想取数次数时,找到数取次数即可,当某数+1时,找到数对应位置,将数的pair进行erase,次数+1然后insert,此时即O(logn)count一个数
unordered_set:类set
可以当成set用,all操作O(1),但常数比较大,不要在CF上用会被卡,自定义类型用unordered_set要制定一个hash函数
浙公网安备 33010602011771号