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

搜索

DFS 深度搜索:连通性问题

模板中,参数控制解答树的高度,内循环控制树的广度

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

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

//并查集优化
1. 并查集优化
    int parent(int x){
        return (a[x]==x)? x : a[x]=parent(a[x]);
    }
2. 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互素数的个数;

互素测试(辗转相除):

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

图论

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. “自底向上”计算最优解(初始化+记忆化搜索)

扫描二维码关注公众号,回复: 2655888 查看本文章

猜你喜欢

转载自blog.csdn.net/Fantastic_/article/details/78847185