算法基础课 acwing

2、双指针

image-20210823141957365

1、i往后面扫描,当i指向的数字小于等于x,就继续往后,当指向的数字大于x的时候停下

2、此时j往前面扫描,当j指向的数字大于x,就继续往前,当指向的数字小于x的时候,停下

3、此时i指向的大于x,j指向的小于x,那么这两个数字交换,并且两个指针都往中间移动一位,直到i和j穿过

可以发现,任何时候,i右边的数字都小于等于x,j左边的数字都大于x

模板:

void quick_sort(int q[],int l,int r){

if(l >= r) return; // 如果区间内只有一个或者没有数字,跳出循环

int x = q[l + r >> 1],i = l -1,j = r + 1;

while(i < j){

do i++; while(q[i] < x);

do j–; while(q[j] > x);

if(i < j) swap(q[i],q[j]);

}

quick_sort(q,l,j);

quick_sort(q,j+1,r);

}

快速排序

https://www.acwing.com/problem/content/787/

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 10;

int q[N];

void quick_sort(int q[],int l,int r){

if(l >= r) return;

int x = q[l + r >> 1],i = l -1,j = r + 1;

while(i < j){

do i++; while(q[i] < x);

do j–; while(q[j] > x);

if(i < j) swap(q[i],q[j]);

}

quick_sort(q,l,j);

quick_sort(q,j+1,r);

}

int main(){

int n;

scanf(“%d”,&n);

for(int i = 0;i < n;i++) scanf(“%d”,&q[i]);

quick_sort(q,0,n-1);

for(int i = 0;i < n;i++) printf("%d ",q[i]);

return 0;

}

第k个数

https://www.acwing.com/problem/content/788/

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int a[N],n,k;

int quick_sort(int l,int r,int k){

if(l == r) return a[l];

int i = l - 1,j = r + 1,x = a[(l+r) >> 1];

while(i < j){

while(a[++ i] < x); // 这里只能是++i,不能是i–

while(a[-- j] > x);

if(i < j) swap(a[i],a[j]);

}

int sl = j - l + 1;

if(k <= sl) return quick_sort(l,j,k);

else return quick_sort(j+1,r,k-sl);

}

int main(){

scanf(“%d%d”,&n,&k);

for(int i = 0 ;i < n;i++) scanf(“%d”,&a[i]);

printf(“%d\n”,quick_sort(0,n-1,k));

return 0;

}

快速选择算法

image-20210829144929837

归并排序

image-20210823145441744

如何归并

双指针

image-20210823145721003

image-20210823150047410

归并的思路:

1、先递归,将序列一直分半,直到只剩一个元素

2、归并,归并采取双指针

模板:

void merge_sort(int q[],int l,int r){

if(l >= r) return; // 如果区间只有一个或者没有数字,不用递归了

int mid = (l + r) >> 1; // mid是中间那个数字

merge_sort(q,l,mid); // 归并排序先递归

merge_sort(q,mid+1,r); // 分到最后就只剩一个数字了

int k = 0,i = l,j = mid + 1; // 归并,使用双指针,i指向左半边的起点,j指向右半边的起点

while(i <= mid && j <= r){

if(q[i] <= q[j]) tmp[k++] = q[i++];

else tmp[k++] = q[j++];

}

while(i <= mid) tmp[k++] = q[i++];

while(j <= r) tmp[k++] = q[j++];

for(i = l,j = 0;i <= r;i++,j++) q[i] = tmp[j];

}

归并排序

https://www.acwing.com/problem/content/789/

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 10;

int q[N],tmp[N],n;

void merge_sort(int q[],int l,int r){

if(l >= r) return; // 如果区间只有一个或者没有数字,不用递归了

int mid = (l + r) >> 1; // mid是中间那个数字

merge_sort(q,l,mid); // 归并排序先递归

merge_sort(q,mid+1,r); // 分到最后就只剩一个数字了

int k = 0,i = l,j = mid + 1; // 归并,使用双指针,i指向左半边的起点,j指向右半边的起点

while(i <= mid && j <= r){

if(q[i] <= q[j]) tmp[k++] = q[i++];

else tmp[k++] = q[j++];

}

while(i <= mid) tmp[k++] = q[i++];

while(j <= r) tmp[k++] = q[j++];

for(i = l,j = 0;i <= r;i++,j++) q[i] = tmp[j];

}

