C++算法模板二

来源:https://www.acwing.com/blog/content/4019/

模拟链表
题目:Acwing

int e[N], ne[N]; // 链表元素及下个结点的地址
int head;       // 头结点地址
int idx;        // 可用位置

/** 创建含头结点的单链表 */
void init() {
    head = 0;
    // 头结点
    e[0] = 0;       // 值为链表长度
    ne[0] = -1;     
    idx = 1;        // 第1个结点的下标从1开始
}

/** 向链表头部插入一个数 */
void insert_head(int x) {
    e[idx] = x;
    ne[idx] = ne[head];
    ne[head] = idx;
    idx++;
    e[0]++;     // 链表长度+1
}

/** 删除下标为k后面的数 */
void rem(int k) {
    ne[k] = ne[ne[k]];
    e[0]--;     // 链表长度-1
}

/** 在下标为k的位置后插入一个数 */
void insert(int k, int x) {
    e[idx] = x;
    ne[idx] = ne[k];
    ne[k] = idx;
    idx++;
    e[0]++;     // 链表长度+1
}

/** 遍历链表 */
void print() {
    for (int i = ne[head]; i != -1; i = ne[i]) cout << e[i] << " ";
}

采用含头结点的单链表,头结点存储链表长度
元素从下标1开始存储

单调栈
题目:洛谷
注意:洛谷那个题有两个坑。1是数组要开到足够大空间,2是注意考虑相等的情况。

#include <iostream>
#include <stack>
#include <vector>
using namespace std;
const int N = 10000010;
int a[N];
int main() {
	int n;
	cin >> n;
	stack<int>s;
	vector<int>re;
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
	}
	for (int i = n; i >= 1; i--) {
		while (!s.empty() && a[i] >=a[s.top()])s.pop();
		re.push_back(!s.empty() ? s.top() : 0);
		s.push(i);
	}
	for (int i = n-1; i >= 0; i--) {
		printf("%d ", re[i]);
	}
	return 0;
}

KMP
题目:洛谷
注意:Kmp算法都是从下标1开始,所以注意初始化字符串s=" "+s,但是字符串的长度还是原来的长度。另:第一步:求解匹配串的next数组。第二步:字符串匹配。

// s[]是长文本,p[]是模式串,n是s的长度,m是p的长度
// 求模式串的Next数组:
for (int i = 2, j = 0; i <= m; i ++ )
{
    while (j && p[i] != p[j + 1]) j = ne[j];
    if (p[i] == p[j + 1]) j ++ ;
    ne[i] = j;
}
// 匹配
for (int i = 1, j = 0; i <= n; i ++ )
{
    while (j && s[i] != p[j + 1]) j = ne[j];
    if (s[i] == p[j + 1]) j ++ ;
    if (j == m)
    {
        j = ne[j]; // 匹配成功后的逻辑 ex:len =i-m+1
    }
}

字典树
题目:洛谷

int son[N][26], cnt[N], idx;
// 0号点既是根节点,又是空节点
// son[][]存储树中每个节点的子节点
// cnt[]存储以每个节点结尾的单词数量

// 插入一个字符串
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 p[N]; //存储每个点的祖宗节点
// 返回x的祖宗节点
int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);       // 路径压缩
    return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ ) p[i] = i;

// 合并a和b所在的两个集合:
p[find(a)] = find(b);

// 判断两个结点是否属于同一集合
if (find(a) == find(b)) {...}

维护size

int p[N], size[N];                  // 变动部分
//p[]存储每个点的祖宗节点, size[]只有祖宗节点的有意义,表示祖宗节点所在集合中的点的数量

// 返回x的祖宗节点
int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ )
{
    p[i] = i;
    size[i] = 1;                    // 变动部分
}

// 合并a和b所在的两个集合:
int x = find(a), y = find(b);
if (x != y) {
    p[x] = y;
    size[y] += size[x];
}
// 判断两个结点是否属于同一集合
if (find(a) == find(b)) {...}

模拟堆
题目:洛谷,力扣

// h[N]存储堆中的值, h[1]是堆顶,x的左儿子是2x, 右儿子是2x + 1
int h[N], size;}
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)
    {
        swap(u, t);
        down(t);
    }
}
void up(int u)
{
    while (u / 2 && h[u] < h[u / 2])
    {
        swap(u, u / 2);
        u >>= 1;
    }
}

// --------------------------------基本操作--------------------------------
// 0. 建堆
void init() {
    for (int i = n / 2; i; i -- ) down(i);
}

// 1. 插入一个数
void insert(int x) {
    h[++size] = x;
    up[size];  
}

// 2. 求最小值
int top() {
    return h[1];
}

// 3. 删除最小值
void remove() {
    h[1] = h[size];
    size--;
    down(1);
}

// 4. 删除任意位置的元素(STL没有)
void remove(int k) {
    h[k] = h[size];
    size--;
    down(k);
    up(k);
}

// 5. 修改任意位置的元素(STL没有)
void update(int k, int x) {
    h[k] = x;
    down(k);
    up(k);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值