常見算法面試題


校招中遇到的常見算法題總結(持續更新)

主要是相關的題型,原題較少

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;
}
}

3、K個一組翻轉鏈表(leetcode-25

主要問題可概括如下:

  • 翻轉K個節點的鏈表(翻轉鏈表應該都做過,原理一樣)
  • 反轉后的首尾鏈表和原鏈表首尾的拼接(保存前一個結點和后一個節點;返回節點為翻轉后的第一個和最后一個節點,即可拼接)
  • 第一個節點無法保存第一個節點(要么特設,要么添加無實際作用的頭節點)
  • 最后少於k個節點時不需要翻轉(因為新增頭節點的緣故,直接返回頭節點.next即可)

代碼如下:

class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
	ListNode headPre=new ListNode(0);//添加頭節點
	headPre.next=head;
	ListNode pre=headPre;
	while(head!=null){
		ListNode tail=pre;
		for(int i=0;i<k;i++){
			tail=tail.next;
			if(tail==null){
				return headPre.next;//最后少於k個節點可以直接返回當前鏈表
			}
		}
		ListNode nxt=tail.next;
		ListNode[] l=reverseKList(head,tail);
		head=l[0];
		tail=l[1];
		pre.next=head;//拼接原鏈表
		tail.next=nxt;//拼接原鏈表
		head=nxt;
		pre=tail;
	}
	return headPre.next;
}
public static ListNode[] reverseKList(ListNode head,ListNode tail){//翻轉鏈表操作(通常情況翻轉整條鏈表時tail.next為null)
	ListNode prev=tail.next;
	ListNode h=head;
	ListNode t=tail.next;
	while(h!=t){
		ListNode nex=h.next;
		h.next=prev;
		prev=h;
		h=nex;
	}
	return new ListNode[]{tail,head};
}

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM