程序竞赛中的数据结构

常见的数据结构

集合结构--->并查集

线性结构--->数组--->栈,队列,双端队列

树状结构--->二叉树,BST--->AVL树,splay树,Treap,Cartesian Tree,Size Balance Tree

图状结构--->邻接矩,阵邻接表,十字链表,邻接多重表

堆形结构--->二叉堆--->左偏堆,斜堆

数学结构--->哈希表

统计结构--->树状数组,线段树

字符结构--->前缀树,后缀树,后缀数组

快速排序

void ksort(int l, int h, int a[]) {
    if (h < l + 2) return;
    int e = h, p = l;
    while (l < h) {
        while (++l < e && a[l] <= a[p]);
        while (--h > p && a[h] >= a[p]);
        if (l < h) swap(a[l], a[h]);
    }
    swap(a[h], a[p]);
    ksort(p, h, a);
    ksort(l, e, a);
} 

并查集

/*==================================================*\ 
 | 带权值的并查集  | INIT: makeset(n); 
 | CALL: findset(x); unin(x, y); 
\*==================================================*/ 
struct lset {
    int p[N], rank[N], sz;

    void link(int x, int y) {
        if (x == y) return;
        if (rank[x] > rank[y]) p[y] = x; else p[x] = y;
        if (rank[x] == rank[y]) rank[y]++;
    }

    void makeset(int n) {
        sz = n;
        for (int i = 0; i < sz; i++) {
            p[i] = i;
            rank[i] = 0;
        }
    }

    int findset(int x) {
        if (x != p[x]) p[x] = findset(p[x]);
        return p[x];
    }

    void unin(int x, int y) { link(findset(x), findset(y)); }

    void compress() { for (int i = 0; i < sz; i++) findset(i); }
};

树状数组(区间求和)

/*==================================================*\ 
 | INIT: ar[]置为0; 
 | CALL: add(i, v): 将i点的值加v;  sum(i): 求[1, i]的和; 
\*==================================================*/ 
#define typev int        // type of res 
typev ar[N];             // index: 1 ~ N
int lowb(int t) { return t & (-t); }

void add(int i, typev v) { 
    for (; i < N; ar[i] += v, i += lowb(i)); 
}

typev sum(int i) {
    typev s = 0;
    for (; i > 0; s += ar[i], i -= lowb(i));
    return s;
} 

二维树状数组

/*==================================================*\ 
 | 二维树状数组 
 | INIT: c[][]置为0; Row,Col要赋初值 
\*==================================================*/ 
const int N = 10000;
int c[N][N];
int Row, Col;

inline int Lowbit(const int &x) { // x > 0  
    return x & (-x);
}

int Sum(int i, int j) {
    int tempj, sum = 0;
    while (i > 0) {
        tempj = j;
        while (tempj > 0) {
            sum += c[i][tempj];
            tempj -= Lowbit(tempj);
        }
        i -= Lowbit(i);
    }
    return sum;
}

void Update(int i, int j, int num) {
    int tempj;
    while (i <= Row) {
        tempj = j;
        while (tempj <= Col) {
            c[i][tempj] += num;
            tempj += Lowbit(tempj);
        }
        i += Lowbit(i);
    }
} 

RMQ问题(区间最小值)

/*==================================================*\ 
 | RMQ离线算法 O(N*logN)+O(1) 
 | INIT: val[]置为待查询数组; initrmq(n); 
\*==================================================*/ 
int st[20][N], ln[N], val[N];

void initrmq(int n) {
    int i, j, k, sk;
    ln[0] = ln[1] = 0;
    for (i = 0; i < n; i++) st[0][i] = val[i];
    for (i = 1, k = 2; k < n; i++, k <<= 1) {
        for (j = 0, sk = (k >> 1); j < n; ++j, ++sk) {
            st[i][j] = st[i - 1][j];
            if (sk < n && st[i][j] > st[i - 1][sk]) st[i][j] = st[i - 1][sk];
        }
        for (j = (k >> 1) + 1; j <= k; ++j) ln[j] = ln[k >> 1] + 1;
    }
    for (j = (k >> 1) + 1; j <= k; ++j) ln[j] = ln[k >> 1] + 1;
}

int query(int x, int y) // min of { val[x] ... val[y] }
{
    int bl = ln[y - x + 1];
    return min(st[bl][x], st[bl][y - (1 << bl) + 1]);
}
/*==================================================*\
 | RMQ(Range Minimum/Maximum Query)-st算法(O(nlogn + Q))
 | ReadIn() 初始化数组a[0...n-1];   
 | InitRMQ()利用st算法( O(nlogn) )进行预处理; 
 | Query()根据输入的下标查询值(O(Q))                                
 | Hint: 下标范围:0...n-1,如果为1...n须稍做修改; 此处实现的的是求
 | 大值, 如果求小值需要把max->min  
 | Call: ReadIn(n); InitRMQ(n); Query(Q); 
\*==================================================*/
const int N = 200001;
int a[N], d[20];
int st[N][20];

