开发与面试涉及的基础数据结构和算法-Algorithm

来源:互联网 发布:宋徽宗 知乎 编辑:程序博客网 时间:2024/06/05 03:25

搜索

DFS 深搜方向:叶子数量级

1.排列级P(m,n)=m!/(m-n)!

int vis[n];dfs(int k){    if(k==n){        //执行P(m,n)次        return;     }    for(int i=0; i < n; i++)    if(vis[i]){        vis[i]=1;        dfs(k+1);        vis[i]=0;    }}

2.组合级C(m,n)=P(m,n)/m!

int vis[n];dfs(int k){    if(k==n){        //执行C(m,n)次        return;    }    for(int i=k; i < n; i++)        dfs(k+1);}

3.记忆化:路径压缩
思想: 对于已经搜索过并足够小的路径,记忆搜索结果备下次使用

//并查集优化int parent(int x){    return (a[x]==x)? x : a[x]=parent(a[x]);}1. a+b组不成的最大数:     dfs[x]=(dfs[x-a]||dfs[x-b])?1:0;     if(!dfs[x])ans=max(ans,x);

BFS 宽搜

int g[n][n],vis[n],arr[n],ap=0;//初始化vis[0]=1;arr[ap++]=0;//遍历队列for(int i=0;i < ap; i++){    int k=a[i];    //code for deal node k;    for(int j=0;j < n;j++){//拓展队列        if(g[k][j] && !vis[j]){            arr[ap++]=j;            vis[j]=1;           }    }}

数论

//线性时间打素数表int arr[(int)(n/log(n)*1.5)],ap,vis[n];for(int i=2; i < n; i++){    if(!vis[i])arr[ap++]=i;    for(int j=0; j < ap && i*a[j] < n;j++ ){        vis[i*a[j]]=1;        if(i%a[j] == 0)break;//num=abx,其中a,b为素数,这样的num会被筛两次    }}

欧拉函数:euler(n)=n* (1-1/q[i]);其中q为n唯一分解素数,函数值是1-n中与n互素数的个数; 若 euler(n)=n-1则为素数;
互素测试(辗转相除):

int gcd(int x,int y){    return (y==0) ? x : gcd(y, x%y); //Fibonacci级减少}

图论

1. 最小生成树:连通一个图的所有节点,所消耗总代价最少(电话连线问题)2. Dijkstra最短路径:单源点到其他各点的最短路径 - O(n^2)3. Floyd最短路径:图中任意两点最短路径 - O(n^3)

贪心模型(启发式算法)

证明无后效性:对具有最优子结构的一维问题,每次决策都直接影响结果,并不可回退(前面状态->当前状态->后面状态);

1. 最优装载问题(部分背包问题):尽量装载单位体积价值大的物品2. Haffman可变长编码:出现频率高的字符,编码长度尽量短3. Prim算法:每次找出距离当前【集】最近的结点4. Dijkstra算法:每次找出距离【源点】最近的结点5. krusal算法:每次找出两个端点 不在同一集中 且 长度最短的边

分治模型

可分解:问题可分解成和原问题 性质相同 且 相互独立 的若干子问题;
可合并:若干子问题的解可合并得出原问题的解;

1. 二分法: 原问题分解成两段【快速幂】2. 第K小的数: [1,n]求k => [1,n/2)或(n/2,n]求k - 快排3. 逆序对数: 一个序列,相邻元素可以交换,求交换多少次序列有序 - 归并

动态规划模型

DP-贪心: DP中局部最优解不一定是全局最优解;
DP-分治: DP划分的子问题不具备独立性;

1. 阶段求解(递推式)

1. 上N阶台阶方案数: 一次上1阶或2阶,求上到n台阶的总方案数;    对于 i 台阶,能达到的台阶为 i-1和i-2; 故 a[i]=a[i-1]+a[i-2];2. RPG难题: 对于N个空位,使用R,P,G填充,要求相邻和首尾不同,求总方案数;    当i>=4时: 若   p[i-1] == p[0] 则 a[i]=2*a[i-1];             若  p[i-1] != p[0] 则 a[i]=a[i-2];3. 危险的组合: 对于N个格子摆放01要求至少30连续摆放,求总方案数;    当i>=4时:     若前i-1个已经符合要求 则 a[i]=2*a[i-1]                若前i-1个不符合时i必须放0a[i]=2^(i-3) - a[i-3];4. 全错位排列:     对于_(___)    若 _与(____)中恰好交换:a[i]=a[i-2]*(i-1)                 若 _与(____)中不是交换:a[i]=a[i-1]*(i-1),此时把_在(___)中视为正确摆放5. 排队问题: 队伍中m个5和n个10,初始无5元找开,求合法排队方案数(同样钱的人交换视为同一种)    对于队伍m+n: 若最后一人持5元 则方案数 a[m][n] = a[m-1][n]                若最后一人持10元 则方案数 a[m][n] = a[m][n-1]        初始化:a[i][0]=1; a[i][i+x]=0;

2. 动态决策

1. 【线性】最长公共子序列: 对于每个i,讨论j=[1,n],dp[j]表示在逆序遍历j位置时最长公共序列长    从后往前遍历s和t,  若s[i]==t[j] 则 dp[j]=dp[j+1]+1;                    否则 dp[j]=max(dp[j],dp[j+1]);最长上升子序列: 对于每个i,dp[i]表示在i位置的最长上升序列长度    顺序遍历a[N]:对于a[i]往前找到一个j使得a[i]>a[j] 则 dp[i]=dp[j]+1;3. 【背包】0-1背包问题:对于每个容量c放a[i]物品,放或不放形成两个决策结果    对于dp[c] 若c>=v[i] 则 dp[c]=max(dp[c-a]+v,dp[c]);    注意: 此处要求逆序遍历容量c;完全背包问题(物品无限)    同0-1背包问题状态转移 dp[c]=max(dp[c-a]+v,dp[c]);    注意: 对于容量要求顺序遍历,这样才能保证多次放入i物品DAG定点最长/短路径: 硬币找零问题,划分当前金额数为子结构    对于当前金额i dp[i]=max(dp[i-v[j]]+1,dp[i])    注意: 对于当前金额要顺序遍历,保证同一硬币可以多次加入;6. 【划分】整数划分:把n划分为k份的方案数    对于dp[n][k]  若 n>=k dp[n][k]=dp[n-k][k]+dp[n][k-1];                若 n < k  dp[n][k]=dp[n][k-1];    初始化: dp[n][1]=1;7. 【区域】石子合并:相邻合并若干堆石子,代价为w[i]+w[i+1],求最小总代价    对于dp[i][j]表示合并石子i->j的最优解;定义k=[i,j]则 dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+w[j]-w[i-1]);最后值为dp[1][n];    初始化: dp[i][j]=INF,dp[i][i]=0; w[i]表示1-i石子代价总和;Floyd:     找到一个中间点k,用k插入到所有的[i,j]区间并更新[i,j]区间长度9. 【树形】数字三角形    对于以i为根的树,dp[i]=dp[i]+max(dp[2*i],dp[2*i+1]);

DP问题:
1. 描述一个最优解的结构(划分问题)
2. 递归地定义最优解的值(状态转移)
3. “自底向上”计算最优解(初始化+记忆化搜索)

原创粉丝点击