小米公司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];
}