常用代码模板

注:链表用数组替换

单链表

// head存储链表头的索引,e[]存储节点的值,ne[]存储节点的next指针的索引,idx表示当前新节点的索引
int head, e[N], ne[N], idx;

// 初始化链表
void init()
{
    head = -1; // 初始化链表头为-1,表示链表为空
    idx = 0; // 初始化当前节点索引为0,准备使用数组的第一个位置
}

// 在链表头插入一个数a
void insert(int a)
{
    e[idx] = a; // 将新节点的值设置为a,索引为idx
    ne[idx] = head; // 将新节点的next指针指向当前头结点
    head = idx; // 更新头结点为新节点的索引
    idx++; // 准备下一个新节点,索引自增
}

// 将头结点删除,需要保证头结点存在
void remove()
{
    head = ne[head]; // 将头结点更新为下一个节点的索引,实现删除当前头结点
}

双链表

// e[]表示节点的值,l[]表示节点的左指针(索引),r[]表示节点的右指针(索引),idx表示当前新节点的索引
int e[N], l[N], r[N], idx;

// 初始化
void init()
{
    // 假设数组索引0是左端点,索引1是右端点,这里构建一个虚拟的根节点
    r[0] = 1; // 左端点的右指针指向右端点
    l[1] = 0; // 右端点的左指针指向左端点
    idx = 2; // 初始化当前节点索引为2,准备使用数组的第三个位置(索引从0开始)
}

// 在节点a的右边插入一个数x
void insert(int a, int x)
{
    e[idx] = x; // 新节点的值为x
    l[idx] = a; // 新节点的左指针指向a,即a成为新节点的左子节点
    r[idx] = r[a]; // 新节点的右指针指向a原来的右子节点
    l[r[a]] = idx; // 将a原来的右子节点的左指针指向新节点,链接新节点和a原来的右子节点
    r[a] = idx; // 将a的右指针指向新节点,完成新节点的插入
    idx++; // 准备下一个新节点,索引自增
}

// 删除节点a
void remove(int a)
{
    // 重新链接a的左右节点,绕过a
    l[r[a]] = l[a]; // 将a的右子节点的左指针指向a的左子节点
    r[l[a]] = r[a]; // 将a的左子节点的右指针指向a的右子节点
}

栈 

// tt表示栈顶的索引
int stk[N], tt = 0;

// 向栈顶插入一个数x
// 先增加tt的值,然后在栈顶插入x
stk[++tt] = x;

// 从栈顶弹出一个数
// 减少tt的值,即移除栈顶元素
tt--;

// 栈顶的值
// 返回栈顶元素的值,即当前tt索引处的元素
stk[tt];

// 判断栈是否为空
// 如果tt大于0,则表示栈中至少有一个元素,栈不为空
if (tt > 0) {
    // 栈不为空时的逻辑可以在这里实现
}

队列 

// hh 表示队头的索引,tt表示队尾的索引
int q[N], hh = 0, tt = -1;

// 向队尾插入一个数x
// 先增加tt的值,然后在这个新的位置插入x
q[++tt] = x;

// 从队头弹出一个数
// 增加hh的值,即移除队头元素
hh++;

// 队头的值
// 返回队头元素的值,即hh指向的元素
q[hh];

// 判断队列是否为空
// 如果hh小于等于tt,则表示队列中至少有一个元素,队列不为空
if (hh <= tt)
{
    // 队列不为空时的逻辑可以在这里实现
}

循环队列

// hh 表示队头的索引,tt表示队尾的索引,注意tt指向队尾的下一个位置
int q[N], hh = 0, tt = 0;

// 向队尾插入一个数x
// 将x放入队尾,然后增加tt的值
q[tt++] = x;
// 如果tt达到数组的末尾,将其重置为0,实现循环队列的效果
if (tt == N) tt = 0;

// 从队头弹出一个数
// 增加hh的值,即移除队头元素
hh++;
// 如果hh达到数组的末尾,将其重置为0,继续循环队列
if (hh == N) hh = 0;

// 队头的值
// 返回队头元素的值,即hh指向的位置的元素
q[hh];

// 判断队列是否为空
// 如果hh不等于tt,队列中至少有一个元素,队列不为空
if (hh != tt) {
    // 队列不为空时的逻辑可以在这里实现
}

单调栈

int tt = 0; // tt用作栈顶的索引,初始化为0,表示栈为空

// 遍历数组中的每个元素
for (int i = 1; i <= n; i++)
{
    // 维护栈内序性质:栈内元素应满足check函数定义的顺序关系
    while (tt && check(stk[tt], i)) 
    {
        // 如果栈不为空且栈顶元素违反了顺序性质(即栈顶元素不符合条件),则弹出栈顶元素
        tt--;
    }
    
    // 将当前元素i压入栈中
    stk[++tt] = i;
}

单调队列 

int hh = 0, tt = -1; // hh 和 tt 分别表示双端队列(或称为deque)的头部和尾部索引,初始时窗口为空

// 遍历数组中的每个元素
for (int i = 0; i < n; i++)
{
    // 维护窗口的左边界,移除已经滑出窗口的元素
    while (hh <= tt && check_out(q[hh])) 
    {
        hh++; // 如果队头元素q[hh]满足滑出窗口的条件,则向右移动窗口的左边界
    }

    // 维护窗口的右边界,移除不满足条件的元素
    while (hh <= tt && check(q[tt], i)) 
    {
        tt--; // 如果队尾元素q[tt]不满足条件(例如比当前元素i小),则向左移动窗口的右边界
    }

    // 将当前元素i加入窗口
    q[++tt] = i; // 将当前元素i加入队列尾部
}

KMP

求模式串的Next数组:
// s[] 是长文本,p[] 是模式串,n 是 s 的长度,m 是 p 的长度
// 初始化 Next 数组,ne[] 存储 Next 值,用于模式串的局部匹配
int ne[m + 1];

// 计算模式串 p 的 Next 数组
for (int i = 2, j = 0; i <= m; i++)
{
    // 当 j 非零且当前字符 p[i] 与之前的字符 p[j + 1] 不相等时,更新 j 的值
    while (j && p[i] != p[j + 1])
        j = ne[j]; // 使用之前计算的 Next 值

    // 如果当前字符 p[i] 与之前的字符 p[j + 1] 相等,j 向前移动
    if (p[i] == p[j + 1])
        j++; // 更新 j 为匹配的字符长度

    // 存储当前位置 i 的 Next 值
    ne[i] = j;
}
匹配:
// 使用 Next 数组进行字符串匹配
for (int i = 1, j = 0; i <= n; i++)
{
    // 当 j 非零且当前文本字符 s[i] 与模式串字符 p[j + 1] 不相等时,更新 j 的值
    while (j && s[i] != p[j + 1])
        j = ne[j]; // 使用 Next 数组来确定 j 的新位置

    // 如果当前文本字符 s[i] 与模式串字符 p[j + 1] 相等,j 向前移动
    if (s[i] == p[j + 1])
        j++; // j 现在指向当前匹配的模式串字符长度

    // 如果模式串已经完全匹配,执行匹配成功后的逻辑
    if (j == m)
    {
        j = ne[j]; // 为下一次匹配,更新 j 的值
        // 匹配成功后的逻辑,例如打印匹配位置、存储匹配信息等
    }
}

Trie树

int son[N][26], cnt[N], idx;
// N 是树中节点的总数
// son[][] 是一个二维数组,存储指向子节点的索引,每个节点有26个可能的子节点(对应英文字母a-z)
// cnt[] 是一个一维数组,存储以每个节点结尾的单词数量
// idx 是一个计数器,用于生成新节点的索引

// 0号点既是根节点,又是空节点
// 根节点不包含任何字符,空节点是尚未存储任何信息的节点

// 插入一个字符串到树中
void insert(char *str)
{
    int p = 0; // p 是当前节点的索引,初始为根节点
    for (int i = 0; str[i]; i++) // 遍历字符串str中的每个字符
    {
        int u = str[i] - 'a'; // 将字符转换为0-25的整数,对应字母a-z
        if (!son[p][u]) son[p][u] = ++idx; // 如果在当前节点p下没有字符u的子节点,则创建一个新的子节点,并更新idx
        p = son[p][u]; // 移动到子节点,继续插入下一个字符
    }
    cnt[p]++; // 在字符串完全插入后,增加以该节点结尾的单词数量
}

// 查询字符串在树中出现的次数
int query(char *str)
{
    int p = 0; // p 是当前节点的索引,初始为根节点
    for (int i = 0; str[i]; i++) // 遍历字符串str中的每个字符
    {
        int u = str[i] - 'a'; // 将字符转换为0-25的整数
        if (!son[p][u]) return 0; // 如果在当前节点p下没有字符u的子节点,则字符串不存在于树中,返回0
        p = son[p][u]; // 移动到子节点,继续查询下一个字符
    }
    return cnt[p]; // 返回以该节点结尾的单词数量
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值