1. 程式人生 > >小米公司Android工程師社招面試(2016/08/03)

小米公司Android工程師社招面試(2016/08/03)

我在2016年8月3日下午3點半~6點半進行了三輪面試,除了自己做過的專案,也問了一些通用的問題。
當時本人能回答的包括:
 AsyncTask和Handler+Thread機制的區別;
 雜湊表原理
 紅黑樹(由Java 8中HashMap的新特性引申)
 Int型別整數二進位制1的個數(演算法題,手寫)
 樓梯走法次數(演算法題,手寫)
 ……
當時不會的包括:
 Java中final修飾成員函式的作用
 Java中執行緒池如何設計的(由AsyncTask引申出來的題目,參見“Java執行緒池原理與例項詳解”)
 自定義控制元件如何設計,例如顯示gif圖片
 ……

一面演算法題

題目:Int型別整數二進位制1的個數,例如3的二進位制是11,1的個數是2。
【分析】
方法一:判斷最後一位是否是1,然後不斷右移該整數,繼續判斷,直到該數為0。這個演算法對於正數有效,但是對於負數無效。因為右移時最左側會補符號位,正數就是補0,而負數就是補1。不過,由於java中有右移補0的符號>>>,所以,該演算法可行。

public static int bitCounter2( int n){
        int c =0 ; // 計數器
        while (n !=0)
        {
            if((n &1
) ==1) // 當前位是1 ++c ; // 計數器加1 n >>>=1 ; // 移位,左側補0,n改變了! } return c ; }

方法二:向左移動掩碼1,然後進行位運算“&”,就能判斷該位是否是1。本方法還能保證被判斷數不變。

    public static int bitCounter(int n){
        int mask=1;
        int counter=0;
        while ((n&mask)!=0) {
            counter++;
            mask<<=1
; } return counter++; }

這是我面試的回答,當然,回來一查,“演算法-求二進位制數中1的個數”中有更多好的方法。

二面演算法題

題目:一個人邁步上臺階,一次可以邁1階、2階、3階,這樣,到1階的走法有1中,到2階的走法有2中,到3階的走法有4種,那麼到第n級的走法有多少種?
【分析】走到n階前,可能位於n-1階,再邁1步1階即可;可能為n-2階,再邁1步2階即可;可能位於n-3階,再邁1步3階即可。遞迴公式:f(n)= f(n-1)+ f(n-1)+ f(n-2)。
解法一:遞迴

public static int stepCounter(int n){
        if (n<=0) throw new IllegalArgumentException("引數錯誤,n必須大於0!");
        if (n==1) return 1;
        if (n==2) return 2;
        if (n==3) return 4;
        return stepCounter(n-1)+stepCounter(n-2)+stepCounter(n-3);
    }

解法二:空間換時間(類似動態規劃)

public static int stepCounter2(int n){
        if (n<=0) throw new IllegalArgumentException("引數錯誤,n必須大於0!");

        int[] counter=new int[n+1];
        counter[1]=1;
        counter[2]=2;
        counter[3]=4;
        for (int i = 4; i < n+1; i++) {
            counter[i]=counter[i-1]+counter[i-2]+counter[i-3];
        }
        return counter[n];
    }

然後面試官問我是否可以優化,我說空間可以再小一點,只記錄最近的3次。

public static int stepCounter2(int n){
        if (n<=0) throw new IllegalArgumentException("引數錯誤,n必須大於0!");

        int[] counter=new int[4];
        counter[1]=1;
        counter[2]=2;
        counter[3]=4;
        for (int i = 4; i < n+1; i++) {
            //counter[0]用來儲存結果
            counter[0]=counter[1]+counter[2]+counter[3];
            //移動前三個數
            counter[1]=counter[2];
            counter[2]=counter[3];
            counter[3]=counter[0];
        }
        return counter[3];
    }

寫這篇部落格時我想,其實最後三個數的交換也是沒有必要的,只要記住返回值的位置就OK。

    public static int stepCounter3(int n){
        if (n<=0) throw new IllegalArgumentException("引數錯誤,n必須大於0!");

        int[] counter=new int[4];
        int j=0;//返回值的位置
        for (int i = 1; i < n+1; i++) {
            j=i%4;
            if (i==1) {

                counter[j]=1;
            }else if (i==2) {

                counter[j]=2;
            }else if (i==3) {

                counter[j]=4;
            }else {
                counter[j]=0;//清0
                for (int k = 0; k < 4; k++) {
                    if (k==j) continue;
                    counter[j]+=counter[k];//其它三個數之和
                }
            }
        }
        return counter[j];
    }