数据结构

数据结构

单链表

// 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 ++ ;
}

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

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

双链表

int e[N], l[N], r[N], idx;
//初始化
void init(){
    // 0是左端点,1是右端点
    r[0] = 1, l[1] = 0;
    idx = 2;
}
// 在节点a的右边插入一个数x
void insert(int a, int x)
{
    e[idx] = x;
    l[idx] = a, r[idx] = r[a];
    l[r[a]] = idx, r[a] = idx ++ ;
}

// 删除节点a
void remove(int a)
{
    l[r[a]] = l[a];
    r[l[a]] = r[a];
}

单调栈

//维护一个单调递增或者单调递减的栈
int main()
{
    int n;
    cin >> n;
    while (n -- )
    {
        int x;
        scanf("%d", &x);
        while (tt && stk[tt] >= x) tt -- ;//如果栈顶元素大于当前待入栈元素,则出栈
        if (!tt) printf("-1 ");//如果栈空,则没有比该元素小的值。
        else printf("%d ", stk[tt]);//栈顶元素就是左侧第一个比它小的元素。
        stk[ ++ tt] = x;
    }
    return 0;
}

滑动窗口

//单调队列一般用双端队列保证其单调性
//q[i] 表示元素下标
int main()
{
    int n, k;
    scanf("%d%d", &n, &k);
    for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);

    int 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 -- ;//保持队列单调性
        q[ ++ tt] = i;

        if (i >= k - 1) printf("%d ", a[q[hh]]);
    }

    puts("");

    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 -- ;
        q[ ++ tt] = i;

        if (i >= k - 1) printf("%d ", a[q[hh]]);
    }

    puts("");

    return 0;
}

KMP

#include <iostream>

using namespace std;

const int N = 100010, M = 1000010;

int n, m;
int ne[N]; // 最大公共前后缀
char s[M], p[N];// s 模式串 t 文本串

int main()
{
    cin >> n >> p + 1 >> m >> s + 1; //下标都从 1 开始

    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;
    }

    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 <iostream>

using namespace std;

const int N = 100010;
//son[][]存储子节点的位置,分支最多26条;
//cnt[]存储以某节点结尾的字符串个数(同时也起标记作用)
//idx表示当前要插入的节点是第几个,每创建一个节点值+1
int son[N][26]; //相当于链表中的ne[N]
int cnt[N], idx; 
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];  //使“p指针”指向下一个节点
    }
    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];//p[]数组存储的是某个节点的直接父亲节点

int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}
//初始化
for (int i = 1; i <= n; i ++ ) p[i] = i;
//合并
p[find(a)] = find(b);

//d[x]便可以始终表示x到根节点的距离
int find(int x)
{
    if(p[x] != x)
    {
        int tmp = find(p[x]);
        d[x] += d[p[x]];
        p[x] = tmp;
    }
    return p[x];
}
/*
因为执行完路径压缩后,x的所有祖宗都指向了根节点,所以d[p[x]]就是x的父节点到根节点的距离,而此时x没有指向根节点!!!!!!!(记住这句话),所以d[x]的值是x到父节点的距离,两者相加就是x到根节点的距离,之后再执行p[x] = tmp,让x指向根节点。
*/

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 = u * 2 + 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;
    }
}

//建堆
for (int i = n / 2; i; i -- ) down(i);

//输出堆顶元素
while (m -- )
{
    printf("%d ", h[1]);
    h[1] = h[cnt -- ];
    down(1);
}

哈希表

开放寻址发

const int N = 200003, null = 0x3f3f3f3f;

int h[N];

memset(h, 0x3f, sizeof h);

int find(int x)
{
    int t = (x % N + N) % N;
    while (h[t] != null && h[t] != x)
    {
        t ++ ;
        if (t == N) t = 0;
    }
    return t;
}

拉链法

const int N = 100003;

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

memset(h, -1, sizeof h);

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;
}

字符串哈希

typedef unsigned long long ULL; 
//用 unsigned long long 存储,溢出相当于对 2^64 取模,省略了手动运算
const int N = 100010, P = 131;

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", &n, &m);
    scanf("%s", str + 1);

    p[0] = 1;
    for (int i = 1; i <= n; i ++ )
    {
        h[i] = h[i - 1] * P + str[i];
        p[i] = p[i - 1] * P;
    }

    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;
}

hash(DEF) = hash(ABCDEF) - hash(ABC) x P^3
    1       2       3       4       5       6
    A       B       C       D       E       F  
  1xP^5 + 2xP^4 + 3xP^3 + 4xP^2 + 5xP^1 + 6xP^0

                            D       E       F
                          4xP^2 + 5xP^1 + 6xP^0

    A       B       C  
  1xP^2 + 2xP^1 + 3xP^0


posted @ 2025-08-12 10:13  catis  阅读(9)  评论(0)    收藏  举报