算法 —— 数据结构

目录

一. 单向链表(数组模拟)模板

二. 双向链表(数组模拟)模板

三.单调栈 

 四.单调队列

五.KMP

六. trie树(又名:字典树)

七.并查集

八.堆(小堆顶) 

九. 哈希表 

 拉链法

开放寻址法

字符串前缀哈希法 


一. 单向链表(数组模拟)模板
#include<iostream>
using namespace std;

const int N = 100010;

// head 表示头节点的下标
// e[i] 表示节点 i 的值
// ne[i] 表示节点 i 的 next 指针是多少
// idx 储存当前已经用到了哪个点 
int head, e[N], ne[N], idx;

// 初始化 
void init(){
	head = -1;
	idx = 0;
}

// 将 x 插到头结点 
void add_to_head(int x){
	e[idx] = x;
	ne[idx] = head;
	head = idx;
	idx ++;	
}

// 将 x 插到下标是 k 的点后面 
void add(int k, int x){
	e[idx] = x;
	ne[idx] = ne[k];
	ne[k] = idx;
	idx ++;
}

// 将下标是 k 的点后面的点删掉 
void remove(int k){
	ne[k] = ne[ne[k]];
} 

int main(){
	
	init();
	add_to_head(1);
	add_to_head(2);
	add_to_head(3);
	add(1, 4);
	add(3, 6);
	remove(2);
	
	for (int i=head; i != -1; i = ne[i]) cout << e[i] << " "; 
	
	return 0;
}
二. 双向链表(数组模拟)模板
#include<iostream>
using namespace std;

const int N = 100010;

int n[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){
	n[idx] = x;
	r[idx] = r[k];
	l[idx] = k;
	l[r[k]] = idx;
	r[k] = idx;
	idx ++;
}

// 删除下标是 k 的点
void remove(int k){
	r[l[k]] = r[k];
	l[r[k]] = l[k];
} 

int main(){
	
	init();
	add(0, 1);
	add(2, 2);
	add(3, 3);
	add(3, 4);
	remove(2);
	
	for (int i=r[0]; i!=1; i=r[i]) cout << n[i] << " ";
	
	return 0;
}
三.单调栈 

给一串序列:1 3 2 5 3 4;

判断每个数前面离它最近且比它小的数是多少;如果没有,输出 -1;

则会输出:-1 1 1 2 2 3。

#include<bits/stdc++.h>
using namespace std;

const int N = 100010;

int n;
int stk[N], tt;

int main()
{
	cin.tie(0);
	ios::sync_with_stdio(false);
	
	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;
	}
	
	
	return 0;
}
 四.单调队列

给一串序列:1 3 2 -1 -5 2 -3 2;

给一个特定的长度 k = 3;判断每三个数中,最小的那个数,并将其输出;

则输出:1 -1 -5 -5 -5 -3。

#include<bits/stdc++.h>
using namespace std;

const int N = 100010;

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

int main()
{
	scanf("%d %d", &n, &k);
	for (int i=0; i<n; i++) scanf("%d", &a[i]);
	
	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

给一串长为 n 的字符串 p = ab;

给一串长为 m 的字符串 s = sababababa,且 m >= n;

判断 p 在 s 中出现的所有位置; 

则输出:1 3 5 7。

#include<iostream>
using namespace std;

const int N = 10010, M = 100010;

int n, m;
char p[N], s[M];
int ne[N];

int main(){
	
	cin >> n >> p + 1 >> m >> s + 1;
	
	//求next的过程
	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){
			printf("%d ", i - n);
			j = ne[j];
		}
	} 
	
	
	return 0;
}
六. trie树(又名:字典树)

 实现对字符串的快速插入和查找。

#include <bits/stdc++.h>
using namespace std;

const int N = 100010;

int son[N][26], cnt[N], idx;	// 下标是 0 的点,即是根节点,又是空节点
char str[N];

void insert(char str[]){
	
	int p = 0;
	for (int i = 0; str[i]; i++){
		int u = str[i] - 'a';
		if (!son[p][u]) son[p][u] = ++ idx;
		p = son[p][u];
	}
	
	cnt[p] ++;
}

int query(char str[]){
	int p = 0;
	for (int i =0; str[i]; i++){
		int u = str[i] - 'a';
		if (!son[p][u]) return 0;
		p = son[p][u];
	}
	
	return cnt[p];
}

int main(){
	
	int n;
	scanf("%d", &n);
	while (n --){
		char op[2];
		scanf("%s%s", op, str);
		if (op[0] == 'I') insert(str);
		else printf("%d\n", query(str));
	}
	
	return 0;
}
七.并查集

#include <iostream>
using namespace std;

const int N = 100010;

int n, m;
int p[N];
// find 函数是最重要的部分 
int find(int x){	//返回 x 的祖宗节点 + 路径压缩 
	
	if (p[x] != x) p[x] = find(p[x]);
	return p[x];
}