int main(void) {
    int n, Q;
    while (scanf("%d%d", &n, &Q) != EOF) {
        ReadIn(n);
        InitRMQ(n);
        Query(Q);
    }
    return 0;
}

void ReadIn(const int &n) {
    int i;
    for (i = 0; i < n; ++i) scanf("%d", &a[i]);
}

inline int max(const int &arg1, const int &arg2) { return arg1 > arg2 ? arg1 : arg2; }

void InitRMQ(const int &n) {
    int i, j;
    for (d[0] = 1, i = 1; i < 21; ++i) d[i] = 2 * d[i - 1];
    for (i = 0; i < n; ++i) st[i][0] = a[i];
    int k = int(log(double(n)) / log(2)) + 1;
    for (j = 1; j < k; ++j)
        for (i = 0; i < n; ++i) {
            if (i + d[j - 1] - 1 < n) {
                st[i][j] = max(st[i][j - 1], st[i + d[j - 1]][j - 1]);
            } else break; // st[i][j] = st[i][j-1];   
        }
}

void Query(const int &Q) {
    int i;
    for (i = 0; i < Q; ++i) {
        int x, y, k; // x, y均为下标:0...n-1   
        scanf("%d%d", &x, &y);
        k = int(log(double(y - x + 1)) / log(2.0));
        printf("%d\n", max(st[x][k], st[y - d[k] + 1][k]));
    }
} 

线段树 (动态范围最小值)

const int maxn=2005+5;
#define lson l,m,rt<<1          //预定子左树
#define rson m+1,r,rt<<1|1      //预定右子树
int sum[maxn<<2];//表示节点,需要开到最大区间的四倍
void pushup(int rt){
    //对于编号为rt的节点,他的左右节点分别为rt<<1和rt<<1|1
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}

//造树
void build(int l,int r,int rt=1){
    //建树操作,生成一个区间为l~r的完全二叉树

    //如果到底,则线段长度为0,表示一个点,输入该点的值
    if (l==r) {
        sum[rt]=0;
        return;
    }

    //准备子树
    int m=(l+r)>>1;

    //对当前节点建立子树
    build(lson);
    build(rson);

    //由底向上求和
    pushup(rt);
}

//更新点和包含点的枝
void update(int pos,int val,int l,int r,int rt=1){
    //pos为更新的位置 val为增加的值,正则加,负则减
    //l r为区间的两个端点值

    //触底,为一个点的时候,该节点值更新
    if (l==r) {
        sum[rt]+=val;
        return;
    }

    int m = ( l + r ) >> 1;

    if (pos<=m)     //pos在左子树的情况下,对左子树进行递归
        update(pos, val, lson);
    else            //pos在右子树的情况下,对右子树进行递归
        update(pos, val, rson);

    //更新包含该点的一系列区间的值
    pushup(rt);
}

//查询点或区间
int query(int L,int R,int l,int r,int rt=1){
    // L~R为被查询子区间 l~r为“当前”树的全区间
    if (L<=l&&r<=R)    //子区间包含“当前”树全区间
        return sum[rt]; //返回该节点包含的值
    int m=(l+r)>>1;
    int res=0;
    if (L<=m)       //左端点在左子树内
        res+=query(L, R, lson);
    if (R>m)        //右端点在右子树内
        res+=query(L, R, rson);
    return res;
}

字典树(Trie树)

/*==================================================*\ 
 | Trie树(k叉) 
 | INIT: init(); 
 | 注: tree[i][tk]>0时表示单词存在, 当然也可赋予它更多含义; 
\*==================================================*/ 
const int tk = 26, tb = 'a';  // tk叉; 起始字母为tb;
int top, tree[N][tk + 1];     // N: 大结点个数 
void init() {
    top = 1;
    memset(tree[0], 0, sizeof(tree[0]));
}

int sear(char *s) { // 失败返回0  
    for (int rt = 0; rt = tree[rt][*s - tb];) 
        if (*(++s) == 0) return tree[rt][tk];
    return 0;
}

void insert(char *s, int rank = 1) {
    int rt, nxt;
    for (rt = 0; *s; rt = nxt, ++s) {
        nxt = tree[rt][*s - tb];
        if (0 == nxt) {
            tree[rt][*s - tb] = nxt = top;
            memset(tree[top], 0, sizeof(tree[top]));
            top++;
        }
    }
    tree[rt][tk] = rank; //1表示存在0表示不存在,也可以赋予其其他含义 
}

void delt(char *s) { // 只做标记, 假定s一定存在  
    int rt = 0;
    for (; *s; ++s) rt = tree[rt][*s - tb];
    tree[rt][tk] = 0;
}

