基本数据结构汇总

元素先进后出 栈顶进栈顶出

一般用数组或链表来实现

插入删除时间复杂度O(1) 空间复杂度O(n)

单调栈 乱发节 从前向后扫,维护一个自底向上单调递减的单调栈。每扫到一个数,就把栈顶所有小于这个数的元素弹出栈,把这个元素加入栈。

进出栈序列问题

递归 O(2^n)

枚举每一步进栈还是出栈

递推 O(n*n)

考虑1在出栈序列中的位置,如果1排在第k个,问题就被划分为"k-1个数进出栈"和"n-k个数进出栈"两个子问题

得到递推公式:Sn=∑(1≤k≤n)Sk-1*Sn-k

动规  O(n*n)

F[i][j]表示有i个数尚未入栈,目前有j个数还在栈里,已经有n-i-j个数出栈的方案总数

f[i,j]表示的是当前状态到最终状态的方案数

最终所有数都已经出栈时顺序已经确定 所以边界为F[0][0]=1

目标为F[n][0]

每一步两种决策分别是一个数入栈或栈顶出栈

F[i][j]=F[i-1][j+1]+F[i][j-1]

公式中枚举下一步操作 相当于倒着推

数学 O(n)

求第n项卡特兰数

 

表达式计算

后缀表达式求值 形如"A B op" 不存在括号

建立一个用于存数的栈,逐一扫描元素

遇到数入栈,遇到运算符取栈顶两个数计算并将结果入栈,最后栈中恰好剩下一个数为结果

中缀表达式(最常见的表达式)转后缀表达式

建立一个用于存运算符的栈,逐一扫描元素

遇到数输出,遇到左括号入栈,遇到右括号不断取出栈顶并输出直到左括号出栈,遇到运算符 只要栈顶符号的优先级(乘除>加减>左括号)不低于新符号就不断取出栈顶并输出 最后把新符号进栈

依次取出并输出栈中的剩余符号,最终输出序列即为等价的后缀表达式

 

队列

元素先进先出 在表的前端(队首)进行删除,在表的后端(队尾)进行插入

一般用数组(加两个变量)或链表来实现

循环队列:循环利用数组空间来节省空间

                     STL中的queue

单调队列算法 O(n)

思想是在决策集合队列中及时排除不可能成为最优解的答案

滑动的窗户

枚举区间结尾,用一个单调递减的数列维护区间最大值,一个单调递增的数列维护区间最小值。

修剪草坪

f[i]表示删去第i只奶牛,前i只合法删去的最小效率和

f[i]=min(f[i-j])+Ei (1<=j<=k)

用一个单增队列维护i-k~i-1的情况 判断第一项是否在范围内,用首项更新f[i],然后把单调队列末尾比f[i]大的删去,把f[i]加入

 

链表

链表是非连续、非顺序的存储结构,数据元素的逻辑顺序是通过指针链接次序实现的

插入删除时间复杂度O(1) 查询元素时间复杂度O(n)空间复杂度O(n)

数组模拟链表 下标模拟指针

struct Node{

    int val;

    int pre,nxt;

}n[ ];

int head,tail,tot;

邻接表

//加入有向边(x,y)权值为z

void add ( int x ,int y ,int z ){

    ver[++tot]=y,val[tot]=z;

    nxt[tot]=head[x],head[x]=tot;

//访问从x出发的所有边

for(int i=head[x];i;i=nxt[i]){

    int y=ver[i],z=val[i];

}

 

并查集

一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。常以森林表示。

初始时将每个节点的dad数组设为本身

优化:路径压缩 按秩合并

void find(int x){

    return dad[x]==x?x:dad[x]=find(dad[x]);

}

dad[find(x)]=find(y);

带权并查集  对于每一条连向父亲的边都保存其权值 

食物链  带权并查集 0表示同类 1表示吃 2表示被吃

如果已经知道x和y,y和z的关系,就可以通过相加对3取模求得x和z的关系

int find(int x){

    if(dad[x]==x) return x;

    find(dad[x]);

    (h[x]+=h[dad[x]])%=3;

    return dad[x]=dad[dad[x]];

}

bool comb(int x,int y,int z){   //x和y之间关系为z是否合法

    int X=find(x),Y=find(y);

    if(X==Y) return (h[x]==(z+h[y])%3);

    dad[X]=Y;

    h[X]=(z-h[x]+h[y]+3)%3;

    return 1;

}

 

Hash

主要用来对大规模数据进行比较

一般由hash函数和邻接表结构共同实现,以hash函数的值域作为表头数组head,映射后的值相同的原始信息被分到同一类,构成一个链表接到对应的表头之后

字符串hash

按位转化成一个高进制大整数(进制数必须>字母种类数 为保证在不取模情况下不会重复 最好选质数) 然后取模

进制数是mod的原根

原根:x^1~x^(p-2)对p取模都不重复 x是p的原根

取一固定值p,把字符串看作p进制数,并分配一个>0的数值代表每种字符(远小于p);取一固定值m,求出该p进制数对m的余数作为该字符串的hash值

一般取p=131或p=13331 m=2^64(用unsigned long long存储 自然溢出)

O(n)预处理字符串所有前缀Hash值,并在O(1)的时间内查询任意子串的Hash值

H(T)=( H(S+T)-H(S)*p^(length(T)) )mod m

hash可以直接进行修改

    for(int i = 1; i <= n; i++){

        hesh = hesh * p + a[i] - 'a' + 1;

    }

 

 

Trie树

用于实现字符串检索的多叉树结构

每个节点都有若干个字符指针,若插入或扫描到一个字符,就沿着当前节点的该字符指针向下走

int trie[size][26],tot=1,end[size];//初始化 假设由小写字母构成

void insert(char* str){//插入字符串

    int len=strlen(str),p=1;

    for(int k=0;k<len;k++){

        int ch=str[k]-'a';

        if(trie[p][ch]==0) trie[p][ch]=++tot;

        p=trie[p][ch];

    }

    end[p]=true;

}

bool search(char* str){//检索字符串是否存在

    int len=strlen(str),p=1;

    for(int k=0;k<len;k++){

        p=trie[p][str[k]-'a'];

        if(p==0) return 0;

    }

    return end[p];

}

 

堆为一棵二叉树,满足:①父节点的键值总是>=(or <=)任何一个子节点的键值

                                     ②每个节点的左子树和右子树都是一个堆

二叉堆 完全二叉树 支持插入节点、删除节点、求最小值

节点x的儿子分别为2x,2x+1

插入节点时,放在末尾,与父亲不断比较 交换

删除节点时,用二叉堆末尾节点替换该节点,之后将末尾节点向下pushdown,和左右儿子中较小的比较

种树   把整个环用双向链表套起来

          之后每次找到环上权值最大的节点,更新答案,之后将它的权值更新为该节点左右两个节点减去该节点的权值,最后将左右两个节点从双向链表中删除即可。

 

 

#include<cstdio>

#include<iostream>

using namespace std;

int hp[105],n,siz;

void push(int x)//输(插)入

{

    if(hp[x]>=hp[x/2])

        return;

    else

    {

        int a=hp[x];

        hp[x]=hp[x/2];

        hp[x/2]=a;

        push(x/2);

    }

}

void del()//删除堆顶 维护堆

{

    int now=1;

    while(2*now<=siz)//有儿子

    {

        int lison=2*now;

        if(lison<siz && hp[lison+1]<hp[lison])//有右儿子 且 左儿子>右儿子

            lison++;

        if(hp[now]>hp[lison])

            swap(hp[now],hp[lison]);

        else break;

        now=lison;

    }

}

int main()

{

    cin>>n;

    for(int i=1;i<=n;i++)

    {

        cin>>hp[i];

        push(i);

    }

    for(siz=n;siz>=2; )//堆排

    {

        swap(hp[1],hp[siz]);//将最小的置于末尾

        siz--;

        del();

    }

    for(int i=1;i<=n;i++)//从大到小

        cout<< hp[i] <<" ";

    return 0;

}

 

©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值