int main(){
	
	scanf("%d%d", &n, &m);
	
	for (int i = 1; i <= n; i++) p[i] = i;
	
	while (m --){
		char op[2];
		int a, b;
		scanf("%s%d%d", op, &a, &b);
		
		if (op[0] == 'M') p[find(a)] = find(b);
		else{
			if (find(a) == find(b)) puts("Yes");
			else puts("No");
		}
	}
	
	return 0;
}
八.堆(小堆顶) 

 

#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
const int N = 100010;

int h[N], ph[N], hp[N], size;

void heap_swap(int a, int b)
{
	swap(ph[hp[a]], ph[hp[b]]);
	swap(hp[a], hp[b]);
	swap(h[a], h[b]);
}

void down(int u)
{
	int t = u;
	if (u * 2 <= size && h[u * 2] < h[t]) t = u * 2;
	if (u * 2 + 1 <= size && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
	if (u != t)
	{
		heap_swap(u, t);
		down(t);
	}
}

void up(int u)
{
	while (u / 2 && h[u / 2] > h[u])
	{
		heap_swap(u / 2, u);
		u /= 2;
	}
}

int main()
{
	int n, m = 0;
	cin >> n;
	while (n--)
	{
		char op[10];
		int k, x;
		
		cin >> op;
		if (!strcmp(op, "I"))
		{
			cin >> x;
			size ++;
			m ++;
			ph[m] = size, hp[size] = m;
			h[size] = x;
			up(size);
		}
		else if (!strcmp(op, "PM")) cout << h[1];
		else if (!strcmp(op, "DM"))
		{
			heap_swap(1, size);
			size --;
			down(1);
		}
		else if (!strcmp(op, "D"))
		{
			cin >> k;
			k = ph[k];
			heap_swap(k, size);
			size --;
			down(k), up(k);
		}
		else
		{
			cin >> k >> x;
			k = ph[k];
			h[k] = x;
			down(k), up(k);
		}
	}
	
	return 0;
}
九. 哈希表 

 拉链法
#include <cstring>
#include <iostream>
using namespace std;
const int N = 100003;

int h[N], e[N], ne[N], idx;

void insert (int x) //向表中插入元素 
{
	int k = (x % N + N) % N;
	e[idx] = x;
	ne[idx] = h[k];
	h[k] = idx ++;
}

bool find (int x) //查找表中的元素 
{
	int k = (x % N + N) % N;
	for (int i = h[k]; i!=-1; i = ne[i])
		if (e[i] == x)
			return true;
	return false;
}

int main()
{
	int n;
	cin >> n;
	
	memset(h, -1, sizeof h);
	
	while (n--)
	{
		char op[2];
		int x;
		cin >> op >> x;
		
		if (*op == 'I') insert(x);
		else
		{
			if (find(x)) puts("yes");
			else puts("no"); 
		}
	}
	
	return 0;
}
开放寻址法
#include <cstring>
#include <iostream>
using namespace std;
const int N = 200003, null = 0x3f3f3f3f; // N一般开到数据范围的 2~3 倍(解决冲突) 

int h[N]; 

int find (int x) // 如果 x 在表中,返回 x 的位置;否则返回 x  应该在的位置 
{
	int k = (x % N + N) % N;
	
	while (h[k]!=null && h[k]!=x)
	{
		k ++;
		if (k == N) k = 0;
	}
	
	return k;
}

int main()
{
	int n;
	cin >> n;
	
	memset(h, 0x3f, sizeof h);
	
	while (n--)
	{
		char op[2];
		int x;
		scanf("%s%d", op, &x);
		
		int k = find (x);
		if (*op == 'I') h[k] = x;
		else
		{
			if (h[k] != null) puts("yes");
			else puts("no");
		}
	}
	for (int i=1;i<=5; i++) cout << h[i] << " ";
	
	return 0;
}
字符串前缀哈希法 
#include<iostream>
#include<cstring>
using namespace std;
typedef unsigned long long ULL; // 超出这个范围,就相当于取模操作 
const int N = 100010, P = 131; // P 是进制,经验值取 131 或 13331 的 冲突最少

int n, m;
char str[N];
ULL h[N], p[N];

ULL get(int l, int r)
{
	return h[r] - h[l - 1] * p[r - l + 1];
}

int main()
{
	scanf("%d%d%s", &n, &m, str + 1);
	
	p[0] = 1;
	for (int i=1; i<=n; i++)
	{
		p[i] = p[i-1] * P;
		h[i] = h[i-1] * P + str[i];
	}
	
	while (m --)
	{
		int l1, r1, l2, r2;
		scanf ("%d%d%d%d", &l1, &r1, &l2, &r2);
		
		if (get(l1, r1) == get(l2, r2)) puts("yes");
		else puts("no");
	}
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值