int main(){

scanf(“%d”,&n);

for(int i = 0;i < n;i++) scanf(“%d”,&q[i]);

merge_sort(q,0,n-1);

for(int i = 0;i < n;i++) printf("%d ",q[i]);

return 0;

}

逆序对的数量

https://www.acwing.com/problem/content/790/

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int a[N],temp[N];

long long merge_sort(int l,int r){

if(l >= r) return 0;

int mid = (l + r) >> 1;

long long res = merge_sort(l,mid) + merge_sort(mid+1,r);

// 归并

int k = 0,i = l,j = mid + 1;

while(i <= mid && j <= r) {

if(a[i] <= a[j]) temp[k++] = a[i++];

else {

temp[k++] = a[j++];

res += mid - i + 1;

}

}

while(i <= mid) temp[k++] = a[i++];

while(j <= r) temp[k++] = a[j++];

for(i = l,k = 0;i <= r;i++,k++) a[i] = temp[k];

return res;

}

int main(){

int n;

scanf(“%d”,&n);

for(int i = 0;i < n;i++) scanf(“%d”,&a[i]);

printf(“%lld\n”,merge_sort(0,n-1));

return 0;

}

image-20210829151700702

二分

模板:https://www.acwing.com/blog/content/31/

bool check(int x) {/* … */} // 检查x是否满足某种性质

// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:

int bsearch_1(int l, int r)

{

while (l < r)

{

int mid = l + r >> 1;

if (check(mid)) r = mid; // check()判断mid是否满足性质

else l = mid + 1;

}

return l;

}

// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:

int bsearch_2(int l, int r)

{

while (l < r)

{

int mid = l + r + 1 >> 1;

if (check(mid)) l = mid;

else r = mid - 1;

}

return l;

}

image-20210823154253988

数的范围

https://www.acwing.com/problem/content/791/

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int q[N],m,n;

int main(){

scanf(“%d %d”,&n,&m);

for(int i = 0;i < n;i++) scanf(“%d”,&q[i]);

while(m–){

int x;

scanf(“%d”,&x);

// 先不管check函数,先把l,r和while,mid写好

int l = 0,r = n - 1;

while(l < r){

int mid = (l + r) >> 1;

if(q[mid] >= x) r = mid;

else l = mid + 1;

}

if(q[l] != x) puts(“-1 -1”);

else{

printf("%d ",l);

l = 0,r = n - 1;

while(l < r){

int mid = (l + r + 1) >> 1;

if(q[mid] <= x) l = mid;

else r = mid - 1;

}

printf(“%d\n”,l);

}

}

return 0;

}

浮点数二分

image-20210823160402292

开平方

#include <bits/stdc++.h>

using namespace std;

int main(){

double x;

cin >> x;

double l = 0,r = x;

while(r - l > 1e-6){

double mid = (l + r) / 2;

if(mid * mid >= x) r = mid;

else l = mid;

}

printf(“%lf\n”,l);

return 0;

}

数的三次方根

https://www.acwing.com/problem/content/792/

#include <bits/stdc++.h>

using namespace std;

int main(){

double n;

scanf(“%lf”,&n);

double l = -10000,r = 10000;

while(r-l > 1e-8){

double mid = (l + r) / 2;

if(mid * mid * mid > n) r = mid;

else l = mid;

}

printf(“%lf\n”,l);

return 0;

}

高精度

前缀和

一维前缀和

image-20210824140018283

前缀和

https://www.acwing.com/problem/content/797/

预处理:O(n)

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int a[N],s[N];

int main(){

int n,m;

scanf(“%d%d”,&n,&m);

for(int i = 1;i <= n;i++) {

scanf(“%d”,&a[i]);

s[i] = s[i-1] + a[i]; // 前缀和数组的初始化

}

while(m–){

int l,r;

scanf(“%d%d”,&l,&r);

printf(“%d\n”,s[r]-s[l-1]);

}

return 0;

}

二维前缀和

S[i,j]的含义:这个格子左上角面积之和

image-20210824142157204

S[i,j]计算方法:

image-20210824142941774

(x1,y1),(x2,y2)子矩阵中所有数之和怎么计算?

image-20210824143910149

子矩阵的和

https://www.acwing.com/problem/content/798/

#include <bits/stdc++.h>