int prefix(char *s) { // 最长前缀  
    int rt = 0, lv;
    for (lv = 0; *s; ++s, ++lv) {
        rt = tree[rt][*s - tb];
        if (rt == 0) break;
    }
    return lv;
} 
/*==================================================*\ 
 | Trie树(左儿子又兄弟) 
 | INIT: init(); 
\*==================================================*/ 
int top;
struct trie {
    char c;
    int l, r, rk;
} tree[N];

void init() {
    top = 1;
    memset(tree, 0, sizeof(tree[0]));
}

int sear(char *s) {               // 失败返回0  
    int rt;
    for (rt = 0; *s; ++s) {
        for (rt = tree[rt].l; rt; rt = tree[rt].r) 
            if (tree[rt].c == *s) break;
        if (rt == 0) return 0;
    }
    return tree[rt].rk;
}

void insert(char *s, int rk = 1) { //rk: 权或者标记  
    int i, rt;
    for (rt = 0; *s; ++s, rt = i) {
        for (i = tree[rt].l; i; i = tree[i].r) 
            if (tree[i].c == *s) break;
        if (i == 0) {
            tree[top].r = tree[rt].l;
            tree[top].l = 0;
            tree[top].c = *s;
            tree[top].rk = 0;
            tree[rt].l = top;
            i = top++;
        }
    }
    tree[rt].rk = rk;
}

void delt(char *s) {   // 假定s已经存在, 只做标记  
    int rt;
    for (rt = 0; *s; ++s) { 
        for (rt = tree[rt].l; rt; rt = tree[rt].r) 
            if (tree[rt].c == *s) break; 
    }
    tree[rt].rk = 0;
}

int profix(char *s) {  // 最长前缀  
    int rt = 0, lv;
    for (lv = 0; *s; ++s, ++lv) {
        for (rt = tree[rt].l; rt; rt = tree[rt].r) 
            if (tree[rt].c == *s) break;
        if (rt == 0) break;
    }
    return lv;
} 

KMP(单模式匹配)

/*==================================================*\ 
 | KMP匹配算法O(M+N) 
 | CALL: res=kmp(str, pat); 原串为str; 模式为pat(长为P); 
\*==================================================*/ 
int fail[P];

int kmp(char *str, char *pat) {
    int i, j, k;
    memset(fail, -1, sizeof(fail));
    for (i = 1; pat[i]; ++i) {
        for (k = fail[i - 1]; k >= 0 && pat[i] != pat[k + 1]; k = fail[k]);
        if (pat[k + 1] == pat[i]) fail[i] = k + 1;
    }
    i = j = 0;
    while (str[i] && pat[j]) { // By Fandywang  
        if (pat[j] == str[i]) ++i, ++j;
        else if (j == 0)++i;   //第一个字符匹配失败,从str下个字符开始  
        else j = fail[j - 1] + 1;
    }
    if (pat[j]) return -1; else return i - j;
} 

AC自动机(多模式匹配)

/*==================================================*\ 
 | tire是字典树,fail是失败数组;
 | 剩下的数组在用到的时候会说明 。
 | 这里假定每个模式串的长度不超过45; 
\*==================================================*/ 

int tire[INF][30], fail[INF], End[INF],auxLen[INF]; 
char aux[INF][45] ;		 
int root = 0, index = 0;
void insert(char* data, int rt) {
    int len = strlen(data);
    rt = root;
    for(int i = 0; i < len; i++) {  //建树的过程 
        int y = data[i] - 'a';
        if(tire[rt][y] == 0) {
            tire[rt][y] = ++index;
        }
        rt = tire[rt][y];
    }

    // 此时的rt结点是代表的字符串是该模式串,而不是模式串的一个前缀。
    // 在查找的时候如果End[rt]大于0,就说明rt结点代表的是一个模式串,而不是一个前缀;
	
    End[rt]++; 
    auxLen[rt] = len;	  //把rt结点代表的字符串的长度存在auxLen中。 
    strcpy(aux[rt],data); //把rt结点代表的字符串拷贝到aux中; 
}
 
