1. 单链表
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int n;
// 分别存储当前节点的值,当前节点下一个节点的值,头结点,id号
int value[N], nepoint[N], head, idx;
void init(){
head = -1;
idx = 0;
}
// 1.H 将某个x插入到头结点上
void int_to_head(int x){
value[idx] = x;// 初始化节点的值
nepoint[idx] = head;// 初始化头结点的下一个节点,头插法
head = idx;// 改变头结点
idx++;
}
// 2.删除
void remove(int k){
nepoint[k] = nepoint[nepoint[k]];
}
// 3.添加
void add(int k, int x){
value[idx] = x;
nepoint[idx] = nepoint[k];
nepoint[k] = idx;
idx++;
}
int main(){
cin >> n;
init();//初始化链表
for (int i = 0; i < n; i++) {
char s;
cin >> s;
if (s == 'H'){
int x;
cin >> x;
int_to_head(x);
}
if (s == 'D'){
int k;
cin >> k;
if (k == 0) head = nepoint[head];
else remove(k - 1);
}
if (s == 'I'){
int k, x;
cin >> k >> x;
add(k - 1, x);// 找到 k 的前一个元素,利用它进行插入
}
}
for (int i = head; i != -1; i = nepoint[i]){
cout << value[i] << ' ';
}
return 0;
}
2. 双链表
#include<iostream>
using namespace std;
const int N = 1e5+10;
int e[N], l[N], r[N];
int m, idx;
void init(){
r[0] = 1;
l[1] = 0;
// 1.idx 从 2 开始。我们插入的第i个数的idx为i+1
idx = 2;
}
// 在第k个插入的数右边插入x
void add(int k, int x){
e[idx] = x;
r[idx] = r[k];
l[idx] = k;
l[r[k]] = idx;
r[k] = idx;
// 2.要让idx++
idx++;
}
void remove(int k){
r[l[k]] = r[k];
l[r[k]] = l[k];
}
int main(){
cin >> m;
init();
while(m--){
string op;
cin >> op;
int k, x;
if (op == "R"){
cin >> x;
// 在尾节点左边的一个节点的右边插入
add(l[1], x);
}else if (op == "L"){
cin >> x;
// 在头结点的右边插入
add(0, x);
}else if (op == "D"){
cin >> k;
// k 和 k + 1 的对应关系
remove(k + 1);
}else if (op == "IL"){
cin >> k >> x;
add(l[k + 1], x);
}else{
cin >> k >> x;
add(k + 1, x);
}
}
// 3.注意!头结点不用输出,所以第一个输出的是r[0]
// 尾节点也不用输出
// 不断推进
for (int i = r[0]; i != 1; i = r[i]){
cout << e[i] << " ";
}
}
3. 模拟栈
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int top = -1;// 栈顶元素为空
int v[N];
int main(){
int n;
cin >> n;
while(n--){
string s;
cin >> s;
if (s == "push")
{
int a;
cin >> a;
v[++top] = a;// 更新栈顶元素
}
if (s == "pop")
{
top--;// 移动栈顶指针
}
if (s == "query")
{
cout << v[top] << endl;
}
if (s == "empty")
{
cout << (top == -1 ? "YES" : "NO") << endl;
}
}
}
4. 模拟队列
#include<iostream>
using namespace std;
const int N = 1e5+10;
int q[N];
int main(){
int n;
cin >> n;
int h = 0, t = -1;
while(n--){
string op;
cin >> op;
int x;
if (op == "push"){
cin >> x;
q[++t] = x;
}else if (op == "pop"){
h++;
}else if (op == "empty"){
if (h <= t) puts("NO");
else puts("YES");
}else {
cout << q[h] << endl;
}
}
}
5. 单调栈
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int stk[N], t;
int main(){
int n;
cin >> n;
while(n--){
int x;
scanf("%d", &x);
// 如果栈非空,检查其中是否有 >= x 的元素,如果有要清空
while(t && stk[t] >= x) t--;
// 如果栈是空的,打印 -1
if (!t) cout << -1 << ' ';
// 如果栈是非空的,打印栈顶元素
else cout << stk[t] << " ";
// 将新来的元素加入栈
stk[++t] = x;
}
return 0;
}
6. 模拟堆
down: 往下调整,
up: 往上调整
插入:heap[s++] = x, up(size)
求最小:heap[1]
删除:swap[heap[1], heap[size]], size--, down[1] 删除头元素很麻烦
删除任意元素:heap[k] = heap[size]; size--; down(k), up(k)
修改元素:heap[k] = x; down(k), up(k)
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
const int N = 1e5 + 10;
int h[N], ph[N], hp[N], cnt;
// ph[1] = a, hp[a] = 1
void heap_swap(int a, int b)
{
swap(ph[hp[a]], ph[hp[b]]);
swap(hp[a], hp[b]);
// 交换堆中存储的 a 和 b 位置的值
swap(h[a], h[b]);
}
// 对堆中位置在 u 的元素执行
void down(int u)
{
int t = u;
if (u*2 <= cnt && h[u*2] < h[t]) t = u*2;
if (u*2 + 1 <= cnt && h[u*2+1] < h[t]) t = 2*u + 1;
if (u != t)
{
heap_swap(u, t);
down(t);
}
}
void up(int u)
{
while(u/2 && h[u] < h[u/2])
{
heap_swap(u, u/2);
u >>= 1;
}
}
int main()
{
int n, m = 0;
scanf("%d", &n);
while(n--)
{
char op[5];
int k, x;
scanf("%s", op);
if (!strcmp(op, "I"))
{
scanf("%d", &x);
// 更新 size
cnt++;
// 唯一 ID 表示当前是第 m 个插入的数
m++;
// size 和 ID 建立联系
ph[m] = cnt, hp[cnt] = m;
h[cnt] = x;
up(cnt);
}
// h[1] 存储的是最小的元素
else if (!strcmp(op, "PM")) printf("%d\n", h[1]);
else if (!strcmp(op, "DM"))
{
heap_swap(1, cnt);
cnt--;
down(1);
}
else if (!strcmp(op, "D"))
{
scanf("%d", &k);
// 找到该数在堆中的位置
k = ph[k];
heap_swap(k, cnt);
cnt--;
up(k);
down(k);
}
else
{
scanf("%d%d", &k, &x);
k = ph[k];
h[k] = x;
up(k);
down(k);
}
}
return 0;
}
7. 堆排序
从 n/2 开始 down,复杂度为 O(n)
#include<iostream>
#include<algorithm>// 因为需要调用swap
using namespace std;
const int N = 1e5 + 10;
int h[N], Msize;//堆的大小
int n, m;
void down(int u){
int t = u;
// 判断左右孩子是否存在以及他们的大小
// 更新 t 为最小的元素下标
if(u*2 <= Msize && h[u*2] < h[t]) t = u*2;
if(u*2 + 1 <= Msize && h[u*2 + 1] < h[t]) t = u*2 + 1;
if(u != t){
swap(h[u], h[t]);
down(t);
}
}
int main(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%d", &h[i]);
Msize = n;
// 收敛性证明:错位相减
for(int i = n / 2;i;i--) down(i);
while(m--){
cout << h[1] << " ";
h[1] = h[Msize--];
down(1);
}
return 0;
}