using namespace std;

const int N = 1e3 + 10;

int a[N][N],s[N][N];

int main(){

int n,m,q;

scanf(“%d%d%d”,&n,&m,&q);

for(int i = 1;i <= n;i++){

for(int j = 1;j <= m;j++){

scanf(“%d”,&a[i][j]);

// 初始化二维前缀和

s[i][j] = a[i][j] + s[i-1][j] + s[i][j-1] - s[i-1][j-1];

}

}

while(q–){

int x1,y1,x2,y2;

scanf(“%d%d%d%d”,&x1,&y1,&x2,&y2);

// 计算差

printf(“%d\n”,s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1]);

}

return 0;

}

差分

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z6zhrdDJ-1630933432550)(https://gitee.com/Crescent_P/picture-bed/raw/master/hh.png)]差分数组不需要构造

一维差分

https://www.acwing.com/problem/content/799/

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int a[N],b[N];

// 插入函数,使得b[l] + c,b[r+1] - c

void insert(int l,int r,int c){

b[l] += c;

b[r + 1] -= c;

}

int main(){

int n,m;

scanf(“%d%d”,&n,&m);

for(int i = 1;i <= n;i++){

scanf(“%d”,&a[i]);

// 差分数组不需要构建,先假设a数组为全零,那么b数组也就为全零

// [1,1]插入a[1],[2,2]插入a[2]

// 就能构造出差分数组

insert(i,i,a[i]);

}

while(m–){

int l,r,c;

scanf(“%d%d%d”,&l,&r,&c);

insert(l,r,c);

}

for(int i = 1;i <= n;i++) {

a[i] = a[i-1] + b[i];

printf("%d ",a[i]);

}

}

二维差分

image-20210824153105843

image-20210824153839528

差分矩阵

https://www.acwing.com/problem/content/800/

#include <bits/stdc++.h>

using namespace std;

const int N = 1e3 + 10;

int a[N][N],b[N][N];

// 插入公式

void insert(int x1,int y1,int x2,int y2,int c){

b[x1][y1] += c;

b[x1][y2+1] -= c;

b[x2+1][y1] -= c;

b[x2+1][y2+1] += c;

}

int main(){

int n,m,q;

scanf(“%d%d%d”,&n,&m,&q);

for(int i = 1;i <= n;i++){

for(int j = 1;j <= m;j++){

scanf(“%d”,&a[i][j]);

// 构建差分数组b

insert(i,j,i,j,a[i][j]);

}

}

while(q–){

int x1,y1,x2,y2,c;

scanf(“%d%d%d%d%d”,&x1,&y1,&x2,&y2,&c);

insert(x1,y1,x2,y2,c);

}

for(int i = 1;i <= n;i++){

for(int j = 1;j <= m;j++){

a[i][j] = b[i][j] + a[i-1][j] + a[i][j-1] - a[i-1][j-1];

printf("%d ",a[i][j]);

}

printf(“\n”);

}

return 0;

}

双指针算法

image-20210829160939604

输出单词

#include <bits/stdc++.h>

using namespace std;

int main(){

char str[1000];

gets(str);

int n = strlen(str);

for(int i = 0;i < n;i++){

int j = i;

while(j < n && str[j] != ’ ') j++;

// 这道题的具体逻辑

for(int k = i;k < j;k++) printf(“%c”,str[k]);

puts(“”);

i = j;

}

return 0;

}

最长连续不重复子序列

https://www.acwing.com/problem/content/801/

暴力:O(n^2)

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int a[N],b[N];

int main(){

int n;

scanf(“%d”,&n);

for(int i = 0;i < n;i++) scanf(“%d”,&a[i]);

int ans = 0;

for(int i = 0;i < n;i++){

b[a[i]]++;

for(int j = i+1;j < n;j++){

if(b[a[j]] != 0) break;

b[a[j]]++;

ans = max(ans,j - i + 1);

}

b[a[i]]–;

for(int i = 0;i < n;i++) b[i] = 0;

}

printf(“%d\n”,ans);

return 0;

}

双指针:O(n)

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int a[N],b[N];

int main(){

int n;

scanf(“%d”,&n);

for(int i = 0;i < n;i++) scanf(“%d”,&a[i]);

int ans = 0;

for(int i = 0,j = 0;i < n;i++){

while(j < n && b[a[j]] == 0){

ans = max(ans,j - i + 1);

b[a[j++]]++;

}

b[a[i]]–;

}

printf(“%d\n”,ans);

return 0;

}

数组元素的目标和

https://www.acwing.com/problem/content/802/

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int a[N],b[N],n,m,x;

int main(){

scanf(“%d%d%d”,&n,&m,&x);

for(int i = 1;i <= n;i++) scanf(“%d”,&a[i]);

for(int j = 1;j <= m;j++) scanf(“%d”,&b[j]);

for(int i = 1,j = m;i <= n ;i++){

while(j <= m && a[i] + b[j] > x) j–;

if(a[i] + b[j] == x) {

printf(“%d %d”,i-1,j-1);

break;

}

}

return 0;

}

判断子序列

https://www.acwing.com/problem/content/description/2818/

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int n,m;

int a[N],b[N];

int main(){

scanf(“%d%d”,&n,&m);

for(int i = 1;i <= n;i++) scanf(“%d”,&a[i]);

for(int j = 1;j <= m;j++) scanf(“%d”,&b[j]);

bool flag = true;

for(int i = 1,j = 1;i <= m && flag;i++){

while(j <= n && b[i] == a[j]) j++,i++;

// 移动到最后一位后面了

if(j == n+1) flag = false;

}

if(flag) puts(“No”);

else puts(“Yes”);

return 0;

}

位运算

haha

可以求出10的二进制表示

#include <bits/stdc++.h>

using namespace std;

int main(){

int n = 10;

for(int k = 3;k >= 0;k-- ) printf(“%d”,(n>>k)&1);

return 0;

}

lizi

lowbit运算

image-20210830153204218

image-20210830153435293

二进制中1的个数

https://www.acwing.com/problem/content/803/

#include <bits/stdc++.h>

using namespace std;

int lowbit(int x){

return x & -x;

}

int main(){

int n;

scanf(“%d”,&n);

while(n–){

int x;

scanf(“%d”,&x);

int res = 0;

// 每次减去x的最后一位1

while(x) x -= lowbit(x), res++;

printf("%d ",res);

}

return 0;

}

离散化

lisanhjua

image-20210830154951470

区间和

https://www.acwing.com/problem/content/804/

#include <bits/stdc++.h>

using namespace std;

typedef pair<int,int> PII;

const int N = 3e5 + 10;

int n,m;

int a[N],s[N];

vector alls;

vector add,query;

// 通过x的值找到下标,将一个值序列,映射到从1开始的数组

int find(int x){

int l = 0,r = alls.size() - 1;

while(l < r){

int mid = (l + r) >> 1;

if(alls[mid] >= x) r = mid;

else l = mid + 1;

}

return r + 1;

}

int main(){

scanf(“%d%d”,&n,&m);

for(int i = 0;i < n;i++){

int x,c;

scanf(“%d%d”,&x,&c);

add.push_back({x,c});

alls.push_back(x);

}

for(int i = 0;i < m;i++){

int l,r;

scanf(“%d%d”,&l,&r);

query.push_back({l,r});

alls.push_back(l);

alls.push_back®;

}

// 去重

sort(alls.begin(),alls.end());

alls.erase(unique(alls.begin(),alls.end()),alls.end());

// 上面操作完,是一个去重并且排序后的一个数组

// 处理插入

for(auto item: add) {

int x = find(item.first);

// 上面x是通过值找到的下标,x是下标

a[x] += item.second;

}

// 预处理前缀和

for(int i= 1;i <= alls.size();i++) s[i] = s[i-1] + a[i];

// 处理询问

for(auto item : query) {

int l = find(item.first);

int r = find(item.second);

printf(“%d\n”,s[r] - s[l-1]);

}

return 0;

}

区间合并

merge

区间合并

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2x7KVK5l-1630933432558)(算法基础课.assets/image-20210830163830687.png)]

