面試題

微軟面試題:地球上有多少個滿足這樣條件的點

站在地球上的某一點,向南走一公里,然後向東走一公里,最後向北走一公里,回到了原點。地球上有多少個滿足這樣條件的點?

北極點滿足這個條件。

距離南極點很近的一個圈上也滿足這個條件。在這個圓圈上,向南走一公里,然後向東走一公里恰好繞南極點一圈,向北走一公里回到原點。

所以地球上總共有無數點滿足這個條件。

谷歌面試題:判斷一個自然數是否是某個數的平方

判斷一個自然數是否是某個數的平方。當然不能使用開方運算。

假設待判斷的數字是 N。

 

方法1:

遍歷從1到N的數字,求取平方並和N進行比較。

如果平方小於N,則繼續遍歷;如果等於N,則成功退出;如果大於N,則失敗退出。

複雜度爲O(n^0.5)。

 

方法2:

使用二分查找法,對1到N之間的數字進行判斷。

複雜度爲O(log n)。

 

方法3:

由於

(n+1)^2

=n^2 + 2n + 1,

= ...

= 1 + (2*1 + 1) + (2*2 + 1) + ... + (2*n + 1)

注意到這些項構成了等差數列(每項之間相差2)。

所以我們可以比較 N-1, N - 1 - 3, N - 1 - 3 - 5 ... 和0的關係。

如果大於0,則繼續減;如果等於0,則成功退出;如果小於 0,則失敗退出。

複雜度爲O(n^0.5)。不過方法3中利用加減法替換掉了方法1中的乘法,所以速度會更快些。

 

谷歌面試題:如何隨機選取1000個關鍵字

給定一個數據流,其中包含無窮盡的搜索關鍵字(比如,人們在谷歌搜索時不斷輸入的關鍵字)。如何才能從這個無窮盡的流中隨機的選取1000個關鍵字?

定義長度爲1000的數組。

對於數據流中的前1000個關鍵字,顯然都要放到數組中。

對於數據流中的的第n(n>1000)個關鍵字,我們知道這個關鍵字被隨機選中的概率爲 1000/n。所以我們以 1000/n 的概率用這個關鍵字去替換數組中的隨機一個。這樣就可以保證所有關鍵字都以 1000/n的概率被選中。

對於後面的關鍵字都進行這樣的處理,這樣我們就可以保證數組中總是保存着1000個隨機關鍵字。

 

谷歌面試題:將下列表達式按照複雜度排序

將下列表達式按照複雜度排序

 

2^n

n^Googol (其中 Googol = 10^100)

n!

n^n

答:

按照複雜度從低到高爲

n^Googol

2^n

n!

n^n

 

微軟面試題:正確標註水果籃

有三個水果籃。其中一個裏面只有蘋果,一個裏面只有橘子,另外一個既有蘋果又有橘子。每個水果籃上都有標籤,但標籤都是錯的。如何檢查某個水果籃中的一個水果,然後正確標註每個水果籃?

從標註成既有蘋果也有橘子的水果籃中選取一個進行檢查。

如果是橘子,則此籃中只有橘子;標有橘子的水果籃中只有蘋果;標有蘋果的水果籃中既有蘋果也有橘子。

如果是蘋果,則此籃中只有蘋果;標有蘋果的水果籃中只有橘子;標有橘子的水果籃中既有蘋果也有橘子。

 

IBM面試題:爲什麼小和尚會在同一時間出現在同一地點

有一座山,山上有座廟,只有一條路可以從山上的廟到山腳,每週一早上8點,有一個聰明的小和尚去山下化緣,週二早上8點從山腳回山上的廟裏,小和尚的上下山的速度是任意的,在每個往返中,他總是能在週一和週二的同一鐘點到達山路上的同一點。例如,有一次他發現星期一的8點30和星期二的8點30他都到了山路靠山腳的3/4的地方,問這是爲什麼?

可以用畫圖法來解釋:

在一個平面上,x 軸代表從8點開始的時間,y 軸代表距廟的距離。那麼從廟到山腳就是一條從左下到右上的一條曲線,從山腳到廟就是一條從左上到右下的一條曲線。考慮到兩條曲線的起始點和終點,兩線必定交於一點。

 

微軟面試題:不利用浮點運算,畫一個圓

不利用浮點運算,在屏幕上畫一個圓 (x**2 + y**2 = r**2,其中 r 爲正整數)。

考慮到圓的對稱性,我們只需考慮第一象限即可。

等價於找到一條連接點(0,r)到點(r,0)的一條曲線,曲線上的點距圓心(0,0)的距離最接近 r。

我們可以從點(0,r)開始,搜索右(1,r),下(0,r-1),右下(1,r-1)三個點到圓心的距離,選擇距圓心距離最接近 r 的點作爲下一個點。反覆進行這種運算,直至到達點(r,0)。

由於不能利用浮點運算,所以距離的比較只能在距離平方的基礎上進行。也就是比較 x**2 + y**2 和 r**2之間的差值。

 

微軟面試題:計算n bit的整數中有多少bit 爲1

設此整數爲x。

 

方法1:

讓此整數除以2,如果餘數爲1,說明最後一位是1,統計值加1。

將除得的結果進行上面運算,直到結果爲0。

 

方法2:

考慮除法複雜度有些高,可以使用移位操作代替除法。

將 x 和 1 進行按位與操作(x&1),如果結果爲1,說明最後一位是1,統計值加1。

將x 向右一位(x >> 1),重複上面過程,直到移位後結果爲0。

 

方法3:

如果需要統計很多數字,並且內存足夠大,可以考慮將每個數對應的bit爲1的數量記錄下來,這樣每次計算只是一次查找操作。

 

微軟面試題:快速求取一個整數的7倍

乘法相對比較慢,所以快速的方法就是將這個乘法轉換成加減法和移位操作。

可以將此整數先左移三位(×8)然後再減去原值:X << 3 - X。

 

微軟面試題:判斷一個數是不是2的n次冪

設要判斷的數是無符號整數X。

首先判斷X是否爲0,如果爲0則不是2的n次冪,返回。

X和X-1進行按位與操作,如果結果是0,則說明這個數是2的n次冪;如果結果非0,則說明這個數不是2 的n次冪。

 

證明:

如果是2的n次冪,則此數用二進制表示時只有一位是1,其它都是0。減1後,此位變成0,後面的位變成1,所以按位與後結果是0。

如果不是2的n次冪,則此數用二進制表示時有多位是1。減1後,只有最後一個1變成0,前面的 1還是1,所以按位與後結果不是0。

微軟面試題:三隻螞蟻不相撞的概率是多少

在三角形的三個頂點上各有一隻螞蟻,它們向另一個頂點運動,目標隨機(可能爲另外兩個頂點的任意一個)。問三隻螞蟻不相撞的概率是多少?

如果螞蟻順時針爬行記爲0,逆時針爬行記爲1。那麼三隻螞蟻的狀態可能爲000,001,...,110,111中的任意一個,且爲每種狀態的概率相等。在這8種狀態中,只有000和111可以避免相撞,所以螞蟻不相撞的概率是1/4。

 

題目的一些擴展:

1. 如果將三角形變成正方形,並且有四隻螞蟻,那麼不相撞的概率是多少?(1/8)

2. 如果將正方形的對角線相連,螞蟻也可以在對角線上爬行,那麼不相撞的概率是多少?(假設對角線相交處有立交橋)(暫無答案)

擴展2的答案好像是 6/ 81。

由於每隻螞蟻都可以向其它三個頂點移動,所以總的可能情況爲3*3*3*3=81。

所有螞蟻順時針或者逆時針可以避免相撞,這有兩種情況。

如果只有一隻螞蟻順着對角線移動,是無法避免相撞的。

兩隻螞蟻順着對角線移動可以避免相撞,總共有4種情況。

所以總的避免相撞的情況有6 種。

題目還可以進一步擴展:

將正方形換成正n邊形,每兩個頂點都相互鏈接。(暫無答案)

 

微軟面試題:判斷數組中是否包含重複數字

給定一個長度爲N的數組,其中每個元素的取值範圍都是1到N。判斷數組中是否有重複的數字。(原數組不必保留)

 

方法1.

對數組進行排序(快速,堆),然後比較相鄰的元素是否相同。

時間複雜度爲O(nlogn),空間複雜度爲O(1)。

 

方法2.

使用bitmap方法。

定義長度爲N/8的char數組,每個bit表示對應數字是否出現過。遍歷數組,使用 bitmap對數字是否出現進行統計。

時間複雜度爲O(n),空間複雜度爲O(n)。

 

方法3.

遍歷數組,假設第 i 個位置的數字爲 j ,則通過交換將 j 換到下標爲 j 的位置上。直到所有數字都出現在自己對應的下標處,或發生了衝突。

時間複雜度爲O(n),空間複雜度爲O(1)。

 

微軟面試題:如何將蛋糕切成相等的兩份

一塊長方形的蛋糕,其中有一個小長方形的空洞(角度任意)。使用一把直刀,如何一刀將蛋糕切成相等的兩份?

通過長方形中心的的任意直線都能將長方形等分,所以連接兩個長方形的中心點的直線可以等分這個蛋糕。

 

谷歌面試題:在半徑爲1的圓中隨機選取一點

在半徑爲1的圓中隨機選取一點。

假設圓心所在位置爲座標元點(0, 0)。

 

方法1.

在x軸[-1, 1],y軸[-1, 1]的正方形內隨機選取一點。然後判斷此點是否在圓內(通過計算此點到圓心的距離)。如果在圓內,則此點即爲所求;如果不在,則重新選取直到找到爲止。

正方形的面積爲4,圓的面積爲pi,所以正方形內的隨機點在圓內的概率是 pi / 4。

 

方法2.

從[0, 2*pi)中隨機選一個角度,對應於圓中的一條半徑,然後在此半徑上選一個點。但半徑上的點不能均勻選取,選取的概率應該和距圓心的長度成正比,這樣才能保證隨機點在圓內是均勻分佈的。

 

谷歌面試題:給定一個未知長度的整數流,如何隨機選取一個數

給定一個未知長度的整數流,如何隨機選取一個數?

方法1.

將整個整數流保存到一個數組中,然後再隨機選取。

如果整數流很長,無法保存下來,則此方法不能使用。

 

方法2.

如果整數流在第一個數後結束,則我們必定會選第一個數作爲隨機數。

如果整數流在第二個數後結束,我們選第二個數的概率爲1/2。我們以1/2的概率用第2個數替換前面選的隨機數,得到滿足條件的新隨機數。

....

如果整數流在第n個數後結束,我們選第n個數的概率爲1/n。我們以1/n的概率用第n個數替換前面選的隨機數,得到滿足條件的新隨機數。

....

利用這種方法,我們只需保存一個隨機數,和迄今整數流的長度即可。所以可以處理任意長的整數流。

 

谷歌面試題:設計方便提取中數的數據結構

設計一個數據結構,其中包含兩個函數,1.插入一個數字,2.獲得中數。並估計時間複雜度。

1. 使用數組存儲。

插入數字時,在O(1)時間內將該數字插入到數組最後。

獲取中數時,在O(n)時間內找到中數。(選數組的第一個數和其它數比較,並根據比較結果的大小分成兩組,那麼我們可以確定中數在哪組中。然後對那一組按照同樣的方法進一步細分,直到找到中數。)

 

2. 使用排序數組存儲。

插入數字時,在O(logn)時間內找到要插入的位置,在O(n)時間裏移動元素並將新數字插入到合適的位置。

獲得中數時,在O(1)複雜度內找到中數。

 

3. 使用大根堆和小根堆存儲。

使用大根堆存儲較小的一半數字,使用小根堆存儲較大的一半數字。

插入數字時,在O(logn)時間內將該數字插入到對應的堆當中,並適當移動根節點以保持兩個堆數字相等(或相差1)。

獲取中數時,在O(1)時間內找到中數。

 

谷歌面試題:在一個特殊數組中進行查找

給定一個固定長度的數組,將遞增整數序列寫入這個數組。當寫到數組尾部時,返回數組開始重新寫,並覆蓋先前寫過的數。

請在這個特殊數組中找出給定的整數。

假設數組爲a[0, 1, ..., N-1]。

我們可以採用類似二分查找的策略。

首先比較a[0]和a[N/2],如果a[0] < a[N/2],則說明a[0,1,...,N/2]爲遞增子序列,否則另一部分是遞增子序列。

然後判斷要找的整數是否在遞增子序列範圍內。如果在,則使用普通的二分查找方法繼續查找;如果不在,則重複上面的查找過程,直到找到或者失敗爲止。

谷歌面試題:給定兩個已排序序列,找出共同的元素

不妨假設序列是從小到大排序的。定義兩個指針分別指向序列的開始。

如果指向的兩個元素相等,則找到一個相同的元素;如果不等,則將指向較小元素的指針向前移動。

重複執行上面的步驟,直到有一個指針指向序列尾端。

 

谷歌面試題:找到鏈表的倒數第m個節點

方法1:

首先遍歷鏈表,統計鏈表的長度N。

然後再次遍歷鏈表,找到第N-m個節點,即爲倒數第m個節點。

 

方法2:

使用兩個指針,並使它們指向的節點相距m-1個。

然後同時向前移動兩個指針,當一個指針指最後一個節點時,第二個指針指向倒數第m個節點。

 

兩個方法的複雜度都是O(n)。

但是當N較大而m較小時,方法2可能會更快一些。因爲方法2能更好利用CPU的緩存。

 

更多閱讀:

http://baike.baidu.com/view/2089.htm CPU -> 緩存

 

谷歌面試題:給定一個排序數組,如何構造一個二叉排序樹?

採用遞歸算法。

選取數組中間的一個元素作爲根節點,左邊的元素構造左子樹,右邊的節點構造有子樹。

 

谷歌面試題:數組中是否有兩個數的和爲10

1.

比較任意兩個數的和是否爲10。如

for (int i = 0; i < n; ++i) { for (int j = i+1; j < n; ++j) { .... }}

複雜度爲O(n*n)。

 

2.

將數組排序後,對每個數m,使用二分查找在數組中尋找10-m。

複雜度爲O(nlogn)。

 

3.

將數組存儲到hash_set中去,對每個數m,在hash_set中尋找10-m。

複雜度爲O(n)。

 

4.

如果數組很大,超過內存的容量,可以按照hash(max(m, 10-m))%g,將數據分到g個小的group中。然後對每個小的group進行單獨處理。

複雜度爲O(n)。

 

百度面試題:三個警察和三個囚徒的過河問題

三個警察和三個囚徒共同旅行。一條河擋住了去路,河邊有一條船,但是每次只能載2人。存在如下的危險:無論在河的哪邊,當囚徒人數多於警察的人數時,將有警察被囚徒殺死。

問題:請問如何確定渡河方案,才能保證6人安全無損的過河。

警察囚徒過去,警察回來

囚徒囚徒過去,囚徒回來

警察警察過去,警察囚徒回來

警察警察過去,囚徒回來

囚徒囚徒過去,囚徒回來

囚徒囚徒過去

 

雅虎面試題:HTTP中Get和Post的區別

 

Get和Post都是瀏覽器向網頁服務器提交數據的方法。

 

Get把要提交的數據編碼在url中,比如 http://hi.baidu.com/mianshiti?key1=value1&key2=value2 中就編碼了鍵值對 key1,value1 和key2,value2。受限於url的長度限制,Get方法能傳輸的數據有限(不同瀏覽器對url長度限制不同,比如微軟IE設爲2048)。

Post把要提交的數據放在請求的body中,而不會顯示在url中,因此,也沒有數據大小的限制。

 

由於Get把數據編碼在URL中,所以這些變量顯示在瀏覽器的地址欄,也會被記錄在服務器端的日誌中。所以Post方法更加安全。

 

雅虎面試題:燈炮能亮的概率是多少?

一個開關接一個燈炮,開關一年內壞掉的概率爲10%,燈炮一年內壞掉的概率爲20%,問一年後打開開關,燈炮能亮的概率是多少?(假定其他設備都不損壞)

開關好的概率是90%,燈泡好的概率是80%,

所以兩個都好的概率是90%*80%=72%。

 

華爲面試題:IP,TCP和UDP協議的定義和主要作用

 

IP協議是網絡層的協議。IP協議規定每個互聯網網上的電腦都有一個唯一的IP地址,這樣數據包就可以通過路由器的轉發到達指定的電腦。但IP協議並不保證數據傳輸的可靠性。

TCP協議是傳輸層的協議。它向下屏蔽了IP協議不能可靠傳輸的缺點,向上提供面向連接的可靠的數據傳輸。

UDP協議也是傳輸層的協議。它提供無連接的不可靠傳輸。

 

華爲面試題:全局變量和局部變量有什麼區別?

全局變量是整個程序都可訪問的變量,生存期從程序開始到程序結束;局部變量存在於模塊中(比如某個函數),只有在模塊中纔可以訪問,生存期從模塊開始到模塊結束。

全局變量分配在全局數據段,在程序開始運行的時候被加載。局部變量則分配在程序的堆棧中。因此,操作系統和編譯器可以通過內存分配的位置來知道來區分全局變量和局部變量。

 

華爲面試題:析構函數和虛函數的用法和作用?

析構函數是在類對象消亡時由系統自動調用。主要用來做對象的清理工作,比如來釋放對象申請的動態空間。

基類中用virtual修飾的函數稱爲虛函數。在派生類中可以對虛函數進行重新定義,這樣同樣的函數接口可以在不同的派生類中對應不同的實現。當通過基類的指針來調用虛函數時,程序會根據指針實際指向的對象來決定調用哪個實現。

 

華爲面試題:6個整數,每個數中都包含'6',和爲100可能嗎?

6個整數,每個數中都包含'6'(在個位或十位上),6個數的和爲100可能嗎?

可能,這六個數的取值爲:60,16,6,6,6,6

首先,6不可能都在個位數上出現,否則6個數的和個位也是會是6。

其次,最多有一個數在十位上出現6,否則6個數的和會大於100。

這六個數爲 6?,?6,?6,?6,?6,?6。不考慮?部分,總和等於90。所以最終的結果60,16,6,6,6,6。

 

 

華爲面試題:找出字符串中的數字和字母,字母要求大寫輸出

首先定義一個長度爲256的字符數組char_map,將數組的值設爲:

char_map['0'] = '0';

....

char_map['9'] = '9';

char_map['A'] = 'A';

....

char_map['Z'] = 'Z';

char_map['a'] = 'A';

....

char_map['z'] = 'Z';

其它值設爲0。

 

然後遍歷字符串中的所有字符,使用此字符作爲下標到char_map中找出對應的值。如果值爲0,則不輸出,如果不爲0,則輸出查找到的值。

完美時空面試題:memcpy 和 memmove 有什麼區別?

memcpy和memmove都是將源地址的若干個字符拷貝到目標地址。

如果源地址和目標地址有重疊,則memcpy不能保證拷貝正確,但memmove可以保證拷貝正確。

 

例如:

char src[20];

// set src

char* dst = src + 5;

此時如果要從src拷貝10個字符到dst,則麼memcpy不能保證拷貝正確,但是memmove可以保證。

 

騰訊面試題:const的含義及實現機制

const用來說明所定義的變量是隻讀的。

這些在編譯期間完成,編譯器可能使用常數直接替換掉對此變量的引用。

 

迅雷面試題:門面模式的解釋、適用場合?

門面模式又被稱爲外觀模式,爲子系統中的一組接口提供一個一致的界面,該模式定義了一個高層接口,使得這個子系統更加容易使用。

舉個例子:在做項目或產品的過程中進行跨部門合作的時候,每個部門都有個相應的接口人,那麼我們只需和對應部門的接口人交互即可。

 

適用場合:

爲一個複雜子系統提供一個簡單接口:子系統往往因爲不斷演化而變得越來越複雜,使用門面模式可以使得子系統更具有可複用性。

子系統的獨立性:引入門面模式將一個子系統與它的客戶端以及其他子系統分離,可以提高子系統的獨立性和可移植性。

層次化結構:在構建一個層次化的系統時,可以使用 門面模式定義系統中每一層的入口。如果層與層之間是相互依賴的,則可以限定它們僅通過門面進行通信,簡化層與層之間的依賴關係。

 

迅雷面試題:兩整數相除,求循環節

求循環節,若整除則返回NULL,否則返回char*指向循環節。先寫思路。

函數原型:char* get_circle_digits(unsigned k,unsigned j)

 

回想我們使用手算時如何發現循環節:
- 如果除得的餘數等於0,則說明整除;
- 如果除得的餘數不等於0,則將餘數乘以10,繼續相除;
直到發現兩次獲得的餘數相等,則找到了循環節。

 

迅雷面試題:AJAX的原理、如何實現刷新及其優點

AJAX即“Asynchronous JavaScript and XML”(異步JavaScript和XML),是指一種創建交互式網頁應用的網頁開發技術。

 

使用了AJAX技術的網頁,利用Javascript和服務器通信,獲取數據,然後再通過修改網頁的DOM中的某些元素來實現刷新網頁的特定部分。

 

使用了AJAX技術後,由於只需要更新網頁的一部分,而不是全部,所以和服務器交互的數據比較少。這就降低了服務器的負載,並提高了用戶端的響應速度。另外,AJAX並不需要在瀏覽器中安裝插件。

 

百度面試題:設計DNS服務器中cache的數據結構

要求設計一個DNS的Cache結構,要求能夠滿足每秒5000以上的查詢,滿足IP數據的快速插入,查詢的速度要快。(題目還給出了一系列的數據,比如:站點數總共爲5000萬,IP地址有1000萬,等等)

DNS服務器實現域名到IP地址的轉換。

 

每個域名的平均長度爲25個字節(估計值),每個IP爲4個字節,所以Cache的每個條目需要大概30個字節。

總共50M個條目,所以需要1.5G個字節的空間。可以放置在內存中。(考慮到每秒5000次操作的限制,也只能放在內存中。)

 

可以考慮的數據結構包括hash_map,字典樹,紅黑樹等等。

這道題目是系統設計題,應該不是這麼簡單的,比如如果查詢包含了許多該DNS不包含的url,如何快速的排除掉,hash查找的效率在這裏應該要仔細考慮。 IP地址的快速插入實際上要考慮多線程的問題,隱藏了讀寫鎖的問題在裏面。

 

騰訊面試題:買200返100優惠券,實際上折扣是多少?

到商店裏買200的商品返還100優惠券(可以在本商店代替現金)。請問實際上折扣是多少?

由於優惠券可以代替現金,所以可以使用200元優惠券買東西,然後還可以獲得100元的優惠券。

假設開始時花了x元,那麼可以買到 x + x/2 + x/4 + ...的東西。所以實際上折扣是50%.(當然,大部分時候很難一直兌換下去,所以50%是折扣的上限)

 

如果使用優惠券買東西不能獲得新的優惠券,那麼

總過花去了200元,可以買到200+100元的商品,所以實際折扣爲 200/300 = 67%.

 

迅雷面試題:數組與鏈表的區別?

在數組中,元素在內存中連續存放。對於訪問操作,由於元素類型相同,佔用內存相同,所以可以通過數組的下標計算出元素所在的內存地址,便於快速訪問。但對於插入或刪除操作,需要移動大量元素,所以速度比較慢。

在鏈表中,元素在內存中沒有連續存放,而是通過元素中的指針將各個元素連接在一起。對於訪問操作,需要從鏈表頭部開始順序遍歷鏈表,直到找到需要的元素,所以速度比較慢。對於插入或刪除操作,只需修改元素中的指針即可完成,速度比較快。

所以,如果需要頻繁訪問數據,很少插入刪除操作,則使用數組;反之,如果頻繁插入刪除,則應使用鏈表。

IBM面試題:平面上畫1999條直線,最多能將平面分成多少部分?

沒有直線時有一個空間;(1)

1條直線時,這條這些可以將這個空間分成兩個;(1+1)

2條直線時,第二條直線可以和第一條直線相交,這樣第二條直線可以將兩個空間分成四個;(1+1+2)

....

注意到畫每條直線時能增加多少個空間,取決於此直線從多少個空間中通過。

而從多少個空間中通過,取決於和多少條直線相交。

例如,如果一條直線和其它5條直線相交,那麼最大可以通過6個空間,此直線可以增加6個子空間。

畫每條直線時,能相交的直線數爲總的已經畫過的直線。

 

所以總的空間數最多爲

1+1+2+3+...+1999 = 1999001

 

IBM面試題:使用兩根燒1小時的香,確定15分鐘的時間

第一根點燃兩頭,第二根只點一頭。

當第一根燒完時,時間過去了30分鐘,所以第二根還能燒30分鐘。這時點燃第二根的另外一頭,第二根香還能燒的時間就是15分鐘。

IBM面試題:27個人去買礦泉水

有27個人去買礦泉水,商店正好在搞三個空礦泉水瓶可以換一瓶礦泉水的活動,他們至少要買幾瓶礦泉水才能每人喝到一瓶礦泉水?

 

如果開始買3瓶,那麼可以四個人喝,並且還能剩一個空瓶。

如果開始買9瓶,可以13個人喝,最後還剩一個空瓶。

如果開始買18瓶,那麼26個人喝,可以剩下兩個空瓶。

如果開始買19瓶,那麼27個人喝,最後剩下三個空瓶。所以最少買19瓶。

 

如果可以向商店先欲借一個空瓶,那麼買18瓶,最後一個人喝完再將空瓶還給商店。

那麼買18瓶也可以滿足要求。

 

IBM面試題:c++中引用和指針有什麼不同?指針加上什麼限制等於引用?

引用不是一個變量,它只表示該引用名是目標變量名的一個別名,它本身不是一種數據類型,因此引用本身不佔存儲單元,系統也不給引用分配存儲單元。引用一經確定就不能修改。

指針是一個變量,需要在內存中分配空間,此空間中存儲所指對象的地址。由於指針是一個普通變量,所以其值還可以通過重新賦值來改變。

把指針定義爲const後,其值就不能改變了,功能和引用類似,但有本質的區別。

 

百度面試題:將多個集合合併成沒有交集的集合

給定一個字符串的集合,格式如:{aaa bbb ccc}, {bbb ddd},{eee fff},{ggg},{ddd hhh}要求將其中交集不爲空的集合合併,要求合併完成後的集合之間無交集,例如上例應輸出{aaa bbb ccc ddd hhh},{eee fff}, {ggg}。

(1)請描述你解決這個問題的思路;

(2)請給出主要的處理流程,算法,以及算法的複雜度

(3)請描述可能的改進。

集合使用hash_set來表示,這樣合併時間複雜度比較低。

 

1. 給每個集合編號爲0,1,2,3...

2. 創建一個hash_map,key爲字符串,value爲一個鏈表,鏈表節點爲字符串所在集合的編號。

遍歷所有的集合,將字符串和對應的集合編號插入到hash_map中去。

 

[空間中的評論有字數限制,所以只能將答案分成多個評論。

3. 創建一個長度等於集合個數的int數組,表示集合間的合併關係。例如,下標爲5的元素值爲3,表示將下標爲5的集合合併到下標爲3的集合中去。

開始時將所有值都初始化爲-1,表示集合間沒有互相合並。

在集合合併的過程中,我們將所有的字符串都合併到編號較小的集合中去。

遍歷第二步中生成的hash_map,對於每個value中的鏈表,首先找到最小的集合編號(有些集合已經被合併過,需要順着合併關係數組找到合併後的集合編號),然後將鏈表中所有編號的集合都合併到編號最小的集合中(通過更改合併關係數組)。

4.現在合併關係數組中值爲-1的集合即爲最終的集合,它的元素來源於所有直接或間接指向它的集合。

 

算法的複雜度爲O(n),其中n爲所有集合中的元素個數。

 

題目中的例子:

0: {aaa bbb ccc}

1: {bbb ddd}

2: {eee fff}

3: {ggg}

4: {ddd hhh}

生成的hash_map,和處理完每個值後的合併關係數組分別爲

aaa: 0。[-1, -1, -1, -1, -1]

bbb: 0, 1。[-1, 0, -1, -1, -1]

ccc: 0。[-1, 0, -1, -1, -1]

ddd: 1, 4。[-1, 0, -1, -1, 0]

eee: 2。[-1, 0, -1, -1, 0]

fff: 2。[-1, 0, -1, -1, 0]

ggg: 3。[-1, 0, -1, -1, 0]

hhh: 4。[-1, 0, -1, -1, 0]

所以合併完後有三個集合,第0,1,4個集合合併到了一起,

第2,3個集合沒有進行合併。

 

網易面試題:new/delete和malloc/free的區別

new/delete:給定數據類型,new/delete會自動計算內存大小,並進行分配或釋放。如果是對類進行操作,new/delete還會自動調用相應的構造函數和析構函數。

malloc/free:沒有進行任何數據類型檢查,只負責分配和釋放給定大小的內存空間。

有些情況下,new/delete和malloc/free都不能滿足性能的要求,我們需要自建內存分配來提高效率。比如,如果程序需要動態分配大量很小的對象,我們可以一次分配可以容納很多小對象的內存,將這些小對象維護在鏈表中,當程序需要時直接從鏈表中返回一個。

還有一點,new返回指定類型的指針;而malloc返回void*,必須強制類型轉化。

有個比較有意思的地方是:int *p=(void*)malloc(1);可以編譯並運行。

百度面試題:有兩個文件,各含50M和500個url,找出共同的url

首先使用包含500個url的文件創建一個hash_set。

然後遍歷50M的url記錄,如果url在hash_set中,則輸出此url並從hash_set中刪除這個url。

所有輸出的url就是兩個記錄裏相同的url。

 

在這些方法之前,可不可以先將兩個文件都分割一下,然後再一一對應着地求交集?比如:將域名理解成由多個n-gram組成,然後將50M文件中的每個域名隨機地按其中一個“n-gram” hash到一個子文件中;將小文件中的每個域名按所有的“n-gram”hash到多個子文件中去。由此,兩個文件分別被分割爲多個子文件,然後只對對應的兩兩子文件做求交操作。

 

 

迅雷面試題:最快的排序法的性能,並列舉至少三個

最快的排序算法是O(N*lgN)。 快速排序,歸併排序 堆排序

 

微軟面試題:刪除鏈表中的重複項

一個沒有排序的鏈表,比如list={a,l,x,b,e,f,f,e,a,g,h,b,m},請去掉重複項,並保留原順序,以上鍊表去掉重複項後爲newlist={a,l,x,b,e,f,g,h,m},請寫出一個高效算法(時間比空間更重要)。

 

建立一個hash_map,key爲鏈表中已經遍歷的節點內容,開始時爲空。

從頭開始遍歷鏈表中的節點:

- 如果節點內容已經在hash_map中存在,則刪除此節點,繼續向後遍歷;

- 如果節點內容不在hash_map中,則保留此節點,將節點內容添加到hash_map中,繼續向後遍歷。

 

谷歌面試題:找到兩個字符串的公共字符,並按照其中一個的排序。寫一函數f(a,b),它帶有兩個字符串參數並返回一串字符,該字符串只包含在兩個串中都有的並按照在a中的順序。寫一個版本算法複雜度O(N^2)和一個O(N) 。

 

O(N^2):

對於a中的每個字符,遍歷b中的每個字符,如果相同,則拷貝到新字符串中。

 

O(N):

首先使用b中的字符建立一個hash_map,對於a中的每個字符,檢測hash_map中是否存在,如果存在則拷貝到新字符串中。

 

谷歌面試題:在給定整數序列中,找出最大和的子序列

給定一個整數序列,其中有些是負數,有些是正數,從該序列中找出最大和的子序列。比如:-5,20,-4,10,-18,子序列[20,-4,10]具有最大和26。

 

` int GetMaxSubArraySum(int* array, int array_len) {

`    int current_sum = 0;

`    int max_sum = 0;

`    for (int i = 0; i < array_len; ++i) {

`      current_sum += array[i];

`      if (current_sum > max_sum) {

`        max_sum = current_sum;

`      } else if (current_sum < 0) {

`        current_sum = 0;

`      }

`    }

`    return max_sum;

` }

 

谷歌面試題:如何儘快找到一個好人有n個人,其中超過半數是好人,剩下的是壞人。好人只說真話,壞人可能說真話也可能說假話。這n個人互相都知道對方是好人還是壞人。

 

現在要你從這n個人當中找出一個好人來,只能通過以下方式:

每次挑出兩個人,讓這兩個人互相說出對方的身份,你根據兩個人的話進行判斷。

 

問通過何種方法才能最快的找出一個好人來。(要考慮最壞的情況)

按照這個方法,答案爲3n,不知有沒有更快的方法。

 

騰訊面試題:用UDP協議通訊時怎樣得知目標機是否獲得了數據包

以在每個數據包中插入一個唯一的ID,比如timestamp或者遞增的int。

發送方在發送數據時將此ID和發送時間記錄在本地。

接收方在收到數據後將ID再發給發送方作爲迴應。

發送方如果收到迴應,則知道接收方已經收到相應的數據包;如果在指定時間內沒有收到迴應,則數據包可能丟失,需要重複上面的過程重新發送一次,直到確定對方收到。

 

網易面試題:沒有拷貝構造函數和重載=運算符的string類,c++中,一個沒有拷貝構造函數和重載=運算符的string類,會出現什麼問題,如何解決?

 

如果沒有定義拷貝構造函數和重載=運算符,則系統會自動生成逐位拷貝的函數。

當我們用string初始化string時,(比如 string a("abc"); string b = a;),兩個對象會指向同樣的內存地址。在兩個對象的析構函數中,我們會對同一個內存塊調用兩次刪除,導致不確定的結果。

當我們將一個string賦值給另外一個string時,(比如 string a("abc"); string b(“cde"); b = a;)除了上面的多次調用析構函數的問題外,由於原來對象b指向的數據沒有被正確刪除,會導致內存泄漏。

 

解決辦法:

1. 添加這兩個函數。

2. 不使用這兩個函數。

- 不用string初始化string:可以使用string a(”abc"); string b(a.c_str()); 代替。

- 不用string給string賦值,包括不能通過傳值方法傳遞string參數:儘量使用指針。

 

百度面試題:芯片測試

有2k塊芯片,已知好芯片比壞芯片多。請設計算法從其中找出一片好芯片,說明你所用的比較次數上限。
其中好芯片和其它芯片比較時,能正確給出另一塊芯片是好還是壞。壞芯片和其它芯片比較時,會隨機的給出好或是壞。

兩塊芯片比較,結果可能是(好好),(好壞),(壞壞)。如果結果是(好好),則可能是兩塊好芯片,也可能是兩塊壞芯片。如果結果是(好壞)或者(壞壞),則兩塊芯片中至少有一塊是壞的。

1. 將2k塊芯片分成k組,每組兩個比較。如果結果是(好壞)或者(壞壞),則把兩塊芯片都去掉;如果結果是(好好),則任選其中一塊去掉。可以保證剩下好芯片比壞芯片多。

2. 如果剩下1個或2個,則可以判斷這些是好芯片。

3. 如果剩下偶數個,則回到第1步。

4. 如果剩下奇數個,則任取一個和其它所有芯片比較,如果有一半將此芯片判爲好芯片,則說明此芯片爲好芯片,測試結束;否則,說明此芯片爲壞芯片,去掉此芯片(當然還可以去掉將此芯片判爲好芯片的芯片)後總芯片數爲偶數,回到第1步。

 

考慮最壞的情況:

第1次,進行k次比較,可以使芯片數減半;

第2次,k爲奇數,則需要進行k-1次比較,使芯片數減爲k-1。

第3次,通過(k-1)/ 2次比較可以使芯片數減爲 (k-1)/2。

第4次,(k-1)/2爲奇數,需要(k-1)/2 - 1次比較

第5次,需要 ((k-1)/2 - 1)/2次比較

..

所以最多4k次比較。

 

請寫出下面c語言代碼的輸出

 

# include<stdio.h>

int main()

{

int a,b,c,d;

a=10;

b=a++;

c=++a;

d=10*a++;

printf("b, c, d: %d, %d, %d", b, c, d);

return 0;

}

a++ 是先用a進行運算,然後在將a的值加1;++a是先將a的值加1,然後再參與運算。

所以答案是 b, c, d: 10, 12, 120

 

迅雷面試題:合併用戶基本信息和看電影的記錄

如何有效合併兩個文件:一個是1億條的用戶基本信息,另一個是用戶每天看電影連續劇等的記錄,5000萬條。其中內存只有1G。

顯然內存不能同時存下所有的數據,所以考慮分而治之的思想。

假設1K Byte可以保存一個用戶的基本信息和看電影記錄。我們可以將基本信息和看電影記錄都按照hash(user_name)%100的餘數各分成100個小文件。利用1G內存,我們可以每次只處理一對小文件,然後將結果輸出到一個文件中即可。

在處理一對小文件時,可以利用key爲用戶名的hash_map將基本信息和看電影記錄合併在一起

 

百度面試題:用C語言將輸入的字符串在原串上倒序

` void revert(char* str) {

`    char c;

`    for (int front = 0, int back = strlen(str) - 1;

`         front < back;

`         ++front, --back) {

`      c = str[back];

`      str[back] = str[front];

`      str[front] = c;

`    }

` }

已知一個字串由GBK漢字和ascii編碼的數字和字母混合組成,編寫c語言函數實現從中去掉所有ascii編碼的字母和數字(包括大小寫),要求在原字符串上返回結果。

例如: “http://hi.baidu.com/mianshiti 是討論IT面試題的博客” 會變成 “://../ 是討論面試題的博客”。

注:函數接口爲:int filter_ascii(char* gbk);漢字的GBK編碼範圍是0x8140-0xFEFE。

 

`int filter_ascii(char* gbk) {

` int new_p = 0;

` int old_p= 0;

` while (gbk[old_p]) {

` if (gbk[old_p] > 0x81 ||

`    gbk[old_p] == 0x81&& gbk[old_p + 1] >= 0x40) {

`   gbk[new_p++] = gbk[old_p++];

`   gbk[new_p++] = gbk[old_p++];

` } else if (gbk[old_p] >= 'a' && gbk[old_p] <= 'z' ||

`    gbk[old_p] >= 'A' && gbk[old_p] <= 'Z' ||

`    gbk[old_p] >= '0' && gbk[old_p] <= '9') {

`   old_p++;

` } else {

`   gbk[new_p++] = gbk[old_p++];

` }

` }

` gbk[new_p] = '\0';

` return 0;

`}

 

一個警察,一個小偷,一個爸爸,一個媽媽,兩個兒子,兩個女兒,共八個人要過一條河,河上沒有橋,只有一條船。
有幾個條件必須滿足:
1、船一次最多隻能坐兩個人
2、小偷必須和警察在一起,否則小偷會偷東西
3、爸爸必須和兒子在一起否則媽媽會打兒子
4、媽媽必須和女兒在一起否則爸爸會打女兒
5、只有警察,爸爸,媽媽會划船
現在要他們八個都安全過河,請提出方案。

1. 警察和小偷過去,警察回來

2. 警察和兒子過去,警察和小偷回來

3. 爸爸和兒子過去,爸爸回來

4. 爸爸和媽媽過去,媽媽回來

5. 警察和小偷過去,爸爸回來

6. 爸爸媽媽過去,媽媽回來

7. 媽媽和女兒過去,警察和小偷回來

8. 警察和女兒過去,警察回來

9. 警察和小偷過去

在每一步都看看有幾種可能的方案,然後選一種方案進行下去。

如果到哪個地方進行不下去了,則向上回溯,找到另外一種可能方案進行下去....

直到找到一種可行的方案。

 

簡直就是一種人肉回溯法。

不知有沒有其它方法。

 

編寫一個C語言函數,要求輸入一個url,輸出該url是首頁、目錄頁或者其他url
如下形式叫做首頁:
militia.info/
www.apcnc.com.cn/
http://www.cyjzs.comwww.greena888.com/
如下形式叫做目錄頁:
http://hi.baidu.com/mianshiti/
thursdaythree.net/greenhouses--gas-global-green-house-warming/

請注意:
a) url有可能帶http頭也有可能不帶
b)動態url(即含有"?"的url)的一律不算目錄頁,如:
www.buddhismcity.net/utility/mailit.php?l=/activity/details/3135/
www.buddhismcity.net/utility/mailit.php?l=/activity/details/2449/ 

` // 0: home, 1: directory, 2: other

` int GetUrlType(char* url) {

` int start_without_http = 0;

` // skip the http head.

` char http_head[] = "http://"

` int i;

` for (i = 0; http_head[i] && url[i]; ++i) {

`   if (url[i] != http_head[i]) {

`    break;

`   }

` }

` if (i == 7) {

`   start_without_http = 7;

` }

` int slash_num = 0;

` for (i = start_without_head; url[i]; ++i) {

`   if (url[i] == '?') {

`    return 2;

`   }

`   if (url[i] == '/') {

`    slash_num++;

`   }  

` }

` if (slash_num > 0 && url[i-1] != '/') {

`   return 2;

` }

` if (slash_num > 1) {

`   return 1;

` }

` return 0;

` }

已知一個無向無環連通圖T的所有頂點和邊的信息,現需要將其轉換爲一棵樹,要求樹的深度最小,請設計一個算法找到所有滿足要求的樹的根結點,並分析時空複雜度。

最簡單直接的方法就是把每個節點都試一遍:

假設某個節點爲根節點,計算樹的深度。當遍歷完所有節點後,也就找到了使樹的深度最小的根節點。

但這個方法的複雜度很高。如果有n個節點,則時間複雜度爲O(n^2)。

樹的深度取決於根節點到最深葉節點的距離,所以我們可以從葉節點入手。

葉節點會且只會和某一個節點連通(反之不成立,因爲根節點也可能只和一個節點連通),所以我們很容易找到所有可能的葉節點。

題目可以等價於找到了兩個葉節點,使得兩個葉節點之間的距離最遠。根節點就是這兩個葉節點路徑的中間點(或者中間兩個點的任意一個)。

我們可以每次都將連接度爲1的節點刪掉,直到最後只剩下1個或2個節點,則這一個節點,或者兩個節點中的任意一個,就是我們要找的根節點。

網易面試題:如何把一個正方形按面積5等分?

 

這道題有點腦筋急轉彎的感覺。

只要將對邊都5等分,然後將對應的點連接起來就可以了。

 

有時還會要求5等分後幾個多邊形是相互連接的。

可以考慮將正方形的周長5等分,然後連接到中心點就可以了。

應該不是這麼簡單,不借助工具,沒辦法五等分,每個頂點連接順時針的第二條邊的中點,得到四個直角形,中間一個正方形,面積都相等。

 

有兩個字符串 str1和str2,寫一個函數實現在str1中查找str2的初始位置。要求不區分大小寫。

從str1的第一個字符到第len(str1)-len(str2)個字符,嘗試匹配,如果相等,則返回對應位置。

設str1和str2的長度分別爲m和n,這樣的複雜度爲O(mn)。

使用KMP算法,複雜度爲O(m + n)。

KMP算法首先對str2進行預處理。當在某處匹配失敗時,不需要回溯str1,而只需要根據預處理的結果回溯str2即可。

至於大小寫問題,只需將所有大寫字母轉成小寫字母后再比較即可。

 

序列Seq=[a,b,…z,aa,ab…az,ba,bb,…bz,…,za,zb,…zz,aaa,…] 類似與excel的排列,任意給出一個字符串s=[a-z]+(由a-z字符組成的任意長度字符串),請問s是序列Seq的第幾個。

注意到每滿26個就會向前進一位,類似一個26進制的問題。

比如ab,則位置爲26*1 + 2;

比如za,則位置爲26*26 + 1;

比如abc,則位置爲26*26*1 + 26*2 + 3;

 

網易面試題:10個人分成4組 有幾種分法?

每組的人數可能爲下面的值:

1,1,1,7

1,1,2,6

1,1,3,5

1,1,4,4

1,2,2,5

1,2,3,4

1,3,3,3

2,2,2,4

2,2,3,3

下面分別計算每種人數對應的分法數:

1,1,1,7 = 10*9*8/(1*2*3)=120

1,1,2,6 = 10*9/(1*2) * 8*7/(1*2) = 1260

1,1,3,5 = 10*9/(1*2) * 8*7*6/(1*2*3) = 2520

1,1,4,4 = 10*9/(1*2) * 8*7*6*5/(1*2*3*4) / (1*2) = 1575

1,2,2,5 = 10 * 9*8/2 * 7*6/2 /2 = 3780

1,2,3,4 = 10 * 9*8/2 * 7*6*5/(2*3) = 12600

1,3,3,3 = 10 * 9*8*7/(2*3) * 6*5*4/(2*3) / (2*3) = 2800

2,2,2,4 = 10*9/2 * 8*7/2 * 6*5/2 /(2*3) = 3150

2,2,3,3 = 10*9/2 * 8*7/2 * 6*5*4/(2*3) / 2 / 2 = 6300

所以總的分法爲

120 + 1260 + 2520 + 1575 + 3780 + 12600 + 2800 + 3150 + 6300 = 34105

 

給出若干個單詞,組成字典,要求查找速度最快。

爲使查找速度最快,可以要使用hash_map。

如果每個單詞還有對應的解釋和例句,可以將解釋和例句對應的指針存放在hash_map的值中

 

寫一段程序,實現atoi(const char* s)方法。

atoi用於將字符串轉換成爲整數。

比如 “123” =》 123, “-246” =》 -246。

 

`   int atoi(const char*s) {

`     int result = 0;

`     bool is_plus = true;

`     if (*s == '+') {

`       ++s;

`     } else if (*s == '-') {

`       ++s;

`       is_plus = false;

`     }

`     while (*s >= '0' && *s <= '9') {

`       result = result * 10 + *s - '0';

`       ++s;

`     }

`     if (is_plus) {

`       return result;

`     } else {

`       return -result;

`     }

`   }

 

 

寫一段程序,找出數組中第k大小的數,輸出數所在的位置。例如{2,4,3,4,7}中,第一大的數是7,位置在4。第二大、第三大的數都是4,位置在1、3隨便輸出哪一個均可。

先找到第k大的數字,然後再遍歷一遍數組找到它的位置。所以題目的難點在於如何最高效的找到第k大的數。

 

我們可以通過快速排序,堆排序等高效的排序算法對數組進行排序,然後找到第k大的數字。這樣總體複雜度爲O(N logN)。

 

我們還可以通過二分的思想,找到第k大的數字,而不必對整個數組排序。

從數組中隨機選一個數t,通過讓這個數和其它數比較,我們可以將整個數組分成了兩部分並且滿足,{x, xx, ..., t} < {y, yy, ...}。

在將數組分成兩個數組的過程中,我們還可以記錄每個子數組的大小。這樣我們就可以確定第k大的數字在哪個子數組中。

然後我們繼續對包含第k大數字的子數組進行同樣的劃分,直到找到第k大的數字爲止。

平均來說,由於每次劃分都會使子數組縮小到原來1/2,所以整個過程的複雜度爲O(N)。

 

給定函數d(n) = n + n的各位之和,n爲正整數,如 d(78) = 78+7+8=93。 這樣這個函數可以看成一個生成器,如93可以看成由78生成。

定義數A:數A找不到一個數B可以由d(B)=A,即A不能由其他數生成。現在要寫程序,找出1至10000裏的所有符合數A定義的數。

申請一個長度爲10000的bool數組,每個元素代表對應的值是否可以有其它數生成。開始時將數組中的值都初始化爲false。

由於大於10000的數的生成數必定大於10000,所以我們只需遍歷1到10000中的數,計算生成數,並將bool數組中對應的值設置爲true,表示這個數可以有其它數生成。

最後bool數組中值爲false的位置對應的整數就是不能由其它數生成的。

 

 

13個壞人和13個好人站成一圈,數到7就從圈裏面踢出一個來,要求把所有壞人都給踢出來,所有好人都留在圈裏。請找出初始時壞人站的位置。

定義一個長度爲26的循環鏈表,節點中存放自己的節點號(順序從0到25)。

我們從0號節點開始,每向前移動7步就將指向的節點從鏈表中刪掉,直到剩下13個節點。

每次刪掉的節點所存儲的節點號就是初始時壞人站的位置,將這些位置收集起來就得到了所有壞人的初始位置。

 

求一個論壇的在線人數,假設有一個論壇,其註冊ID有兩億個,每個ID從登陸到退出會向一個日誌文件中記下登陸時間和退出時間,要求寫一個算法統計一天中論壇的用戶在線分佈,取樣粒度爲秒

一天總共有 3600*24 = 86400秒。

定義一個長度爲86400的整數數組int delta[86400],每個整數對應這一秒的人數變化值,可能爲正也可能爲負。開始時將數組元素都初始化爲0。

然後依次讀入每個用戶的登錄時間和退出時間,將與登錄時間對應的整數值加1,將與退出時間對應的整數值減1。

這樣處理一遍後數組中存儲了每秒中的人數變化情況。

定義另外一個長度爲86400的整數數組int online_num[86400],每個整數對應這一秒的論壇在線人數。

假設一天開始時論壇在線人數爲0,則第1秒的人數online_num[0] = delta[0]。第n+1秒的人數online_num[n] = online_num[n-1] + delta[n]。

這樣我們就獲得了一天中任意時間的在線人數。

 

一個小猴子邊上有100根香蕉,它要走過50米才能到家,每次它最多搬50根香蕉,每走1米就要吃掉一根,請問它最多能把多少根香蕉搬到家裏。

小猴子可以採用如下策略:

小猴子先搬50根,走到1米處,路上吃掉1根,放下48根後返回起始點,並在返回路上吃剩下的1根。然後將起始點處的50根香蕉搬到1米處,又在路上吃掉1根。這樣總共消耗了3根香蕉,將所有香蕉向前搬動了1米。採用類似的策略搬動16米後,總共消耗了48根香蕉,還剩下52根香蕉。

如果繼續按照同樣的策略向前移動到17米處,則剩下49根香蕉;如果直接在16米處丟掉2根香蕉,搬着50根香蕉向前走,在17米處也是有49根香蕉。所以猴子在17米處最多可以保留49根香蕉。

繼續搬到家還有33米,所以最後剩的香蕉數16根。

 

在一個文件中有 10G 個整數,亂序排列,要求找出中位數。內存限制爲 2G。

 

不妨假設10G個整數是64bit的。

 

2G內存可以存放256M個64bit整數。

我們可以將64bit的整數空間平均分成256M個取值範圍,用2G的內存對每個取值範圍內出現整數個數進行統計。這樣遍歷一邊10G整數後,我們便知道中數在那個範圍內出現,以及這個範圍內總共出現了多少個整數。

如果中數所在範圍出現的整數比較少,我們就可以對這個範圍內的整數進行排序,找到中數。如果這個範圍內出現的整數比較多,我們還可以採用同樣的方法將此範圍再次分成多個更小的範圍(256M=2^28,所以最多需要3次就可以將此範圍縮小到1,也就找到了中數)。

 

 

編一個程序求質數的和,例如F(7) = 2+3+5+7+11+13+17=58。

方法1:

對於從2開始的遞增整數n進行如下操作:

用 [2,n-1] 中的數依次去除n,如果餘數爲0,則說明n不是質數;如果所有餘數都不是0,則說明n是質數,對其進行加和。

 

空間複雜度爲O(1),時間複雜度爲O(n^2),其中n爲需要找到的最大質數值(例子對應的值爲17)。

方法2:

可以維護一個質數序列,這樣當需要判斷一個數是否是質數時,只需判斷是否能被比自己小的質數整除即可。

 

對於從2開始的遞增整數n進行如下操作:

用 [2,n-1] 中的質數(2,3,5,7,開始時此序列爲空)依次去除n,如果餘數爲0,則說明n不是質數;如果所有餘數都不是0,則說明n是質數,將此質數加入質數序列,並對其進行加和。

 

空間複雜度爲O(m),時間複雜度爲O(mn),其中m爲質數的個數(例子對應的值爲7),n爲需要找到的最大質數值(例子對應的值爲17)。

方法3:

也可以不用除法,而用加法。

申請一個足夠大的空間,每個bit對應一個整數,開始將所有的bit都初始化爲0。

對於已知的質數(開始時只有2),將此質數所有的倍數對應的bit都改爲1,那麼最小的值爲0的bit對應的數就是一個質數。對新獲得的質數的倍數也進行標註。

對這樣獲得的質數序列累加就可以獲得質數和。

 

空間複雜度爲O(n),時間負責度爲O(n),其中n爲需要找到的最大質數值(例子對應的值爲17)。

 

有一個由大小寫組成的字符串,現在需要對它進行修改,將其中的所有小寫字母排在大寫字母的前面(大寫或小寫字母之間不要求保持原來次序)。

初始化兩個int變量A和B,代表字符串中的兩個位置。開始時A指向字符串的第一個字符,B指向字符串的最後一個字符。

逐漸增加A的值使其指向一個大寫字母,逐漸減小B使其指向一個小寫字母,交換A,B所指向的字符,然後繼續增加A,減小B....。

當A>=B時,就完成了重新排序。

 

小明和小強都是張老師的學生,張老師的生日是M月N日,2人都不知道張老師的生日是下列10組中的一天,張老師把M值告訴了小明,把N值告訴了小強,張老師問他們知道他的生日是那一天嗎?

3月4日,3月5日,3月8日

6月4日,6月7日

9月1日,9月5日

12月1日,12月2日,12月8日

小明說:如果我不知道的話,小強肯定也不知道

小強說:本來我也不知道,但是現在我知道了

小明說:哦,那我也知道了

請根據以上對話推斷出張老師的生日是哪一天?

根據小明的第一句話:

如果是6月7日的話,則小強開始就知道(因爲7日只出現了一次)。小明敢斷言小強不知道,那麼說明張老師的生日不在6月。

同理,如果是12月2日,則小強開始就知道。小明敢斷言小強不知道,說明張老師生日不在12月。

剩下的可能爲:

3月4日,3月5日 ,3月8日

9月1日,9月5日

 

根據小強的第一句話:

如果小強拿到的數字是5,則張老師的生日可能是3月5日或者9月5日,小強並不能斷定。所以小強拿到的數字不可能是5。

剩下的可能爲:

3月4日,3月8日

9月1日

 

根據小明的第二句話:

如果小明拿到的數字是3,則張老師的生日可能是 3月4日或者3月8日 ,小明並不能斷定。所以小明難道的數字不可能是3。

 

所以張老師的生日是9月1日。

 

n是一個奇數,求證n(n^2-1)能被24整除。

也就是證明n(n^2-1)能被8和3整除。

 

因爲n是奇數,假設n=2x+1,其中x爲整數。

n(n^2-1)

=(2x+1)((2x+1)^2-1)

=(2x+1)(4x^2+4x+1-1)

=(2x+1)4x(x+1)

顯然x(x+1)能被2整除,所以(2x+1)4x(x+1)能被8整除。

 

x除以3的餘數可能是0,1,2,對應x,x+1,2x+1能被3整除。也就是說(2x+1)4x(x+1)能被3整除。

所以n(n^2-1)能被24整除。

 

 

兩個整數集合A和B,求其交集。

利用map來解決。

1. 讀取整數集合A中的整數,並將讀到的整數插入到map中。

2. 讀取整數集合B中的整數,如果該整數在map中,則將此數加入到交集當中。

 

如何引用一個已經定義過的全局變量?

可以用引用頭文件的方式,也可以使用extern關鍵字。

用引用頭文件方式,如果將那個變量寫錯了,那麼在編譯期間會報錯。

用extern方式,如果將變量名寫錯了,那麼在編譯期間不會報錯,而在連接期間報錯。

 

 

n個空間(其中n<1M),存放a到a+n-1的數,位置隨機且數字不重複,a爲正且未知。現在第一個空間的數被誤設置爲-1。已經知道被修改的數不是最小的。請找出被修改的數字是多少。

例如:n=6,a=2,原始的串爲5, 3, 7, 6, 2, 4。現在被別人修改爲-1, 3, 7, 6, 2, 4。現在希望找到5。

總結一下

先O(N)取得最小值a

方法1:異或非-1的所有數,再異或a到a+n-1,最後值即被置-1者。

方法2:加一個a到a+n-1中間的數,減一個非-1的數,直到沒數,最後值即被置-1者。

方法3:bitmap

 

實現一個函數,對一個正整數n,算得到1需要的最少操作次數。

操作規則爲:如果n爲偶數,將其除以2;如果n爲奇數,可以加1或減1;一直處理下去。

例子:

func(7) = 4,可以證明最少需要4次運算

n = 7

n-1 6

n/2 3

n-1 2

n/2 1

要求:實現函數(實現儘可能高效) int func(unsign int n);n爲輸入,返回最小的運算次數。

給出思路(文字描述),完成代碼,並分析你算法的時間複雜度。

int func(unsign int n) {

if (n == 1) {

return 0;

}

if (n%2 == 0) {

return 1 + func(n/2);

}

int x = func(n+1);

int y = func(n-1);

if (x > y) {

return y + 1;

} else {

return x + 1;

}

}

假設n表示成二進制有x bit,可以看出計算複雜度爲O(2^x),也就是O(n)。

將n轉換到二進制空間來看(比如7爲111,6爲110):

- 如果最後一位是0,則對應於偶數,直接進行除2操作。

- 如果最後一位是1,情況則有些複雜。

**如果最後幾位是???01,則有可能爲???001,???1111101。在第一種情況下,顯然應該-1;在第二種情況下-1和+1最終需要的步數相同。所以在???01的情況下,應該選擇-1操作。

**如果最後幾位是???011,則有可能爲???0011,???11111011。在第一種情況下,+1和-1最終需要的步數相同;在第二種情況下+1步數更少些。所以在???011的情況下,應該選擇+1操作。

**如果最後有更多的連續1,也應該選擇+1操作。

 

如果最後剩下的各位都是1,則有11時應該選擇-1;111時+1和-1相同;1111時應選擇+1;大於四個1時也應該選擇+1;

` int func(unsign int n) {

`    if (n == 1) {

`      return 0;

`    }

`    if (n % 2 == 0) {

`      return 1 + func(n/2);

`    }

`    if (n == 3) {

`      return 2;

`    }

`    if ( n & 2) {

`      return 1 + func(n + 1);

`    } else {

`      return 1 + func(n - 1);

`    }

` }

對任意輸入的正整數N,求N!的尾部連續0的個數,並指出計算複雜度。如:18!=6402373705728000,尾部連續0的個數是3。(不用考慮數值超出計算機整數界限的問題)

容易理解,題目等價於求因子2和因子5出現的次數。

對於因子2來說,數字2,4,6,8,10....2n...中存在因子2,這樣就獲得了 N/2 (其中N/2只取結果的整數部分)個因子2。這些數字去除因子2後,變成1,2,3....N/2,又可以提取N/4個因子2....這樣一直到只剩下1個數(1)。所以N!中總共可以獲得N/2 + N/4 + N/8 +....個因子2。

同理,N!中可以獲得N/5 + N/25 + ... 個因子5。

尾部連續0的個數就是因子2和因子5較少的那個。

 

對於題目中的例子,18!中包含9+4+2+1個因子2,包含3個因子5。所以尾部有3個連續0。

 

計算的複雜度爲O(logN)。

 

 

請實現兩棵樹是否相等的比較,相等返回1,否則返回其他值,並說明算法複雜度。

 

數據結構爲:

typedef struct_TreeNode{

char c;

TreeNode *leftchild;

TreeNode *rightchild;

}TreeNode;

函數接口爲:int CompTree(TreeNode* tree1,TreeNode* tree2);

注:A、B兩棵樹相等當且僅當Root->c==RootB-->c,而且A和B的左右子樹相等或者左右互換相等。

遞歸方法(代碼未測試,可能存在bug):

int CompTree(TreeNode* tree1,TreeNode* tree2) {

if (tree1 == NULL && tree2 == NULL) {

    return 1;

}

if (tree1 == NULL || tree2 == NULL) {

    return 0;

}

if (tree1->c != tree2.c) {

    retrun 0;

   }

if (CompTree(tree1->leftchild, tree2->leftchil) &&

CompTree(tree1->rightchild, tree2->rightchild) ||

      CompTree(tree1->leftchild, tree2->rightchild) &&

CompTree(tree1->rightchild, tree2->leftchild)) {

    return 1;

}

}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章