前言
本系列是我学习完大佬的方法后,为了蓝桥杯前几天可以快速过一遍所做,所以部分内容会很简洁。如果能够帮助到你,我也会很开心!!!
单调栈
对于每个数,观察他的左边比他大的数的第一个坐标是什么,如果有的话就输出那个数的坐标,如果没有的话就输出-1(找比他大的值的坐标)
for (int i = 0; i <= n; i++)
{
while (top && a[stk[top] <= a[i]]) top--;
if (!top) printf("-1 ");//如果栈空,则没有比该元素小的值。
else printf("%d ", stk[tt]);
skt[++top] = i;
}
单调队列
常用题目:滑动窗口/求某个区间的最小值
for (int i = 1; i <= n; i++)
{
while (hh <= tt && q[hh] < i - k + 1) hh++;//判断当前区间是否合法
while (hh <= tt && a[q[tt]] > a[i]) tt--;
q[++tt] = i;//存入下标
}
其他:
单调队列和单调栈中核心操作中都有存入下标a[++b]=i的操作,只不过单调栈是在队头存,单调队列是在队尾存
ST表(感觉考的可能性不大)
常用题目:区间最值查询
步骤:分解区间,可以分解成两个区间,把[L,R]分为两部分
预处理的时候要提前判断区间的合法性
核心代码:
int Maxnum(int l, int r)
{
int k = log(r - 1 + 1) / log(2);// 找到最大的j,使得2^j <= r - l + 1
return max(st(l][k], st[r - (1 << k) + 1][k]);// 比较两个区间的最值
}
堆
大跟堆/小跟堆可以用优先队列来快速实现,优先队列默认是大跟堆,小跟堆表示方法:
priority_queue<int,vector<int>,greater<int> >q;
向上更新
void up(int u)
{
while (u != 1)//只要现在的点不是根节点就可以遍历
{
if (a[u] > a[u/2]) //如果儿子大于父亲
swap(a[u], a[u/2]);
u >>= 1;
}
}
向下更新
void down(int u)
{
int t = u;
if (u * 2 <= size && h[u * 2] < h[t]) t = 2 * u;//左孩子如果小于父亲,继续向下遍历
if (u * 2 + 1 <= size && h[u * 2] + 1 < h[t]) t = 2 * u + 1;//右孩子如果小于父亲,继续向下遍历
if (u != t)
{
swap(h[u],h[t]);
down(t);
}
}
并查集
核心操作:
1.合并
p[root(a)] = root(b);
2.路径压缩:
int root(int x)
{
if (p[x] != x) p[x] = root(p[x]);//如果现在的点不是根节点,就继续向上遍历
return p[x];
}
树状数组
作用:
单点更新和区间查询
须知
lowbit(x)是求x的二进制表达式中最低位的1所对应的值
int lowbit(int x)
{
return x & -x;
}
核心代码
单点更新(增加值)
void update(int k, int x)
{
for (int i = k; i <= n; i += lowbit(i))
t[i]++;
}
区间查询
//返回区间[l,k]的值
int query(int k)
{
int sum = 0;
for (int i = k; i <= n; i -= lowbit(i))
sum+=bit[i]++;
return sum;
}
trie树(字典树)
可用题目:
查找前缀相同的单词等
核心代码:
插入一个数
void insert(int x)
{
int u = 0;
for(int j=n;j>=0;j--)
{
if (!ch[u][str[i] - 'a'])
ch[u][str[i] - 'a'] = ++tot;
now = ch[u][str[i] - 'a'];
}
cnt[x]++;
}