死磕演算法導論(二)合併排序

2021-08-04 06:40:01 字數 2376 閱讀 3523

將「遞迴」這個概念具體一些,得到的是「分治法」這個思想。分治法的思想,我在這裡摘抄《演算法導論》的內容。

分治模式在每一層遞迴上都有三個步驟

* 分解:將原問題分解成一系列子問題;

* 解決:遞迴地解各子問題。若子問題足夠小,則直接求解;

* 合併:將子問題的結果合併成原問題的解。

在圖中,用實現箭頭代表分解問題,用虛線箭頭來合併解。這種方法解決問題的意圖是什麼呢?很顯然,原問題太過複雜,我們無法直接解決。但是仔細觀察問題我們可以發現,原來的問題可以分解成若干個規模相似,解決方法類似的子問題,子問題又能夠按照這種方法來繼續細分。我們不斷重複這個工作,直到我們無法再繼續細分問題。接著,我們解決最底層的問題,然後一步步地合併,最終合併成原問題的規模,最終就可以完美解決原問題。

應用分治法的乙個例項便是這篇文章所要講的合併排序。在這裡,我再次摘抄《演算法導論》的內容。

合併排序演算法完全依照了上述模式,直觀地操作如下:

* 分解:將n個元素分成各含n/2個元素的子串行;

* 用合併排序法對兩個子串行遞迴地排序;

* 合併兩個已排序的子串行以得到排序結果。

根據《演算法導論》的內容,書中直接給出了合併子串行的演算法,直接一坨文字和演算法甩在我的臉上。對於我來說,從微觀到巨集觀本身不符合我的認知習慣;另外,由於我智商有限,一上來就是抽象的理論未免讓我理解起來太過勞累。此時頗為煩惱的我翻到了合併排序的總算法,函式merge_sort(),這裡我用c++來表達:

void merge_sort(int a, int p, int r)

}

根據這個演算法,我生成了10個隨機數,並以畫圖的方式展示了合併排序的過程:

可以看出合併排序實際上就是分治的乙個例項。乙個包含十個數的陣列,前五個為一組,後五個為一組,分別對這兩組進行合併排序,然後將這兩組有序地合併起來。前五個組成的那一組再次分為3個一組的陣列和兩個一組的陣列。然後不停地分下去,最後分到每一組都只有乙個數,對這個數進行合併排序。實際上只有乙個數的時候已經不能再分了,預設就是乙個有序的陣列。接下來按照圖中虛線箭頭方向一層層合併,就能夠對原來的陣列進行合併。

接下來就到了合併排序最核心的演算法了,也就是合併演算法。其含義是把某個陣列的子陣列有序地合併,而分成的兩個子陣列也應當是有序的。那麼具體的合併演算法應當如何操作呢?我拿圖中13、98、86、36、69來舉例。

這五個數,是a陣列的後五個位置,對這個陣列排序,其p為6,q為8,r為10。根據總排序演算法,這個陣列被分為了p~q和q+1~r的兩個子陣列,並且在進行合併之前都已經經歷過合併排序。因此6到8是有序的,9到10是有序的。所以我們真正處理合併操作的時候,我們面對的陣列是這樣的:

一般來說,圖畫到這裡,一般人大概也就能夠看出一些門道了。所謂的合併過程,實際上就是備份整個陣列到另外乙個陣列,然後按照大小依次重寫相應位置的數,最終就能夠完成合併排序。

可是問題也來了,如何才能「依次」重寫呢?顯然,備份至乙個陣列是一定不行的。但是這個陣列是經過處理的,可以分為兩組,每一組都是有序的。這個組的區分就是索引p~q以及索引q+1~r。那麼就可以創立兩個陣列l和r,分別儲存兩部分索引的內容。原來的陣列因為需要重寫,這時可以看做是空的。

我們的演算法可以這麼進行:13和36比,13小,13放在(6)號位;86和36比,36小,36放在(7)號位;86和69比,69小,69放在(8)號位。那麼接下來呢?雖然很明顯(9)號位是86,那麼為什麼呢?書中提到了兩種辦法:第一種是設計一種方法來判定r陣列為空,如果這個判定為true,那麼就可以將86和98直接放在後兩個位置;第二種是擴充套件r,在69後面新增乙個量,使得l中內容無論多大都不會有這個量大,那麼86理所應當就會置於(9)號位。相應的,l後面也要新增這個量。書中給出的方式是第二種方式。的確,第二種方式是簡單的。它僅僅是在每個子陣列後面新增了乙個量。而對於第一種方式來說,則要設計乙個演算法來判定比較已經到了結尾。我們要在每個陣列之後新增的量,毫無疑問,是無限(

至此,我們的排序策略已經非常清晰:

1. 將p~q索引的元素放入l陣列,將q+1~r的元素放入r陣列

2. l和r分別新增乙個元素∞

3. 兩個陣列的元素兩兩比較,較小的放入a陣列。

4. 如果l和r最後都只有

∞ 沒有放入a陣列,代表整個過程已經完成。

(明日繼續)

死磕演算法之選擇排序

假如我們現在要排序的陣列為 3,1,0,2,8,4,2 那麼選擇排序的排序流程為 在這個陣列中找出最小值與第乙個元素交換,現在陣列為 0,1,3,2,8,4,2 在這個陣列中除了第乙個位置的元素外找出最小值與第二個元素交換,因為第二個元素就是最小的所以此次沒有發生變化。現在陣列為 0,1,3,2,8...

死磕演算法之堆排序

堆排序主要是運用了二叉樹的性質來進行的排序。在進行堆排序之前我們先了解一下二叉樹的幾個性質 1.在排序使用二叉樹的時候我們要排序的陣列的第0個位置其實是不可以用的,這個時候如果我們要排序的陣列為 3,1,0,2,8,4,2 時,我們首先要把它變為 0,3,1,0,2,8,4,2 我們把他轉換為二叉樹...

死磕演算法之快速排序

快速排序是乙個運用了分治法和遞迴演算法的排序方式。假如我們現在要排序的陣列為 3,1,0,2,8,4,2 那麼在進行快速排序的時候我們先要進行一些準備 下面開始排序 先從陣列右邊開始,我們發現j指向的元素2比標桿n小,那麼我們將j指向的元素賦值給i指向的元素,停止操作。此時陣列為 2,1,0,2,8...