演算法題的五種解法 程式設計師面試金典

2021-09-11 20:52:45 字數 2888 閱讀 9525

要解決棘手的演算法問題,世上沒什麼不二法門,不過下面介紹的幾種方法可能管用。常言道熟能生巧,題目練習得越多,就越容易確定該採用哪種方法來解決問題。、

另外,下面這五種方法可以「混搭」使用。也就是說,施以「簡化推廣法」後,還可以接著嘗試「模式匹配法」。

方法一:舉例法

我們先從你可能熟悉的「舉例法」開始,也許你從未聽過這種方法。「舉例法」是先列舉一下具體的例子,看看能否發現其中的一般規則。

示例:給定乙個具體時間,計算時針和分針之間的角度。

下面以3點27分為例。確定3點的時針位置和27分的分針位置,我們可以畫出乙個時鐘。

在下面的解法中,h表示小時,m表示分鐘。同時,我們假定h的範圍是0~23.

從這些例子可以得出以下規則:

簡化上述式子可以得到(30h-5.5m)%360。

方法二:模式匹配法

模式匹配法是指將現有問題與相似問題作模擬,看看能否通過修改相關問題的解法來解決新問題。

示例:乙個有序陣列的元素經過迴圈移動,元素的順序可能變為「3 4 5 6 7 1 2」。怎樣才能找出陣列中最小的那個元素?假設數值中的元素各不相同。

這個問題和下面兩個問題有點類似:

處理方法

在無序陣列中查詢最小元素的演算法沒多大意思(只要遍歷所有元素即可),同時它也沒有利用給定資訊(即這是乙個有序陣列),因此這個問題幫不上什麼忙。

然而,二分查詢法就非常適合。我們知道,這是個有序陣列,只是一部分元素迴圈移動過。因此元素排序肯定是從小到大,在某一位置突然變小,接著又開始從小到大排列。那個「轉折點」正是最小的元素。

比較中間元素 與末尾元素(6和2),由於md > right,可以確定這個轉折點就在這兩個元素之間。這不符合從小到大的排列順序,故而表明轉折點就在其中。

如果mid比right小,說明轉折點要麼在前半部分,要麼根本不存在(此陣列嚴格按照從小到大排序)。不管怎樣,將陣列逐步二分進行查詢,最終找到最小的元素(或是轉折點)。

方法三:簡化推廣法

採用簡化推廣法,我們會分多步走。首先,我們會修改某個約束條件,比如資料型別或資料量,從而簡化這個問題。接著,我們轉而處理這個問題的簡化版本。最後,一旦找到解決簡化版問題的演算法,我們就可以基於這個問題進行推廣,並試著調整簡化版的解決方案,讓它適用於這個問題的複雜版本。

接著,我們只需新建乙個陣列並數出字元的數量,即可解決這個簡化後的勒索信問題。陣列中的每個元素對應乙個字母。首先,我們數出每個字元在勒索信**現的次數,然後再遍歷正本雜誌,確認它是否包含勒索信上的全部字元。

推廣這個演算法是,具體做法和上面的差不多。只不過這一回,我們不再建立包含字元計數的陣列,而是建立乙個雜湊表,將單詞對映到其詞頻上。

方法四:簡單構造法

對於某些型別的問題,簡單構造法非常奏效。使用簡單構造法,我們會先從最基本的情況(比如n=1)來解決問題,一般只需記下正確的結果。得到n=1的結果後,接著設法解決n=2的情況。接下來,有了n=1和n=2的結果,我們就可以試著解決n=3的情況了。

最後,你會發現這其實就是一種遞迴演算法——知道n-1的正確結果,就能計算出n時的結果。有時,只有等到算出n為3或4時的結果,我們才能從中找到規律,基於前面的結果解決整個問題。

示例:設計一種演算法,列印某個字串所有可能的排列組合。為簡單起見,假設字串中沒有重複字元。

以字串abcdefg為例:

只有「a」的情況,結果為:

然後是「ab」,結果為:

再然後是「abc」,結果會是什麼呢?

此時,問題開始變得「有點意思」了。得到p(「ab」)的答案,怎麼才能生成p(「abc」)呢?很簡單,新字元是「c」,我們只需在前一種情況的答案也即字元組合的任意位置加乙個c就可以了。也就是:

p(「abc」)=將「c」字元插入p(「ab」)得到的所有字串的任意位置。

亦即:p(「abc」)=merge(,)。

最後得出結果:p(「abc」)= ,。

既然掌握了其中的套路,我們就可以設計乙個遞迴演算法。要生成字串s1...sn的所有排列,我們可以先「砍掉」最後乙個字元,首先生成s1...sn-1的所有排列。得到s1...sn-1所有排列的結果列表之後,我們會迴圈遍歷這個列表,並在每個字串任意位置插入sn。

簡單構造法最後往往會演變成遞迴法。

方法五:資料結構頭腦風暴法

這種方法看起來有點笨,不管很管用。我們可以快速過一遍資料結構的列表,然後逐一嘗試各種資料結構。這種方法很實用,因為一旦找到合適的資料結構(比如說樹),很多問題也就迎刃而解了。

示例:隨機生成一些數字,並儲存到乙個(可擴充套件的)陣列中。如何跟蹤陣列的中位數?

資料結構頭腦風暴法的過程大致如下。

鍊錶?恐怕不行——在數字的訪問和排序上,鍊錶往往效果不佳。

陣列?也許可以,不過你已經用了乙個陣列。你有辦法讓陣列保持有序狀態嗎?這麼做開銷恐怕比較大。

二叉樹?倒也有可能,因為二叉樹非常適合處理排序問題。實際上,如果這棵二叉樹是完全平衡的,根結點可能就是中位數。不過,你要小心——如果它包含偶數個元素,那麼中位數實際上是中間兩個元素的平均值。而中間兩個元素不可能都是根結點。因此,二叉樹也許可行,我們待會兒再說。

堆?堆非常適合基本排序,跟蹤最大值和最小值。對其實也很有意思——只用兩個堆,就能跟蹤較大 那一半元素和較小的那一半元素。較大的一半儲存在小頂堆中,其中最小元素位於堆頂。較小的一半則儲存在大頂堆中,其中最大元素位於堆頂。現在,有了這些資料結構,整個陣列的中位數很可能就是兩個堆頂之一。如果這兩個堆大小不一樣,你可以從元素較多的堆中彈出乙個元素並壓入另乙個堆中,兩個堆很快就能「重獲平衡」。

切記,問題演練得越多,你就越容易判斷該選用哪種資料結構。當然了,你也能更自如地從這五種方法中選出最管用的那種。

程式設計師面試金典

1.有個小孩正在上樓梯,樓梯有n階台階,小孩一次可以上1階 2階 3階。請實現乙個方法,計算小孩有多少種上樓的方式。為了防止溢位,請將結果mod 1000000007 給定乙個正整數intn,請返回乙個數,代表上樓的方式數。保證n小於等於100000。int countways int n retu...

程式設計師面試金典 2 2

return kth to last 返回單鏈表中倒數第k個元素。下面會分別使用遞迴和非遞迴的方法來解決這道題,一般來說遞迴的方法寫起來更容易,但是效率一般不是最好的,比如這道題遞迴解法的 量大約是非遞迴解法的一半,但是時間複雜度依然是o n 遞迴解法。這種方法的本質是先遍歷到鍊錶尾部,最後再返回的...

程式設計師面試金典 2 4

partition 編寫 將鍊錶中小於x的元素放在鍊錶的前半部分,大於x的元素放在鍊錶的後半部分,沒有順序要求。如果是陣列的話,根據x對陣列進行劃分的方法類似於快排。對於鍊錶會更簡單一些,可以直接將原始鍊錶拆分為兩個鍊錶,乙個中所有元素比x小,乙個中所有元素比x大,最後再進行連線。通過在鍊錶中使用b...