https://www.acwing.com/problem/content/805/

#include<bits/stdc++.h>

using namespace std;

typedef pair<int,int> PII;

const int N = 1e5 + 10;

int n;

vector segs;

void merge(vector &segs){

vector res;

// 以左端点排序

sort(segs.begin(),segs.end());

int st = -2e9,ed = -2e9;

for(auto seg : segs){

// 没有交集

if(ed < seg.first){

if(st != -2e9) res.push_back({st,ed});

st = seg.first,ed = seg.second;

}else ed = max(ed,seg.second);

}

// 最后一个

if(st != -2e9) res.push_back({st,ed});

segs = res;

}

int main(){

scanf(“%d”,&n);

for(int i = 0;i < n;i++){

int l,r;

scanf(“%d%d”,&l,&r);

segs.push_back({l,r});

}

merge(segs);

printf(“%d\n”,segs.size());

return 0;

}

第二讲 数据结构


单链表

image-20210831153657064

用数组模拟链表

e数组存值,ne数组存next

image-20210831153802072

单链表

https://www.acwing.com/problem/content/828/

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

// head 表示头结点的下标

// e[i] 表示节点i的值

// ne[i] 表示节点i的next指针是多少

// 存储到当前用到了哪个点

int head,inx,e[N],ne[N];

