給定兩個已排序的表l1和l2 歸併排序簡介

2021-10-14 14:45:15 字數 4369 閱讀 7318

3. 參考文獻

最近在做題的時候接觸到求逆序數的這一類題目,比如51nod-2134 逆序對個數1000,我參考了網上的一些題解,發現大家普遍用歸併排序來解決這一類問題,於是我想要學習一下歸併排序的具體實現。我從《演算法學習與應用從入門到精通》[1]中摘錄了歸併排序這一部分的內容,形成了這篇筆記。

在使用歸併排序法時,將兩個或兩個以上有序表合併成乙個新的有序表。假設初始序列含有k個記錄,首先將這k個記錄看成k個有序的子串行,每個子串行的長度為1,然後兩兩進行歸併,得到k/2個長度為2k為奇數時,最後乙個序列的長度為1)的有序子串行。最後在此基礎上再進行兩兩歸併,如此重複下去,直到得到乙個長度為k的有序序列為止。上述排序方法被稱作二路歸併排序法

歸併排序就是利用歸併過程,開始時先將k個資料看成k個長度為1的已排好序的表,將相鄰的表成對合併,得到長度為2(k/2)個有序表,每個表含有2個資料;進一步再將相鄰表成對合併,得到長度為4(k/4)個有序表……如此重複做下去,直到將所有資料均合併到乙個長度為k的有序表為止,從而完成了排序。圖1顯示了二路歸併排序的過程。

figure 1. 二路歸併排序過程

圖2中,假設使用函式merge()將兩個有序表進行歸併處理,假設將兩個待歸併的表分別儲存在陣列ab中,將其中乙個的資料安排在下標從m到n單元中,另乙個安排在下標從(n+1)h單元中,將歸併後得到的有序表存入到輔助陣列c中。歸併過程是依次比較這兩個有序表中相應的資料,按照「取小」原則複製到c中。

figure 2. 兩個有序表的歸併圖

函式merge()的功能只是歸併兩個有序表,在進行二路歸併的每一趟歸併過程中,能夠將多對相鄰的表進行歸併處理。接下來開始討論一趟的歸併,假設已經將陣列r中的n個資料分成成對長度為s的有序表,要求將這些錶兩兩歸併,歸併成一些長度為2s的有序表,並把結果置入輔助陣列r2中。如果n不是2s的整數倍,雖然前面進行歸併的表長度均為s,但是最後還是能再剩下一對長度都是s的表。在這個時候,需要考慮如下兩種情況。

剩下乙個長度為s的表和乙個長度小於s的表,由於上述的歸併函式merge()並不要求待歸併的兩個表必須長度相同,仍可將二者歸併,只是歸併後的表的長度小於其他表的長度2s

只剩下乙個表,它的長度小於或等於s,由於沒有另乙個表與它歸併,只能將它直接複製到陣列r2中,準備參加下一趟的歸併。

假設將兩個有序的子檔案(相當於輸入堆)放在同一向量中的相鄰位置上,位置是r[low…m]r[m+1…high]。可以先將它們合併到乙個區域性的暫存向量r1(相當於輸出堆)中,當合併完成後將r1複製回r[low…high]中。

合併過程

2. 動態申請r1

在兩路歸併過程中,r1是動態申請的,因為申請的空間會很大,所以需要判斷加入申請空間是否成功。二路歸併排序法的操作目的非常簡單,只是將待排序列中相鄰的兩個有序子串行合併成乙個有序序列。二路歸併排序法的具體演算法描述如下所示。

/* 已知r1[low…mid]和r1[mid+1…high]分別按關鍵字有序排列 */

/* 將它們合併成乙個有序序列,存放在r[low…high] */

void merge(recordtype r1, int low, int mid, int high, recordtype r)

else

++k;

}while (i <= mid)

while (j <= high)

}

在合併過程中,兩個有序的子表被遍歷了一遍,表中的每一項均被複製了一次。因此,合併的代價與兩個有序子表的長度之和成正比,該演算法的時間複雜度為o(n)

