1. 程式人生 > >【面試筆試-c/c++】人民搜尋2012校園招聘試題

【面試筆試-c/c++】人民搜尋2012校園招聘試題

2012 人民搜尋筆試題:

題目如下(題目都很基礎,但是要拿滿分,或者做到完美,應該還是有一定難度的):

1、列印漢諾塔移動步驟,並且計算複雜度。

2、計算兩個字串的是否相似(字元的種類,和出現次數相同)

3、定義二叉樹,節點值為int,計算二叉樹中的值在[a,b]區間的節點的個數。

4. 在一個座標軸上, 給定兩個點,一個起點,一個終點,起點有一個方塊,方塊可以左右移動,但是移動的長度只能是平方數長(1,4,9,16....) ,
    同時座標軸上還有洞,移動的過程中不能越過這個洞,不然會掉下去,問 由起點到終點 至少需要多少次移動,不能到達返回-1

5、給一個整數陣列,求陣列中重複出現次數大於陣列總個數一半的數。

6、一個128bits 的二進位制流,要求找出 裡面包含 某8bits 二進位制流的數目。
現在一個一個進行分析總結:

1、列印漢諾塔移動步驟,並且計算複雜度。

很經典的遞迴問題。遞迴的一個基本思想是:假設前一步已經得到結果,則下一步的運算(或步驟)是基於上一步驟的結果的。對於遞迴問題,有一點很值得注意的是:“一定要有退出遞迴的出口”,否則程式會無限遞迴下去,最終導致棧溢位。

廢話少說,直接上程式碼:

#include <stdio.h>

//將編號為n的盤子移動從x移動到y 
void move(char x,int n,char y){
     printf("%d號盤子 :%c -> %c\n",n,x,y);     
} 

/*漢諾塔的執行過程,注意n==1是遞迴退出的條件,這是必要的,沒有這個的話會無限遞迴而出錯誤。
 *引數:n==>總盤子數,x==>底座1,y==>底座2(輔助底座),z==>目標底座。
 */
 void hanoi(int n,char x,char y,char z){
     if(n == 1){
          move(x,1,z); 
     } 
     else{
          hanoi(n-1,x,z,y);
          move(x,n,z);
          hanoi(n-1,y,x,z);     
     }      
} 

main(){
    char x = 'x',y = 'y',z = 'z';          
    hanoi(10,x,y,z);
    return 0;   
} 
對於這類遞迴問題的複雜度(時間)分析(方法有遞推式,數學歸納法等):

假設規模為n的問題的時間複雜度為T(n),則有

T(n) = T(n-1)+O(1)+T(n-1);

T(1) = O(1);