void build() {		//通过bfs来建立失败数组 
    queue <int> que;
    int rt = root;
    for(int i = 0; i < 26; i++) {
        if(tire[rt][i] != 0) {
            que.push(tire[rt][i]);	//初始化,将每个模式串的的首字符加入队列 
        }
    }
    while(!que.empty()) {
        int now = que.front();
        que.pop();
        for(int i = 0; i < 26; i++) {
            if(tire[now][i] == 0) {
                tire[now][i] = tire[fail[now]][i];
            } else {
                fail[tire[now][i]] = tire[fail[now]][i];
                que.push(tire[now][i]);
            }
        } 
    }
}
int query(char* data) {  //查询过程 
    int len = strlen(data);
    int rt = root;
    int res = 0;
    for(int i = 0; i < len; i++) {
        int y = data[i] - 'a';
        rt = tire[rt][y];   
        int jump = rt;
		while(jump != root) { //如果能匹配就判断该jump结点代表的字符串是前缀还是模式串 
            if(End[jump] > 0) {  //如果是模式串的话 
                res += End[jump] ;
                printf("%d ",i - auxLen[jump] + 2);
                printf("%s\n",aux[jump]);
                End[jump] = 0;
            } 
			
            // 将jump指向该结点的失败指针 ,
            // 看一下该结点代表的字符串的最大后缀是不是模式串;  
            jump = fail[jump];   
        }
    }
    return res;
}

字符串hash

/*==================================================*\ 
 | 字符串Hash 
 | 注意:mod选择足够大的质数(至少大于字符串个数) 
\*==================================================*/ 
unsigned int hasha(char *url, int mod) {
    unsigned int n = 0;
    char *b = (char *) &n;
    for (int i = 0; url[i]; ++i) b[i % 4] ^= url[i];
    return n % mod;
}

unsigned int hashb(char *url, int mod) {
    unsigned int h = 0, g;
    while (*url) {
        h = (h << 4) + *url++;
        g = h & 0xF0000000;
        if (g) h ^= g >> 24;
        h &= ~g;
    }
    return h % mod;
}

int hashc(char *p, int prime = 25013) {
    unsigned int h = 0, g;
    for (; *p; ++p) {
        h = (h << 4) + *p;
        if (g = h & 0xf0000000) {
            h = h ^ (g >> 24);
            h = h ^ g;
        }
    }
    return h % prime;
} 

Karp-Rabin字符串匹配

/*==================================================*\ 
 | Karp-Rabin字符串匹配 
 | hash(w[0..m-1]) = 
 | (w[0] * 2^(m-1) + ... + w[m-1] * 2^0) % q; 
 | hash(w[j+1..j+m]) = 
 | rehash(y[j], y[j+m], hash(w[j..j+m-1]); 
 | rehash(a, b, h) = ((h - a * 2^(m-1) ) * 2 + b) % q; 
 | 可以用q = 2^32简化%运算 
\*==================================================*/ 
#define REHASH(a, b, h) ((((h) - (a)*d) << 1) + (b))

int krmatch(char *x, int m, char *y, int n) { // search x in y  
    int d, hx, hy, i, j;
    for (d = i = 1; i < m; ++i) d = (d << 1);
    for (hy = hx = i = 0; i < m; ++i) {
        hx = ((hx << 1) + x[i]);
        hy = ((hy << 1) + y[i]);
    }
    for (j = 0; j <= n - m; ++j) {
        if (hx == hy && memcmp(x, y + j, m) == 0) return j;
        hy = REHASH(y[j], y[j + m], hy);
    }
} 

后缀数组

/*==================================================*\ 
 | 后缀数组 O(N * log N) 
 | INIT: n = strlen(s) + 1; 
 | CALL: makesa(); lcp(); 
 | 注: height[i] = lcp(sa[i], sa[i-1]); 
\*==================================================*/ 
char s[N];                          // N > 256 
int n, sa[N], height[N], rank[N], tmp[N], top[N];

void makesa() {                       // O(N * log N)  
    int i, j, len, na;
    na = (n < 256 ? 256 : n);
    memset(top, 0, na * sizeof(int));
    for (i = 0; i < n; i++) top[rank[i] = s[i] & 0xff]++;
    for (i = 1; i < na; i++) top[i] += top[i - 1];
    for (i = 0; i < n; i++) sa[--top[rank[i]]] = i;
    for (len = 1; len < n; len <<= 1) {
        for (i = 0; i < n; i++) {
            j = sa[i] - len;
            if (j < 0) j += n;
            tmp[top[rank[j]]++] = j;
        }
        sa[tmp[top[0] = 0]] = j = 0;
        for (i = 1; i < n; i++) {
            if (rank[tmp[i]] != rank[tmp[i - 1]]  
               || rank[tmp[i] + len] != rank[tmp[i - 1] + len])
                top[++j] = i;
            sa[tmp[i]] = j;
        }
        memcpy(rank, sa, n * sizeof(int));
        memcpy(sa, tmp, n * sizeof(int));
        if (j >= n - 1) break;
    }
}

void lcp() {                          // O(4 * N)  
    int i, j, k;
    for (j = rank[height[i = k = 0] = 0]; i < n - 1; i++, k++)
        while (k >= 0 && s[i] != s[sa[j - 1] + k])
            height[j] = (k--), j = rank[sa[j] + 1];
} 

代码多源于网络

猜你喜欢

转载自blog.csdn.net/qq_42024195/article/details/88927899