// 初始化

void init(){

// 初始化,链表为空,-1表示空集

head = -1;

inx = 0;

}

// 将x插到头结点,就是让x成为头结点

void add_to_head(int x) {

e[inx] = x;

ne[inx] = head;

// head 表示头结点的下标,head不是一个节点哦

head = inx;

inx++;

}

// 将x插入到下标是k的节点的后面

void add(int k,int x){

e[inx] = x;

ne[inx] = ne[k];

ne[k] = inx;

inx++;

}

// 将下标是k的点的后面的点删除

void remove(int k){

ne[k] = ne[ne[k]];

}

int main(){

int M;

scanf(“%d”,&M);

init();

while(M–){

int k,x;

char op;

cin >> op;

if(op == ‘H’){

scanf(“%d”,&x);

add_to_head(x);

}else if(op == ‘D’){

scanf(“%d”,&k);

// 删除头结点,就是让head指向头结点的下一个

if (!k) head = ne[head];

else remove(k-1);

}else{

scanf(“%d%d”,&k,&x);

add(k-1,x);

}

}

for(int i = head;i != -1;i = ne[i]) printf("%d ",e[i]);

return 0;

}

双链表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jF276k9j-1630933432561)(算法基础课.assets/image-20210831153743826.png)]

image-20210831162815817

双链表

https://www.acwing.com/problem/content/829/

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int e[N],l[N],r[N],idx;

// 初始化

void init(){

// 0 表示右端点

// 1 表示左端点

r[0] = 1,l[1] = 0;

idx = 2;

}

// 在下标是k的点的右边,插入x

void add(int k,int x){

e[idx] = x;

l[idx] = k;

r[idx] = r[k];

r[k] = idx;

l[r[idx]] = idx;

idx++;

}

// 删除第k个点

void remove(int k) {

r[l[k]] = r[k];

l[r[k]] = l[k];

}

int main(){

int M;

scanf(“%d”,&M);

init();

while(M–){

int x,k;

string op;

cin >> op;

if(op == “L”){

scanf(“%d”,&x);

add(0,x);

}else if(op == “R”){

scanf(“%d”,&x);

add(l[1],x);

}else if(op == “D”){

scanf(“%d”,&k);

// idx从2开始,第k个点的下标应该是k+1

remove(k+1);

}else if(op == “IL”){

scanf(“%d%d”,&k,&x);

add(l[k+1],x);

}else{

scanf(“%d%d”,&k,&x);

add(k+1,x);

}

}

for(int i = r[0];i != 1;i = r[i]) printf("%d ",e[i]);

return 0;

}

先进后出

模拟栈

https://www.acwing.com/problem/content/830/

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

// tt 是栈顶坐标

int stk[N],tt;

void push(int x) {

stk[++tt] = x;

}

void pop(){

tt–;

}

bool empty(){

if(tt == 0) return true;

else return false;

}

int query(){

return stk[tt];

}

int main(){

int M;

scanf(“%d”,&M);

while(M–){

string op;

int x;

cin >>op;

if(op == “push”){

scanf(“%d”,&x);

push(x);

}else if(op == “pop”){

pop();

}else if(op == “query”){

printf(“%d\n”,query());

}else{

if(empty()) printf(“YES\n”);

else printf(“NO\n”);

}

}

return 0;

}

队列

先进先出

模拟队列

https://www.acwing.com/problem/content/831/

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5+10;

// 在队尾插入元素,在队头弹出原始

int q[N],hh,tt = -1;

