校招中遇到的常见算法题总结(持续更新)
主要是相关的题型,原题较少
1、最长公共子序列(leetcode-1143)
经典的二维动态规划问题之一
- 动态规划难点在于如何定义dp,此处为寻找两个字符序列的最长公共子序列,即从头到尾中去最长。故可将dp[i][j]定义为字符串s1,s2的长度为i和j的前缀的最长子序列长度
即dp[i][j]表示s1.substring(0,i)和s1.substring(0,j)的最长子序列长度
-
随后是边界问题
显然,当i=0时,dp[0][j]都为0,同理j=0时,dp[i][j]=0,dp[0][0]=0 -
紧接着就是一般性分析(找i,j逐渐增大时的递推式,显然和最后一个字符相关)
-
当s1.charAt(i)==s2.charAt(j)时,则dp[i][j]等于dp[i-1][j-1]+1
eg: s1: acg; s2:dag dp[3][3]=dp[2][2]+1=1+1=2
-
当s1.chas1.charAt(i)!=s2.charAt(j)时,dp[i][j-1],dp[i-1][j]取其中的较大值
eg: s1: acf; s2:afgd dp[3][3]=Math.max(dp[2][3],dp[3][2])=max(1,2)=2
-
总结
- dp[i][j]=dp[i-1][j-1]+1 when s1.charAt(i)==s2.charAt(j);
- dp[i][j]=Math.max(dp[i][j-1],dp[i-1][j]) when s1.charAt(i)!=s2.charAt(j);
Java代码如下:
public int longestCommonSubsequence(String s1, String s2) {
int n1=s1.length(),
n2=s2.length();
int[][] dp=new int[n1+1][n2+1];
int result=0;
dp[0][0]=0;//边界值
for(int i=1;i<=n1;i++){//边界值
dp[i][0]=0;
}
for(int j=1;j<=n2;j++){//边界值
dp[0][j]=0;
}
for(int k1=1;k1<=n1;k1++){//一般性分析
char c=s1.charAt(k1-1);
for(int k2=1;k2<=n2;k2++){
if(s2.charAt(k2-1)==c){//最后一个字符相等时
dp[k1][k2]=dp[k1-1][k2-1]+1;
}else{//最后一个字符不等时
dp[k1][k2]=Math.max(dp[k1][k2-1],dp[k1-1][k2]);
}
}
}
return dp[n1][n2];
}
2、手撕快速排序(基准值随机选取)
class Solution {
public int[] sortArray(int[] nums) {
quickSort(nums,0,nums.length-1);
return nums;
}
private static void quickSort(int[] nums,int l,int r){
if(l<r){
int privot=partion(nums,l,r);
quickSort(nums,l,privot-1);
quickSort(nums,privot+1,r);
}
}
private static int partion(int[] nums,int l,int r){
int i=new Random().nextInt(r-l+1)+l;//此处随机选择快排基准值的位置
swap(nums,r,i);//将其移动到末尾
return partionCount(nums,l,r);
}
private static int partionCount(int[] nums,int l,int r){
int privot=nums[r];
int i=l-1;
for(int j=l;j<r;j++){
if(nums[j]<=privot){//交换小于基准和大于基准的两个数
i++;
swap(nums,i,j);
}
}
swap(nums,i+1,r);
return i+1;
}
private static void swap(int[] nums,int i,int j){
int tmp=nums[i];
nums[i]=nums[j];
nums[j]=tmp;
}
}