可以採用遞迴方法實現二路歸併排序,具體描述如下所示。

/* r1[low…high]經過排序後放在r[low…high]中,r2[low…high]為輔助空間 */

void mergesort(recordtype r1, int low, int high, recordtype r)

free(r2);

}

實現歸併排序的方法有兩種,分別是自底向上和自頂向下,具體說明如下所示。

自底向上的基本思想

自底向上的基本思想是,當第1趟歸併排序時,將待排序的檔案r[1…n]看作是n個長度為1的有序子檔案,然後將這些子檔案兩兩歸併。

所以當完成本趟歸併後,前[lgn]個有序子檔案長度為2,最後乙個子檔案長度仍為1

2趟歸併的功能是,將第1趟歸併所得到的[lgn]個有序的子檔案實現兩兩歸併。如此反覆操作,直到最後得到乙個長度為n的有序檔案為止。

上述每次歸併操作,都是將兩個有序的子檔案合併成乙個有序的子檔案,所以稱其為「二路歸併排序」。類似地還有k(k>2)路歸併排序。

2.一趟歸併演算法

在某趟歸併中,設各子檔案長度為length(最後乙個子檔案的長度可能小於length),則歸併前r[1…n]中共有n個有序的子檔案:r[1…length],r[length+1…2length],…,r[([n/length]−1)*length+1…n]

注意:呼叫歸併操作將相鄰的一對子檔案進行歸併時,必須對子檔案的個數可能是奇數、以及最後乙個子檔案的長度小於length這兩種特殊情況進行特殊處理。

使用c語言實現一趟歸併演算法的具體**如下所示。

void mergepass(seqlist r,int length)

//mergepass

3. 二路歸併排序演算法二路歸併排序具體**的演算法如下。

void mergesort(seqlist r)

自底向上的歸併排序演算法雖然效率較高,但可讀性較差。

用分治法進行自頂向下的演算法設計,這種形式更為簡潔。

分治法的3個步驟

設歸併排序的當前區間是r[low…high],分治法的3個步驟如下。

2. 具體演算法

void mergesortdc(seqlist r,int low,int high)

} //mergesortdc

例如,已知序列寫出採用歸併排序演算法排序的每一趟的結果。

歸併排序各趟的結果如下所示。

[26] [5] [77] [1] [61] [11] [59] [15] [48] [19]

[5 26] [1 77] [11 61] [15 59] [19 48]

[1 5 26 77 ] [11 15 59 61] [19 48]

[1 5 11 15 26 59 61 77] [19 48]

[1 5 11 15 19 26 48 59 61 77]

[1]張玲玲. 演算法學習與應用從入門到精通 [m]. 北京 : 人民郵電出版社, 2016.github:

L1和L2正則化的理解

摘錄自 正則化之所以能夠降低過擬合的原因在於,正則化是結構風險最小化的一種策略實現。給loss function加上正則化項,能使得新得到的優化目標函式h f normal,需要在f和normal中做乙個權衡 trade off 如果還像原來只優化f的情況下,那可能得到一組解比較複雜,使得正則項no...

L1和L2正則化的直觀理解

這部分內容將解釋 為什麼l1正則化可以產生稀疏模型 l1是怎麼讓係數等於零的 以及為什麼l2正則化可以防止過擬合 假設有如下帶l1正則化的損失函式 j j0 w w 1 j0 是原始的損失函式,加號後面的一項是l1正則化項,是正則化係數。注意到l1正則化是權值的 絕對值之和 j 是帶有絕對值符號的函...

正則化項L1和L2的區別

一 概括 l1和l2是正則化項,又叫做罰項,是為了限制模型的引數,防止模型過擬合而加在損失函式後面的一項。二 區別 1.l1是模型各個引數的絕對值之和。l2是模型各個引數的平方和的開方值。2.l1會趨向於產生少量的特徵,而其他的特徵都是0.因為最優的引數值很大概率出現在座標軸上,這樣就會導致某一維的...