void push(int x) {

q[++ tt] = x;

}

void pop(){

hh++;

}

bool empty(){

if(hh <= tt) return false;

else return true;

}

int query(){

return q[hh];

}

int main(){

int M;

scanf(“%d”,&M);

while(M–){

string op;

int x;

cin >> op;

if(op == “push”){

scanf(“%d”,&x);

push(x);

}else if(op == “pop”){

pop();

}else if(op == “query”){

printf(“%d\n”,query());

}else{

if(empty()) printf(“YES\n”);

else printf(“NO\n”);

}

}

return 0;

}

单调栈

image-20210831184549374

单调栈

常见模型:找出每个数左边离它最近的比它大/小的数

https://www.acwing.com/problem/content/description/832/

O(n)

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int n;

// tt一开始指向0,指向0表示没有元素

int stk[N],tt;

int main(){

scanf(“%d”,&n);

for(int i = 0;i < n;i++){

int x;

scanf(“%d”,&x);

// 如果栈不空,并且栈顶元素大于x,弹栈

while(tt && stk[tt] >= x) tt–;

// 如果栈不空,输出栈顶

if(tt) printf("%d ",stk[tt]);

else printf("-1 ");

// 压栈,因为对于后面的元素而言,这个是离它最近,并且小的数了

stk[++tt] = x;

}

return 0;

}

单调队列

常见模型:找出滑动窗口中的最大值/最小值

base on 董晓 : https://www.bilibili.com/video/BV1H5411j7o6

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R3TivCQf-1630933432561)(https://gitee.com/Crescent_P/picture-bed/raw/master/image-20210901142939496.png)]

image-20210901143254248

image-20210901143506485

滑动窗口

https://www.acwing.com/problem/content/156/

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 10;

int a[N],q[N],hh,tt = -1;

int main(){

int n,k;

scanf(“%d%d”,&n,&k);

for(int i = 0;i < n;i++) scanf(“%d”,&a[i]);

for(int i = 0;i < n;i++){

// q[hh] 不在[i-k+1,i],队头出队,就是判断队头是否已经滑出了窗口

// 队列中存的是下标

if(hh <= tt && q[hh] < i-k+1) hh++;

// 如果要进来的元素比队列中的元素小,那么队尾元素出队,保证队列的元素是由小到大

// 因为较小的还用的上,大了的就用不上了

while(hh <= tt && a[q[tt]] >= a[i]) tt–;

// 当前元素的下标入队

q[++tt] = i;

// 保证窗口内的元素足够才开始输出

// 队首存的是最小元素的下标

if(i >= k - 1) printf("%d ",a[q[hh]]);

}

puts(“”) ;

hh = 0,tt = -1;

for(int i = 0;i < n;i++){

if(hh <= tt && i-k+1>q[hh]) hh++;

while(hh <= tt && a[q[tt]] <= a[i]) tt–;

q[++tt] = i;

if(i >= k-1 ) printf("%d ",a[q[hh]]);

}

return 0;

}

KMP

image-20210901152508092

image-20210901153639974

image-20210901154845219

KMP字符串

https://www.acwing.com/problem/content/833/

#include <bits/stdc++.h>

using namespace std;

const int N = 1e4+10,M = 1e5+10;

int n,m;

char p[N],s[M];

int ne[N];

int main(){

// 从第1位开始

cin >> n >> p+1 >> m >> s+1;

// 求next数组

// next[1]=0,因为是j+1来匹配

for(int i = 2,j = 0;i <= n;i++) {

// 如果匹配不成功

while(j && p[i]!=p[j+1]) j = ne[j];

// 匹配成功

if(p[i] == p[j+1]) j++;

ne[i] = j;

}

// kmp匹配过程

for(int i = 1,j = 0;i <= m;i++) {

while(j && s[i] != p[j+1]) j = ne[j];

if(s[i] == p[j+1]) j++;

// 匹配完了

if(j == n){

// 下标从1开始的所以要减去1 ,应该是 i-n+1-1

printf("%d ",i-n);

j = ne[j]; //

}

}

return 0;

}

Trie

高效地存储和查找字符串集合的数据结构

要么全是大写,要么全是小写,要么全是数字,类型不会特别多

image-20210901161557289

Trie字符串统计

https://www.acwing.com/problem/content/837/

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

// 下标是0的点,既是根节点,又是空节点

int son[N][26], cnt[N],idx;

知其然不知其所以然,大厂常问面试技术如何复习?

1、热门面试题及答案大全

面试前做足功夫,让你面试成功率提升一截,这里一份热门350道一线互联网常问面试题及答案助你拿offer

2、多线程、高并发、缓存入门到实战项目pdf书籍

3、文中提到面试题答案整理

4、Java核心知识面试宝典

覆盖了JVM 、JAVA集合、JAVA多线程并发、JAVA基础、Spring原理、微服务、Netty与RPC、网络、日志、Zookeeper、Kafka、RabbitMQ、Hbase、MongoDB 、Cassandra、设计模式、负载均衡、数据库、一致性算法 、JAVA算法、数据结构、算法、分布式缓存、Hadoop、Spark、Storm的大量技术点且讲解的非常深入

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

大了的就用不上了

while(hh <= tt && a[q[tt]] >= a[i]) tt–;

// 当前元素的下标入队

q[++tt] = i;

// 保证窗口内的元素足够才开始输出

// 队首存的是最小元素的下标

if(i >= k - 1) printf("%d ",a[q[hh]]);

}

