1、数组模拟单链表
例题:826. 单链表
https://www.acwing.com/problem/content/828/
输入样例:
10
H 9
I 1 1
D 1
D 0
H 6
I 3 6
I 4 5
I 4 5
I 3 4
D 6
输出样例:
6 4 6 5
#include<iostream>
using namespace std;
const int N = 100010;
// head 头结点下标,e[i]表示结点i的值
// ne[i]表示下一个结点的下标,idx表示当前已经用到了哪个点
int head, e[N], ne[N],idx=0;
//初始化
void init() {
head = -1;
idx = 0;
}
// 插入头节点 “H x”,表示向链表头插入一个数x。
void add_to_head(int x) {
e[idx] = x;
ne[idx] = head;
head = idx;
idx++;
}
// 插入指定结点后 “I k x”,表示在第k个输入的数后面插入一个数x(此操作中k均大于0)。
void insert(int k, int x) {
e[idx] = x;
ne[idx] = ne[k];
ne[k] = idx;
idx++;
}
// 删除k结点后一个点, “D k”,表示删除第k个输入的数后面的数(当k为0时,表示删除头结点)。
void remove(int k) {
ne[k] = ne[ne[k]];
}
int main() {
init();
int m;
cin >> m;
while (m--) {
char a;
int k, x;
cin >> a;
if (a == 'H') {
cin >> x;
add_to_head(x);
}
else if (a == 'I') {
cin >> k >> x;
insert(k-1, x);
}
else {
cin >> x;
if (!x)
head = ne[head];
remove(x-1);
}
}
for (int i = head; i != -1; i = ne[i])
cout << e[i] << " ";
}
例题:827. 双链表
数据范围
1≤M≤100000
所有操作保证合法。
输入样例:
10
R 7
D 1
L 3
IL 2 10
D 3
IL 2 7
L 8
R 9
IL 4 7
IR 2 2
输出样例:
8 7 7 3 2 9
#include<iostream>
using namespace std;
const int N = 100010;
// head 头结点下标,e[i]表示结点i的值
// ne[i]表示下一个结点的下标,idx表示当前已经用到了哪个点
int head, e[N], l[N],r[N],idx=0;
// 初始化
void init() {
// 0 表示左端点,1表示右端点
r[0] = 1, l[1] = 0;
idx = 2;
}
// 第k个点右边插入一个点
void IR(int k, int x) {
e[idx] = x;
r[idx] = r[k];
l[idx] = k;
l[r[k]] = idx;
r[k] = idx;
idx++;
}
// 第k个点左边插入一个点
void IL(int k, int x) {
e[idx] = x;
l[idx] = l[k];
r[idx] = k;
r[l[k]] = idx;
l[k] = idx;
idx++;
}
// 删除第k个结点
void remove(int k) {
r[l[k]] = r[k];
l[r[k]] = l[k];
}
// “L x”,表示在链表的最左端插入数x。
void L(int x) {
e[idx] = x;
l[idx] = 0;
r[idx] = r[0];
r[0] = idx;
l[r[idx]] = idx;
idx++;
}
//“R x”,表示在链表的最右端插入数x。
void R(int x) {
e[idx] = x;
r[idx] = 1;
l[idx] = l[1];
r[l[idx]] = idx;
l[1] = idx;
idx++;
}
int main() {
ios::sync_with_stdio(false);
init();
int m; cin >> m;
string opt;
int k, x;
while (m--) {
cin >> opt;
if(opt== "L"){
cin >> x;
L(x);
}
else if (opt == "R") {
cin >> x;
R(x);
}
else if (opt == "D") {
cin >> k;
remove(k+1);
}
else if (opt == "IL") {
cin >> k >> x;
IL(k+1, x);
}
else if (opt == "IR") {
cin >> k >> x;
IR(k+1, x);
}
}
for (int i = r[0]; i != 1; i = r[i]) {
cout << e[i] << " ";
}
}
/*
(1) “L x”,表示在链表的最左端插入数x。
(2) “R x”,表示在链表的最右端插入数x。
(3) “D k”,表示将第k个插入的数删除。
(4) “IL k x”,表示在第k个插入的数左侧插入一个数。
(5) “IR k x”,表示在第k个插入的数右侧插入一个数。
输入样例:
10
R 7
D 1
L 3
IL 2 10
D 3
IL 2 7
L 8
R 9
IL 4 7
IR 2 2
输出样例:
8 7 7 3 2 9
*/
2、数组模拟栈
#include<iostream>
using namespace std;
const int N = 10010;
int stk[N], stt=0;
//========= 栈 ==================
// 插入
void push(int x) {
stk[++stt] = x;
}
// 弹出
void pop() {
stt--;
}
//判断是否为空
bool isempty() {
return stt > 0;
}
//栈顶
int top() {
return stk[stt];
}
3、数组模拟队列
#include<iostream>
using namespace std;
const int N = 10010;
int q[N], hh, tt = -1;
// 插入
void push(int x) {
q[++tt] = x;
}
//弹出
void pop() {
hh++;
}
// 判断是否为空
bool isEmpty() {
return hh > tt;
}
int top() {
return q[hh];
}
4、单调栈
思路:
最小值和最大值分开来做,两个for循环完全类似,都做以下四步:
解决队首已经出窗口的问题;
解决队尾与当前元素a[i]不满足单调性的问题;
将当前元素下标加入队尾;
如果满足条件则输出结果;
需要注意的细节:
上面四个步骤中一定要先3后4,因为有可能输出的正是新加入的那个元素;
队列中存的是原数组的下标,取值时要再套一层,a[q[]];
算最大值前注意将hh和tt重置;
此题用cout会超时,只能用printf;
hh从0开始,数组下标也要从0开始。
#include<iostream>
using namespace std;
const int N = 100010;
int stk[N], tt = 0;
int main() {
cin.tie(0); //通过tie(0)(0表示NULL)来解除cin与cout的绑定,进一步加快执行效率。
ios::sync_with_stdio(false);
int n; cin >> n;
for (int i = 0; i < n; i++) {
int x;
cin >> x;
while (tt && stk[tt] >= x)
tt--;
if (tt)
cout << stk[tt] << ' ';
else
cout << -1 << ' ';
stk[++tt] = x;
}
}
关于ios::sync_with_stdio(false);和 cin.tie(0)加速c++输入输出流
5、单栈队列
https://www.acwing.com/problem/content/156/
您的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。
输入格式
输入包含两行。
第一行包含两个整数n和k,分别代表数组长度和滑动窗口的长度。
第二行有n个整数,代表数组的具体数值。
同行数据之间用空格隔开。
输出格式
输出包含两个。
第一行输出,从左至右,每个位置滑动窗口中的最小值。
第二行输出,从左至右,每个位置滑动窗口中的最大值。
输入样例:
8 3
1 3 -1 -3 5 3 6 7
输出样例:
-1 -3 -3 -3 3 3
3 3 5 5 6 7
#include<iostream>
using namespace std;
const int N = 1e6+10;
/*
输出格式
输出包含两个。
第一行输出,从左至右,每个位置滑动窗口中的最小值。
第二行输出,从左至右,每个位置滑动窗口中的最大值。
输入样例:
8 3
1 3 -1 -3 5 3 6 7
输出样例:
-1 -3 -3 -3 3 3
3 3 5 5 6 7
*/
int n, k;
int a[N], q[N];// a是真实输入的数组,q是队列
int hh, tt = -1;//队头hh、队尾tt
int main() {
//cin >> n >> k;
scanf_s("%d%d", &n,&k);
for (int i = 0; i < n; i++)
scanf_s("%d", &a[i]);
// 求滑动窗口内最小
for (int i = 0; i < n; i++) {
// 判断队头是否滑出窗口,作用:维持滑动窗口的大小
//当队列不为空(hh <= tt) 且 当当前滑动窗口的大小(i - q[hh] + 1)>我们设定的
//滑动窗口的大小(k),队列弹出队列头元素以维持滑动窗口的大小
if (hh <= tt && i - k + 1 >q[hh]) hh++;
//当队列不为空(hh <= tt) 且 当队列队尾元素>=当前元素(a[i])时,那么队尾元素
//就一定不是当前窗口最小值,删去队尾元素,加入当前元素(q[ ++ tt] = i)
while (hh <= tt && a[q[tt]] >= a[i]) tt--; // 若队尾不单调,tt减1,队列中存的是原数组的下标,取值时要再套一层,a[q[]]
q[++tt] = i;
if (i >= k - 1) printf("%d ", a[q[hh]]);
}
printf("\n");
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--; // 若队尾不单调,tt减1
q[++tt] = i;
if (i >= k - 1) printf("%d ", a[q[hh]]);
}
}
6、字符串匹配算法–kMP
KMP字符串匹配算法1
KMP字符串匹配算法1(理论)链接:
https://www.bilibili.com/video/BV1Px411z7Yo/?spm_id_from=333.788.b_7265636f5f6c697374.5
KMP字符串匹配算法2
推荐博客学习:从头到尾彻底理解
例题:28. 实现 strStr()
#include<iostream>
#include<string>
using namespace std;
class Solution {
public:
int strStr(string haystack, string needle) {
int* Next = new int[needle.length()];
int res = kmpSearch(haystack, needle,Next);
delete []Next;
return res;
}
private:
void getNextVal(string p,int Next[]) {
int pLen = p.length();
Next[0] = -1;
int k = -1, j = 0;
while (j < pLen - 1) {
// p[k] 表示前缀,p[j]表示后缀
if (k == -1 || p[k] == p[j]) {
++j;
++k;
if (p[j] != p[k]) {
Next[j] = k;
}
else {
//因为不能出现p[j] = p[ next[j ]],所以当出现时需要继续递归,k = next[k] = next[next[k]]
Next[j] = Next[k];
}
}
else {
k = Next[k];
}
}
}
int kmpSearch(string s,string p,int Next[]) {
if (p.length() == 0) return 0;
getNextVal(p, Next);
int sLen = s.length(), pLen = p.length();
int i = 0, j = 0;
while (i < sLen && j < pLen) {
//①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++
if( -1 == j || s[i] == p[j]) {
i++;
j++;
}
else {
//②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]
//next[j]即为j所对应的next值
j = Next[j];
}
}
if (j == pLen)
return i - j;
else
return -1;
}
};