1. 程式人生 > >百度2011 10 16校園招聘會筆試題

百度2011 10 16校園招聘會筆試題

               

 一、演算法設計1、設rand(s,t)返回[s,t]之間的隨機小數,利用該函式在一個半徑為R的圓內找隨機n個點,並給出時間複雜度分析。

思路:這個使用數學中的極座標來解決,先呼叫[s1,t1]隨機產生一個數r,歸一化後乘以半徑,得到R*(r-s1)/(t1-s1),然後在呼叫[s2,t2]隨機產生一個數a,歸一化後得到角度:360*(a-s2)/(t2-s2)

2、為分析使用者行為,系統常需儲存使用者的一些query,但因query非常多,故系統不能全存,設系統每天只存m個query,現設計一個演算法,對使用者請求的query進行隨機選擇m個,請給一個方案,使得每個query被抽中的概率相等,並分析之,注意:不到最後一刻,並不知使用者的總請求量。

思路:如果使用者查詢的數量小於m,那麼直接就存起來。如果使用者查詢的數量大於m,假設為m+i,那麼在1-----m+i之間隨機產生一個數,如果選擇的是前面m條查詢進行存取,那麼概率為m/(m+i),如果選擇的是後面i條記錄中的查詢,那麼用這個記錄來替換前面m條查詢記錄的概率為m/(m+i)*(1-1/m)=(m-1)/(m+i),當查詢記錄量很大的時候,m/(m+i)== (m-1)/(m+i),所以每個query被抽中的概率是相等的。

3、C++ STL中vector的相關問題:    (1)、呼叫push_back時,其內部的記憶體分配是如何進行的?    (2)、呼叫clear時,內部是如何具體實現的?若想將其記憶體釋放,該如何操作?

vector的工作原理是系統預先分配一塊CAPACITY大小的空間,當插入的資料超過這個空間的時候,這塊空間會讓某種方式擴充套件,但是你刪除資料的時候,它卻不會縮小。  vector為了防止大量分配連續記憶體的開銷,保持一塊預設的尺寸的記憶體,clear只是清資料了,未清記憶體,因為vector的capacity容量未變化,系統維護一個的預設值。

有什麼方法可以釋放掉vector中佔用的全部記憶體呢?

標準的解決方法如下template < class T >void ClearVector( vector< T >& vt ){vector< T > vtTemp;veTemp.swap( vt );}

  事實上,vector根本就不管記憶體,它只是負責向記憶體管理框架acquire/release記憶體,記憶體管理框架如果發現記憶體不夠了,就malloc,但是當vector釋放資源的時候(比如destruct), stl根本就不呼叫free以減少記憶體,因為記憶體分配在stl的底層:stl假定如果你需要更多的資源就代表你以後也可能需要這麼多資源(你的list, hashmap也是用這些記憶體),所以就沒必要不停地malloc/free。如果是這個邏輯的話這可能是個trade-off

  一般的STL記憶體管理器allocator都是用記憶體池來管理記憶體的,所以某個容器申請記憶體或釋放記憶體都只是影響到記憶體池的剩餘記憶體量,而不是真的把記憶體歸還給系統。這樣做一是為了避免記憶體碎片,二是提高了記憶體申請和釋放的效率——不用每次都在系統記憶體裡尋找一番。

二、系統設計正常使用者端每分鐘最多發一個請求至服務端,服務端需做一個異常客戶端行為的過濾系統,設伺服器在某一刻收到客戶端A的一個請求,則1分鐘內的客戶端任何其它請求都需要被過濾,現知每一客戶端都有一個IPv6地址可作為其ID,客戶端個數太多,以至於無法全部放到單臺伺服器的記憶體hash表中,現需簡單設計一個系統,使用支援高效的過濾,可使用多臺機器,但要求使用的機器越少越好,請將關鍵的設計和思想用圖表和程式碼表現出來。

三、求一個全排列函式:如p([1,2,3])輸出:[123]、[132]、[213]、[231]、[321]、[312] 求一個組合函式。

方法1:依次從字串中取出一個字元作為最終排列的第一個字元,對剩餘字元組成的字串生成全排列,最終結果為取出的字元和剩餘子串全排列的組合。

#include <iostream>#include <string>using namespace stdvoid permute1(string prefix, string str){    if(str.length() == 0)        cout << prefix << endl;    else    {        for(int i = 0; i < str.length(); i++)            permute1(prefix+str[i], str.substr(0,i)+str.substr(i+1,str.length()));    }} void permute1(string s){    permute1("",s);} int main(void){    //method1, unable to remove duplicate permutations.    permute1("abc"); return 0;}

優點:該方法易於理解,但無法移除重複的排列,如:s="ABA",會生成兩個“AAB”。

方法2:利用交換的思想,具體見例項,但該方法不如方法1容易理解。

我們以三個字元abc為例來分析一下求字串排列的過程。首先我們固定第一個字元a,求後面兩個字元bc的排列。當兩個字元bc的排列求好之後,我們把第一個字元a和後面的b交換,得到bac,接著我們固定第一個字元b,求後面兩個字元ac的排列。現在是把c放到第一位置的時候了。記住前面我們已經把原先的第一個字元a和後面的b做了交換,為了保證這次c仍然是和原先處在第一位置的a交換,我們在拿c和第一個字元交換之前,先要把b和a交換回來。在交換b和a之後,再拿c和處在第一位置的a進行交換,得到cba。我們再次固定第一個字元c,求後面兩個字元b、a的排列。

既然我們已經知道怎麼求三個字元的排列,那麼固定第一個字元之後求後面兩個字元的排列,就是典型的遞迴思路了。

基於前面的分析,我們可以得到如下的參考程式碼:

void Permutation(char* pStr, char* pBegin){ assert(pStr && pBegin); //if(!pStr || !pBegin)  //return ; if(*pBegin == '\0')  printf("%s\n",pStr); else {  char temp;  for(char* pCh = pBegin; *pCh != '\0'; pCh++)  {   if(pCh != pBegin && *pCh == *pBegin)   //為避免生成重複排列,當不同位置的字元相同時不再交換    continue;   temp = *pCh;   *pCh = *pBegin;   *pBegin = temp;   Permutation(pStr, pBegin+1);   temp = *pCh;   *pCh = *pBegin;   *pBegin = temp;  } }}int main(void)char str[] = "aba"; Permutation(str,str); return 0;}

如p([1,2,3])輸出:[1]、[2]、[3]、[1,2]、[2,3]、[1,3]、[1,2,3]這兩問可以用虛擬碼。