即T(n) = 2(T(n-1))+O(1)也就是T(n)+1 = 2((T(n-1) +O(1) )

對n依次替換,則結果為:

T(n)+1 =2(T(n-1)+1) = 2^2(T(n-2)+1) = .......2^n(T(1)+1) ==>O(2^n)

2、計算兩個字串的是否相似(字元的種類,和出現次數相同)

之前有同學給出的思路是:類似字串計數。比較每個字元出現的次數,然後根據是否相同來比較。但顯然,本題目可以有更加簡潔的方法,看過百度那道“兄弟單詞”的應該記得:這個跟那題十分類似。因此我們的思路是:對於給定的字串,我們先按照字母表中的順序對字串進行排序。如果兩個字串相似,那麼他們必須有相同的簽名(排序後的字串),因此我們可以簡單的通過strcmp進行比較。

對於本思路,程式碼實現如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int cmp(const void *a,const void *b){
	return (int*)a-(int*)b;
}

int isSimple(char *s,char *t ){
	qsort(s,strlen(s),sizeof(char *),cmp);
	qsort(t,strlen(t),sizeof(char *),cmp);
	return !strcmp(s,t);
}
main(){
	char s[] = "abcdefg";
	char t[] = "acbdefg";
	printf("the result if :%s\n",isSimple(s,t)?"simple":"not simple");

}

3、定義二叉樹,節點值為int,計算二叉樹中的值在[a,b]區間的節點的個數。

對於本題目。如果只是一個普通的二叉樹,那麼思路很簡單。就是簡單的遍歷(前中後序甚至層次均可) ==> 計數。以前序為例,程式碼很簡潔:

首先我們定義的二叉樹的資料結構如下:

typedef struct BTreeNode{
     BTreeNode* lchild;
     BTreeNode* rchild;
     int value;

}BTreeNode,*Btree;

那麼求位於區間元素個數的關鍵就在於preOrder:

#include <stdio.h>
#include <malloc.h>
#define LEAF -1

typedef struct BTreeNode{
     BTreeNode* lchild;
     BTreeNode* rchild;
     int value;

}BTreeNode,*Btree;

BTreeNode* createTree(){
     BTreeNode* T;
     int t;
     scanf("%d",&t);
     if(t == LEAF){
          T = NULL;
     }else{
          T = (BTreeNode *) malloc (sizeof(BTreeNode));
          T->value = t;
          T->lchild = createTree();
          T->rchild = createTree();
     }
     return T;
}

//對位於【a,b】區間的元素個數進行統計,為了不用全域性變數。count為引用傳遞。
void preOrder(BTreeNode* root,int a,int b,int &count){
     if(root != NULL){
     	 if(root->value >= a && root->value <= b){
			count++;
		 }
     }
     if(root->lchild != NULL){
         preOrder(root->lchild,a,b,count);
     }
     if(root->rchild !=NULL){
         preOrder(root->rchild,a,b,count);
     }
}

main(){
    BTreeNode * root;
    root = createTree();
    int a = 3,b = 8,count = 0;
	preOrder(root,a,b,count);
	printf("%d\n",count);
    return 0;
}

另一方面,線段樹可以維護一個數組的各個區間(子陣列)狀態。所以本題也可以用線段樹解決。需要注意的是,線段樹並非對所有的區間查詢都有較好的效能(例如對於由0-10這些數字組成的陣列構成的線段樹,查詢區間2-9就是一個比較詭異的查詢,需要做相關處理【a,b】=>[a,(a+b)/2],[(a+b)/2,b]兩個區間)。所以這裡並不給出線段樹的解決方案。

4、一條路有k可坑,每次能跳平方數步長(1 4 9 16。。),不能跳到坑裡,從a跳到b最少幾步?

我們先看看,如果不考慮坑的情況,從a跳到b最少幾步?(初步估算,最多不超過三步,具體的證明暫無)

遞迴轉移方程?

有坑的情況下,演算法如何修改?

//TODO

5、給一個整數陣列,求陣列中重複出現次數大於陣列總個數一半的數。

老生常談的題目,這裡不再贅述,僅給出程式碼:

int findMoreHalf(int a[],int N){
    int theNum = a[0];
    int times = 1,i;
    for(i = 1;i < N;i++){
       if(times == 0){
           theNum = a[i];
           times = 1;
       }
       else{
           if(a[i] == theNum){
               times ++;
           }else{
               times --;
           }
       }
    }
    return theNum;
}

6、一個128bits 的二進位制流,要求找出 裡面包含 某8bits 二進位制流的數目。

a.kmp演算法。

kmp 是普通字串匹配演算法BF演算法的改進演算法,其主要優點是在匹配失敗時,不需要對主串進行回溯,因而大大提高了匹配的質量。

有關kmp演算法的具體細節,可以參考: http://blog.csdn.net/v_july_v/article/details/7041827

由於kmp演算法只是“匹配”,因此,要統計數目的話,需要多次呼叫kmp_search.達到統計的目的。

完整實現的程式碼如下(這裡假設用字串代替位元位):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void get_nextval(char const* pattenStr, int plen, int* nextval){
    int i = 0;
    nextval[i] = -1;
    int j = -1;
    while( i < plen-1 ){
        if( j == -1 || pattenStr[i] == pattenStr[j] ){
            ++i;
            ++j;
            if( pattenStr[i] != pattenStr[j] ){
				nextval[i] = j;
			}
            else{
				 nextval[i] = nextval[j];
			}
        }
        else{
			 j = nextval[j];
		}

    }
}

int index_kmp(char const* src, int slen, char const* ptn, int plen, int const* nextval, int pos){
    int i = pos;
    int j = 0;
    while ( i < slen && j < plen ){
        if( j == -1 || src[i] == ptn[j] ){
            ++i;
            ++j;
        }
        else{
            j = nextval[j];
        }
    }
    if( j >= plen )
        return i-plen;
    else
        return -1;
}

int getMatchCount(char const* src, int slen, char const* ptn, int plen,int const* nextval){
	int start = 0,index = 0,count = 0;
	while((index = index_kmp(src,slen,ptn,plen,nextval,start))!=-1 && index<slen ){
		count++;
		start = index+1;
	}
	return count;
}

main(){
	char s[] = "10101010101111011100001010101010111111110001100110101010101011010000111111111100000001111111111100001111111100000000111100001111";
	char p[] = "11111111";
	int *nextval = new int[strlen(p)];
	get_nextval(p,strlen(p),nextval);

	printf("%d ",getMatchCount(s,strlen(s),p,strlen(p),nextval));
}

b.AC自動機。

AC自動機的原理是在樹(trie樹)上做kmp搜尋。對於本題目,8個bit位的串。需要建立一個9層的trie數(注意trie樹的根是不儲存元素的),然後進行搜尋。命中有出口,計數,否則丟棄。

相應程式碼稍後補上

// TODO