puts(“”) ;

hh = 0,tt = -1;

for(int i = 0;i < n;i++){

if(hh <= tt && i-k+1>q[hh]) hh++;

while(hh <= tt && a[q[tt]] <= a[i]) tt–;

q[++tt] = i;

if(i >= k-1 ) printf("%d ",a[q[hh]]);

}

return 0;

}

KMP

image-20210901152508092

image-20210901153639974

image-20210901154845219

KMP字符串

https://www.acwing.com/problem/content/833/

#include <bits/stdc++.h>

using namespace std;

const int N = 1e4+10,M = 1e5+10;

int n,m;

char p[N],s[M];

int ne[N];

int main(){

// 从第1位开始

cin >> n >> p+1 >> m >> s+1;

// 求next数组

// next[1]=0,因为是j+1来匹配

for(int i = 2,j = 0;i <= n;i++) {

// 如果匹配不成功

while(j && p[i]!=p[j+1]) j = ne[j];

// 匹配成功

if(p[i] == p[j+1]) j++;

ne[i] = j;

}

// kmp匹配过程

for(int i = 1,j = 0;i <= m;i++) {

while(j && s[i] != p[j+1]) j = ne[j];

if(s[i] == p[j+1]) j++;

// 匹配完了

if(j == n){

// 下标从1开始的所以要减去1 ,应该是 i-n+1-1

printf("%d ",i-n);

j = ne[j]; //

}

}

return 0;

}

Trie

高效地存储和查找字符串集合的数据结构

要么全是大写,要么全是小写,要么全是数字,类型不会特别多

image-20210901161557289

Trie字符串统计

https://www.acwing.com/problem/content/837/

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

// 下标是0的点,既是根节点,又是空节点

int son[N][26], cnt[N],idx;

知其然不知其所以然,大厂常问面试技术如何复习?

1、热门面试题及答案大全

面试前做足功夫,让你面试成功率提升一截,这里一份热门350道一线互联网常问面试题及答案助你拿offer

[外链图片转存中…(img-XrlnRs0O-1714779437181)]

2、多线程、高并发、缓存入门到实战项目pdf书籍

[外链图片转存中…(img-zwQDmv3x-1714779437182)]

[外链图片转存中…(img-3ZJDJPOS-1714779437182)]

[外链图片转存中…(img-sxdpn0zX-1714779437182)]

3、文中提到面试题答案整理

[外链图片转存中…(img-gKRmO3xD-1714779437183)]

4、Java核心知识面试宝典

覆盖了JVM 、JAVA集合、JAVA多线程并发、JAVA基础、Spring原理、微服务、Netty与RPC、网络、日志、Zookeeper、Kafka、RabbitMQ、Hbase、MongoDB 、Cassandra、设计模式、负载均衡、数据库、一致性算法 、JAVA算法、数据结构、算法、分布式缓存、Hadoop、Spark、Storm的大量技术点且讲解的非常深入

[外链图片转存中…(img-6Rk7IyHt-1714779437183)]

[外链图片转存中…(img-CZIN3OMp-1714779437183)]

[外链图片转存中…(img-9FifT7fz-1714779437184)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 9
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值