淺入淺出資料結構(17) 有關排序演算法的分析

2022-04-28 15:30:12 字數 2597 閱讀 9071

這一篇博文我們將討論一些與排序演算法有關的定理,這些定理將解釋插入排序博文中提出的疑問:為什麼氣泡排序與插入排序總是執行同樣數量的交換操作,而選擇排序不一定?同時為講述高階排序演算法做鋪墊:高階排序為什麼會更快。

在討論相關定理之前,我們必須先掌握乙個與順序有關的概念:逆序數。

所謂逆序數,就是「逆序組合的個數」,假設我們希望的順序為從小到大(反之同理):

設有元素互異數列x0,x1,x2……xn-1,(元素互異即數列中任取兩數均不相等)從中任取兩數作為組合(xa,xb),若a(即xa在數列中位於xb之前)且xa>xb,則(xa,xb)是逆序組合,若aa

b,則(xa,xb)不是逆序組合。整個數列的逆序數即數列中所有逆序組合(xa,xb)的個數。

不難發現,若數列有n個數,則任取兩數的組合(xa,xb)共有n(n-1)/2個,n為元素個數。

下面我們來舉幾個計算逆序數的例子(依然假設我們希望的順序為從小到大):

1.設有數列1,2,3,4,5,因為從中任取兩數(xa,xb)且aa

b,即任一組合(xa,xb)均非逆序,所以數列1,2,3,4,5的逆序數為:逆序組合的個數=0

2.設有數列5,4,3,2,1,因為從中任取兩數(xa,xb)且aa>xb,即任一組合(xa,xb)均為逆序,所以數列1,2,3,4,5的逆序數為:逆序組合的個數=n(n-1)/2=5*4/2=10

2.設有數列2,3,4,5,1,從中任取兩數(xa,xb),aa>xb的組合有(2,1),(3,1),(4,1),(5,1)共4個,即逆序組合共4個,所以數列2,3,4,5,1的逆序數為:逆序組合的個數=4

從例1我們可以推出定理1:若數列為有序,則其逆序數為0。例1的計算過程即本定理的證明。

而從定理1我們又可以得出定理2:若數列非有序,則其逆序數必大於0。證明過程類似於定理1,略

從定理1和定理2我們又可以得出定理3:將數列從非有序轉換為有序(即排序)的過程,就是減少數列逆序數的過程。

明白了什麼是逆序數,以及排序演算法的「本質」之後,我們開始討論定理4:

設有元素互異數列x0,x

1,x2……x

n-1,其反數列為x

n-1,x

n-2,……x

0,則這兩個數列的逆序數之和必為n*(n-1)/2。

因為數列元素互異,所以任取兩數xa,xb且aa>xb,要麼xa

b。也就是說組合(xa,xb)若在原數列中非逆序,則必在反數列中逆序,反之則反。而元素個數為n的數列中組合(xa,xb)的個數,共為n*(n-1)/2個,任一組合要麼在原數列逆序,要麼在反數列逆序,所以原數列與反數列的逆序數之和即組合(xa,xb)的個數:n*(n-1)/2。

有了定理4,我們就可以給出定理5:

有n個互異元素的數列,其平均逆序數為n*(n-1)/4

從定理4可知,任一數列與其反數列的逆序數之和為n*(n-1)/2,所以任一數列的平均逆序數即兩者之和的一半n*(n-1)/2。

接下來是簡單的定理6:

對於元素互異數列,交換其中相鄰的兩個數xn,x

n+1的位置,則數列的逆序數要麼+1,要麼-1。

假設(xn,xn+1)為逆序,則交換兩者將使該組合變為非逆序,但不會影響其他組合如(xa,xn)(xn,xb)的順序,從而數列逆序數-1

假設(xn,xn+1)為非逆序,則交換兩者將使該組合變為逆序,但不會影響其他組合如(xa,xn)(xn,xb)的順序,從而數列逆序數+1

根據定理3:將數列從非有序轉換為有序(即排序)的過程,就是減少數列逆序數直至0的過程。

定理5:有n個互異元素的數列,其平均逆序數為n*(n-1)/4

定理6:對於元素互異數列,交換其中相鄰的兩個數xn,xn+1的位置,則數列的逆序數要麼+1,要麼-1。

我們可以得出定理7:

通過交換相鄰元素來完成排序的演算法,其平均時間複雜度為ω(n2)。

對於n元素互異的數列,其平均逆序數為n*(n-1)/4,而排序即減少逆序數至0的過程,因為交換相鄰元素最多使逆序數-1,所以必須有n*(n-1)/4次交換才能使數列逆序數為0,即交換相鄰元素的排序演算法平均需要執行n*(n-1)/4次交換操作,也就是說平均時間複雜度為ω(n*(n-1)/4),即ω(n2)。雖然我們一直強調元素互異看起來有所不妥,但排序演算法必然要可以處理元素互異情況,而且ω(n)也只是平均時間複雜度,沒有考慮任何具體情形。

至此,本篇博文對於排序演算法的定理就算是介紹完畢了。但有關排序演算法的分析尚未結束。

我們先來回答插入排序博文中的問題:為什麼氣泡排序和插入排序執行的交換操作總是一樣多。其原因如下:

這兩個演算法都是通過交換相鄰元素來排序的,而且它們都不會執行使數列逆序數+1的交換,也就是說它們每執行一次交換就會且只會使數列的逆序數-1。所以對於給定數列,若其逆序數為n,則兩者都將執行n次交換操作。

那麼,選擇排序有時候只需要更少的交換次數的原因也就出來了,因為選擇排序並不是通過交換相鄰元素來排序的演算法,比如數列5,4,3,2,1,選擇排序第一次「實質交換」後,數列變為了1,4,3,2,5,也就是說它這一次交換就使得數列的逆序數-7。所以選擇排序可以使用更少的「實質交換」來完成排序,當然這並不意味著選擇排序就會很快,原因不再贅述。

接下來,根據前面的定理(3、5、6)。我們可以推出本文最後乙個定理,也就是定理7的推理,也是所有高階排序演算法的根本:

要令排序演算法的時間複雜度低於o(n2),必須令演算法執行「遠距離的元素交換」,使得平均每次交換減少不止1逆序數。

淺入淺出資料結構(18) 希爾排序

而希爾排序就是 簡單地 將這個道理應用到了插入排序中,將插入排序小小的公升級了一下。那麼,希爾排序是怎麼將這個道理應用於插入排序的呢?我們先來回顧一下插入排序的 void insertionsort int a,unsigned int size 不難看出,在插入排序中,對於每乙個元素,我們都令其執...

淺入淺出資料結構(21) 合併排序

在講解合併排序之前,我們先來想一想下面這個問題如何解決 有兩個陣列a和b,它們都已各自按照從小到大的順序排好了資料,現在我們要把它們合併為乙個陣列c,且要求c也是按從小到大的順序排好,請問該怎麼做?這個問題非常容易解決,我們將a b和c都視為佇列,然後不斷比較a和b的首部,取出其中更小的資料出隊,然...

淺入淺出資料結構(16) 插入排序

從這一篇博文開始,我們將開始討論排序演算法。所謂排序演算法,就是將給定資料根據關鍵字進行排序,最終實現資料依照關鍵字從小到大或從大到小的順序儲存。而這篇博文,就是要介紹一種簡單的排序演算法 插入排序 insertion sort 為了使精力專注於排序演算法本身,而不是對資料的分析 處理,若無特殊說明...