项目介绍
- 本项目通过分解各大厂的常见笔面试题,追本溯源至数据结构和算法的底层实现原理,知其然知其所以然;
- 建立知识结构体系,方便查找,欢迎更多志同道合的朋友加入项目AlgorithmPractice,(欢迎提issue和pull request)。
Part One : 牛刀小试
这部分内容选取了几道比较简单的动态规划题目,来逐步了解什么是动态规划。
- 1、青蛙跳台阶
- 2、最大子段和
- 3、最长公共子序列
- 4、回文应用(Palindrome)
- 4.1、判断字符串是否属于回文
- 4.2、添加一个字符构造回文
- 4.3、删除一些字符构造回文
- 4.4、查找字符串中最长的连续回文子串
Part Two : 抛砖引玉
这部分内容只给出了基础的解,如果有更优化的解法,请在下方留言。
Part Three : 综合集锦
这部分内容的解法不限于动态规划,包括贪心、回溯等。
正文开始
1、青蛙跳台阶
- 题目描述:在爬楼梯时,每次可向上走1阶台阶或2阶台阶,问有n阶楼 梯有多少种上楼的方式
- 代码实现:ClimbStairs,测试用例:ClimbStairsTest
- 设计思路:
- 状态转换方程:
statusNum[i] = statusNum[i-1] + statusNum[i-2];
- 注意事项:
- 对于经常访问的数据,可以设置缓存,方便读取。
2、最大子段和
状态转换方程:
LargestSum[i] = Math.max(LargestSum[i-1] + Sequence[i], Sequence[i]);
- 动态规划优化法:
for(int i = 1; i < Sequence.length; i++){
sum_temp += Sequence[i];
if(sum_temp <= Sequence[i]){
//新开始
sum_temp = Sequence[i]; //sum清空
begin_temp = i; //假定的起始位置
}
if(sum < sum_temp){
sum = sum_temp; //记录最大值
begin = begin_temp; //假定的起始地址赋值给起始地址
end = i; //结束地址包括 i
}
}
- 分治法:
int leftValue = divide(Sequence, left, mid);
int rightValue = divide(Sequence, mid + 1, right);
int midValue = mid(Sequence, left, right);
- 注意事项:
3、最长公共子序列
num_matrix[row][column] = num_matrix[row-1][column-1]+1;
num_matrix[row][column] = num_matrix[row-1][column];
num_matrix[row][column] = num_matrix[row][column-1];
- 注意事项:
4、回文应用(Palindrome)
4.1、判断字符串是否属于回文
- 题目描述:判断给定字符串是否是回文,默认单个字符不够成回文
- 代码实现:
public boolean PalindromeJudge(String testString) {
if(testString == null || testString.length() == 0){
return false;
}
boolean flag = new StringBuffer(testString).reverse().toString().equals(testString);
return flag;
}
- 设计思路:字符串反转并匹配
- 注意事项:
4.2、添加一个字符构造回文
- 题目描述:给定一个字符串s,你可以从中添加一个字符,使得剩下的串是一个回文串,如果通过添加一个字符得到回文,则返回添加元素的位置(起始地址:1),否则返回 -1,如果本身是回文,返回中间的位置
- 代码实现:CreatePalindromebyAdd,测试用例:CreatePalindromebyAddTest
- 设计思路:
- 注意事项:
4.3、删除一些字符构造回文
- 题目描述:给定一个字符串s,你可以从中删除一些字符,使得剩下的串是一个回文串。如何删除才能使得回文串最长呢?输出需要删除的字符个数
- 代码实现:CreatePalindromebyDelete,测试用例:CreatePalindromebyDeleteTest
- 设计思路:字符串反转,并通过LCS算法计算最大相似数,那么不相似的那些数就需要删除了。
- 注意事项:
4.4、查找给定字符串中最长的连续回文子串
- 题目描述:
- 代码实现:FindPalindrome,测试用例:FindPalindromeTest
- 4.4.1、暴力法
- 4.4.2、中心扩散法
- 4.4.3、Manacher 算法
- 设计思路:
- 4.4.1、暴力法
- 4.4.2、中心扩散法
- 4.4.3、Manacher 算法
- 注意事项:
5、送货最短路径
- 题目描述:
某物流派送员p,需要给a、b、c、d,4个快递点派送包裹, 请问派送员需要选择什么的路线,才能完成最短路程的派送。假设如图派送员的起点坐标(0,0),派送路线只能沿着图中的方格边行驶,每个小格都是正方形,且边长为1,如p到d的距离就是4。随机输入n个派送点坐标,求输出最短派送路线值(从起点开始完成n个点派送并回到起始点的距离)。
- 代码实现:
- 贪心算法:ShortPath_Greedy,测试用例:ShortPath_GreedyTest
- 回溯法:ShortPath_Recall,测试用例:ShortPath_RecallTest
- 设计思路:
- 贪心算法
- 回溯法
- 注意事项:
6、最佳调度问题
- 题目描述:设有n个任务由k个可并行工作的机器来完成,完成任务i需要时间为taskspendTime。试设计一个算法找出完成这n个任务的最佳调度,使完成全部任务的时间最早
- 代码实现:OptimalSchedule,测试用例:OptimalScheduleTest
- 设计思路:
- 注意事项:
7、最长递增子序列
- 题目描述:在一个给定的数值序列中,找到一个子序列,使得这个子序列元素的数值依次递增,并且这个子序列的长度尽可能地大。最长递增子序列中的元素在原序列中不一定是连续的
- 代码实现:LIS,测试用例:LISTest
- 设计思路:
- 注意事项:
8、最大正方形
- 题目描述:在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积
- 代码实现:
- 8.1、暴力法:LargestSquareViolence,测试用例:LargestSquareViolenceTest
- 8.2、动态规划法:LargestSquareDynamic,测试用例:LargestSquareDynamicTest
- 8.3、动态规划优化:LargestSquareDynamicOpt,测试用例:LargestSquareDynamicOptTest
- 设计思路:
- 8.1、暴力法
- 8.2、动态规划法
- 8.3、动态规划优化
- 注意事项:
9、编辑距离
- 题目描述:
编辑距离指的是在两个单词之间,由其中一个单词转换为另一个单词所需要的最少单字符编辑操作次数。在这里定义的单字符编辑操作有且仅有三种:插入(Insertion)、删除(Deletion)、替换(Substitution)
譬如,“kitten” 和 “sitting” 这两个单词,由 “kitten” 转换为 “sitting” 需要的最少单字符编辑操作有:
1.kitten → sitten (substitution of “s” for “k”)
2.sitten → sittin (substitution of “i” for “e”)
3.sittin → sitting (insertion of “g” at the end)
因此,“kitten” 和 “sitting” 这两个单词之间的编辑距离为 3 。 - 代码实现:EditDistance,测试用例:EditDistanceTest
- 设计思路:
- 状态转换方程:
if (c1[i-1] == c2[j-1]) {
comp[i][j] = comp[i - 1][j - 1];
} else {
comp[i][j] = Math.min(Math.min(comp[i - 1][j - 1], comp[i][j - 1]), comp[i - 1][j]) + 1;
}
- 注意事项:
a、背包问题(backpack)
- 题目描述:
- 代码实现:backpack,测试用例:
- 设计思路:
- 注意事项:
b、合唱团(Choir)
- 题目描述:有 n 个学生站成一排,每个学生有一个能力值,牛牛想从这 n个学生中按照顺序选取 ChoseNum名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 ChoseNum个学生的能力值的乘积最大,你能返回最大的乘积吗?
- 代码实现:
- 动态规划:Choir_Dynamic,测试用例:Choir_DynamicTest
- 回溯法:Choir_Recall,测试用例:Choir_RecallTest
- 设计思路:
- 动态规划:
- 回溯法:
- 注意事项: