程式設計珠璣第2章 習題解答

2021-06-13 06:22:02 字數 3379 閱讀 1854

a、給定乙個最多包含40億個隨機排列的32為整數的順序檔案,找出乙個不存在檔案中的32位整數(在檔案中至少缺失乙個這樣的數——為什麼?)。在具有足夠記憶體的情況下,如何解決該問題?如何有幾個外部的「臨時」檔案可用,但是僅有幾百位元組的記憶體,又該如何解決問題?

1.在檔案中至少缺失乙個這樣的數?為什麼呢?這是因為32為表示能表示n=232-1個數,(42 9496 7296)這個數》4*10的九次方=40億大的數,因此肯定在檔案中缺失乙個這樣的數。

2.在具有足夠記憶體的情況下如何解決這個問題?根據前一篇介紹的位向量表示法,可以判斷該數是否存在。不過要一次性將所有數都對映到記憶體中,大約所需要40 0000 0000/8byte=500mb的記憶體。

3.‍‍0-1二分查詢:探測每個整數的每個bit是0還是1,讀取n=40億個整數,第1個bit為0或為1的放到不同的檔案中(每個至多為n/2億),少於n/2個數的那組 必定缺少某個數,接著探測第2個bit是0還是1,輸入至多n/2億,輸出至多n/4億,少於n/4個數的那組 必定缺少某個數,以此類推,總的執行時間和n成正比。通過對某組排序掃瞄可以得到缺失的數,這樣執行時間變為o(nlogn)。

q2:給定乙個包含4300000000個32位整數的順序檔案,請問如何找到乙個至少出現兩次的整數?

解答:二分查詢。由於4.3g>32位的整數空間,根據鴿籠原理,肯定會有重複的整數。搜尋範圍從所有的32位正整數開始(全部當成unsigned int,簡化問題),即[0, 2^32),中間值即為2^31。然後遍歷檔案,如果小於2^31的整數個數大於n/2=2^31,則調整搜尋範圍為[0, 2^31],反之亦然;然後再對整個檔案再遍歷一遍,直到得到最後的結果。

這樣一共會有logn次的搜尋,每次過n個整數(每次都是完全遍歷),總體的複雜度為o(nlogn)。

例子:陣列[4,2,5,1,3,6,3,7,0,7],假定從3位的整數空間內搜尋。第一次的範圍為[0,8),遍歷過後發現[0,4)範圍內的整數個數為5,於是調整為搜尋[0,4)範圍內的整數。第二次發現[2, 4)範圍內的證書為3,大於2,於是調整為[2, 4)。再經過第三次的遍歷,找出3為重複出現的整數。

改進:上面的辦法有很多的冗餘。於是提出了乙個辦法:建立乙個新的檔案(是順序檔案就可以)。在一次遍歷過後,確定搜尋的範圍後,把原有檔案裡這個範圍內的整數寫到新的檔案裡去,下次搜尋就只要搜尋這個新檔案了。

這樣可以得到近似線性的複雜度(但是常數項應該很大)。

b.將乙個n元一維向量x向左旋轉i=rotdist個位置。例如,當n=8且i=3時,向量abcdefgh旋轉為defghabc。簡單的**使用乙個n元的中間向量在n步內完成該工作。你能否僅適用數十位額外位元組的儲存空間,在正比於n的時間內完成向量的旋轉?

方法一:額外的空間,將x中的前i個元素複製到乙個臨時陣列中,接著將餘下n-i個元素左移i個位置,然後再將前i個元素從臨時陣列中複製回x中後面的位置,缺點:i個額外的空間。

方法二:定義乙個將x中元素左移一位的函式,然後呼叫該函式i次,缺點:太費時間

方法三:雜技法。該方法很巧妙,但不太容易理解,不知道演算法的作者是怎麼想出來的。

方法四:分塊求逆。這個演算法很好用,通過分別求逆後再求逆來得到最終的結果。

比如要將n元向量旋轉i位,過程為:

reverse(0,i-1)

reverse(i,n-1)

reverse(0,n-1)

我的思路:可以用空間換時間的思想來解決。比如要旋轉的n元向量為abcdefgh,要將它旋轉3位:defghabc我們可以開闢一塊新空間來再儲存乙份n元向量,將它們拼接,為abcdefghabcdefgh,則所要求的旋轉結果為這個字串的子串,取s[3:len(n)+3]即為所求。

方法五:gries的迭代解決方法,塊交換:假設a比b短,將b分為b1和br(和a一樣長) 兩部分,a和br交換,即ab1br交換為brb1a,遞迴交換brb1.不過這個每個元素不能一步到位。不如方法四高效。

c.給定一本英語單詞詞典,請找出所有的變位詞集。例如,因為「pots」,「stop」,「tops」相互之間都是由另乙個詞的各個字母改變序列而構成的,因此這些詞相互之間就是變位詞。

錯誤1:考慮如何求解某個單詞的各個字母的所有置換,n!個可能。

錯誤2:比較所有的單詞對

正確方法:給每個單詞簽名,同一變位詞就具有相同的簽名,然後將相同簽名的單詞歸攏在一起。考慮兩個子問題:簽名(簽名按字母順序排序單詞中的字母)+收集相同簽名的詞(按簽名排序)。

解決思路就是對每乙個單詞進行標記,使得互為變位詞的單詞有相同的標記,而不是變位詞的單詞有不同的標記。

這裡列出幾種可用的標記方法:

(1)對每個單詞進行字典序排序,則互為變位詞的單詞有著相同的排序(若單詞量大,對每個單詞排序比較耗時)

(2)將26個英文本母分別對應乙個素數,則乙個單詞的標記為組成它的每個字母的素數乘積。這樣保證了變位詞的標記時一樣的,而非變位詞有不同 的標記。但當乙個單詞長度比較長時,計算乙個乘積結果,資料量也比較大。

(3)統計乙個單詞中各字母出現的次數,並以此作為標記索引。如mississippi對應i4m1p2s4,可以將1省略,為i4mp2s4.

第二章的思考題總結:

1. 考慮查詢給定輸入單詞的所有變位詞的問題。僅給定單詞和字典的情況下,如何解決問題?如果有一些時間和空間可以在響應任何查詢之前預先處理字典,又會怎樣?

答:沒有時間進行預處理,則直接按該單詞的標識值去查字典,標識相同的單詞輸出。

如有時間和空間進行預處理,可以先對整個字典進行標識計算,將計算值作為索引hash到不同的陣列中,並將標識相同的單詞(變位詞)鏈在同乙個鍊錶中。查詢時按給定的單詞去查索引表(第乙個節點即為煉表表頭節點),然後一次取出變位詞輸出。

第二題在上面已經解答。

7.將4000*4000的矩陣轉置。

書中給出的解決方案是對每個元素插入行號,列號,根據轉置矩陣的特點,對原來的矩陣先按列排序,再按行排序,得到結果矩陣。

8.給定乙個n元實數集合,乙個實數t和乙個整數k,如何快速確定是否存在乙個k元子集,其元素之和不超過t?

第一感覺想到的是排序,然後看前k個數的和是否不超過t,不超過的話肯定存在。更優的方法用o(n)的選擇演算法求出第k大的數,然後把陣列掃瞄一遍,求出小於第k大數的數的和sum,加上第k大。這樣看似沒有什麼錯誤,但是仔細想想,如果第k-1大,第k大,第k+1大的數一樣,腫麼辦?。。。。。。。。 easy~掃瞄的時候順便統計小於第k大數的數的個數a,和第k大的數的個數b,嗯,然後如果a

程式設計珠璣 第一章習題解答

4.生成 0,n 的之間k個不重複的隨機整數。include include include include using namespace std const int n 10000000 const int k 10000000 int randint int l,int r int a n i...

Accelerated C 習題解答 第1章

ex.1 01 include include includeint main std cout std string name std cin name std cout include include includeint main std cout std string name std ci...

Accelerated C 習題解答 第3章

部分習題參考了網上已有的解答 ex.3 0 include include include include include include includeusing std cin using std cout using std endl using std string using std ve...