天天看點

排序算法 時間、空間複雜度

概念

1、時間複雜度

    (1)時間頻度 一個算法執行所耗費的時間,從理論上是不能算出來的,必須上機運作測試才能知道。但我們不可能也沒有必要對每個算法都上機測試,隻需知道哪個算法花費的時間多,哪個算法花費的時間少就可以了。并且一個算法花費的時間與算法中語句的執行次數成正比例,哪個算法中語句執行次數多,它花費時間就多。一個算法中的語句執行次數稱為語句頻度或時間頻度, 記為t(n)。

    (2)時間複雜度 在剛才提到的時間頻度中,n稱為問題的規模,當n不斷變化時,時間頻度t(n)也會不斷變化。但有時我們想知道它變化時呈現什麼規律。為此,我們引入時間複雜度概念。 一般情況下,算法中基本操作重複執行的次數是問題規模n的某個函數,用t(n)表示,若有某個輔助函數f(n),使得當n趨近于無窮大時,t(n)/f(n)的極限值為不等于零的常數,則稱f(n)是t(n)的同數量級函數。記作t(n)=O(f(n)),稱O(f(n)) 為算法的漸進時間複雜度,簡稱時間複雜度。

        在各種不同算法中,若算法中語句執行次數為一個常數,則時間複雜度為o(1),不發生交換時間複雜度為o(0)。另外,在時間頻度不相同時,時間複雜度有可能相同,如t(n)=n2+3n+4與t(n)=4n2+2n+1它們的頻度不同,但時間複雜度相同,都為o(n2)。

        常見的時間複雜度,按數量級遞增排列依次為:常數階o(1)、對數階o(log2n)、線性階o(n)、線性對數階o(nlog2n)、平方階o(n^2)、立方階o(n^3)、k次方階o(n^k)、指數階o(2^n)。随着問題規模n的不斷增大,上述時間複雜度不斷增大,算法的執行效率越低。

2、空間複雜度

        空間複雜度(space complexity)是對一個算法在運作過程中臨時占用存儲空間大小的量度。一個算法在計算機存儲器上所占用的存儲空間,包括存儲算法本身所占用的存儲空間,算法的輸入輸出資料所占用的存儲空間和算法在運作過程中臨時占用的存儲空間這三個方面。算法的輸入輸出資料所占用的存儲空間是由要解決的問題決定的,是通過參數表由調用函數傳遞而來的,它不随本算法的不同而改變。存儲算法本身所占用的存儲空間與算法書寫的長短成正比,要壓縮這方面的存儲空間,就必須編寫出較短的算法。

        空間複雜度并不是指所有的資料所占用的空間,而是使用的輔助空間的大小,比如兩個矩陣的運算,在中間設定了一個中間矩陣來儲存一些資料,這些空間叫做空間複雜度。空間複雜度的運算非常麻煩,一般簡單的算法空間複雜度都是o(1),比較複雜的會告知空間複雜度,記住就好了。

3、算法的穩定性

        百度解釋:假定在待排序的記錄序列中,存在多個具有相同的關鍵字的記錄,若經過排序,這些記錄的相對次序保持不變,即在原序列中,ri=rj,且ri在rj之前,而在排序後的序列中,ri仍在rj之前,則稱這種排序算法是穩定的;否則稱為不穩定的。

        穩定意思是說原本鍵值一樣的元素排序後相對位置不變,學習的時候,可能編的程式裡面要排序的元素都是簡單類型,實際上真正使用的時候,可能是對一個複雜類型的數組排序,而排序的鍵實際上隻是這個元素中的一個屬性,對于一個簡單類型,數字值就是其全部意義,即使交換了也看不出什麼不同。。。但是對于複雜的類型,交換的話可能就會使原本不應該交換的元素交換了。。

        比如,一個“學生”數組,按照年齡排序,“學生”這個對象不僅含有“年齡”,還有其他很多屬性,穩定的排序會保證比較時,如果兩個學生年齡相同,一定不交換。

        排序算法,是程式開發中最基本的一項任務。教科課上講的排序算法大概有7-8種,快速排序(quicksort)是使用最廣泛的一種基于比較排序的算法。

一 快速排序

        快速排序是由東尼·霍爾所發展的一種排序算法。在平均狀況下,排序 n 個項目要Ο(n log n)次比較。在最壞狀況下則需要Ο(n^2)次比較,但這種狀況并不常見。事實上,快速排序通常明顯比其他Ο(n log n) 算法更快,因為它的内部循環(inner loop)可以在大部分的架構上很有效率地被實作出來。快速排序使用分治法(divide and conquer)政策來把一個串行(list)分為兩個子串行(sub-lists)。

快速排序的思想很簡單,排序過程隻需要三步:

1. 從數列中挑出一個元素,稱為 “基準”(pivot)

2. 重新排序數列,所有元素比基準值小的擺放在基準前面,所有元素比基準值大的擺在基準的後面(相同的數可以到任一邊)。在這個分區退出之後,該基準就處于數列的中間位置。這個稱為分區(partition)操作。

3. 遞歸地(recursive)把小于基準值元素的子數列和大于基準值元素的子數列排序。

遞歸的最底部情形,是數列的大小是零或一,也就是永遠都已經被排序好了。雖然一直遞歸下去,但是這個算法總會退出,因為在每次的疊代(iteration)中,它至少會把一個元素擺到它最後的位置去。

        舉例來說,現在有一組資料[9,23,12,11,43,54,43,2,12,66],怎麼對其按從小到大順序排序呢?

排序算法 時間、空間複雜度

step1,選擇第一個元素9作為分隔值,把小于9的數字放左邊,大于等于9的數字放右邊。原來的一個數組就被分成了3個部分,左邊+分隔值+右邊=[2]+9+[23 12 11 43 54 43 12 66]

step2, 選擇step1數組左邊的第一進制素2作為分隔值,左邊隻有一個元素,左邊排序完成。選擇step1數組右邊的第一個元素23作為分隔值,把小于23的數字放左邊,大于等于23的數字放右邊,得到[ 12 11 12 ] 23 [ 43 54 43 66 ]。

step3,選擇step2數組以23分隔的左邊第一個元素12作為分隔值,把小于12的數字放左邊,大于等于12的數字放右邊,得到[ 11 ] 12 [ 12 ]。選擇step2數組以23分隔的左邊第一個元素43作為分割值,把小于43的數字放左邊,大于等于43的數字右邊,得到 [] 43 [ 54 43 66 ]。

step4,對step3數組以12分隔的左右兩邊進行排序,都隻有一個元素,排序完成。對step3數組以43分隔左右兩進行排序,左邊無元素,右邊第一個元素54作為分隔值,得到[ 43 ] 54 [ 66 ]。

step5,對step4數組以54分隔的左右兩邊進行排序,都隻有一個元素,排序完成。

最後,後整個數組拼接在一起就得到排序結果:[2 9 11 12 12 23 43 43 54 66 ]

我們看到快速排序,其實就是一種分而治之的辦法。網上還有很多快速排序的優化方法,可以盡一步減少時間複雜度和空間複雜度的開銷。

二 桶排序

        桶排序(bucket sort)是一種基于計數的排序算法,工作的原理是将資料分到有限數量的桶子裡,然後每個桶再分别排序(有可能再使用别的排序算法或是以遞回方式繼續使用桶排序進行排序)。當要被排序的資料内的數值是均勻配置設定的時候,桶排序時間複雜度為o(n)。桶排序不同于快速排序,并不是比較排序,不受到時間複雜度 o(nlogn) 下限的影響。

桶排序按下面4步進行:

1. 設定固定數量的空桶。

2. 把資料放到對應的桶中。

3. 對每個不為空的桶中資料進行排序。

4. 拼接從不為空的桶中資料,得到結果。

桶排序,主要适用于小範圍整數資料,且獨立均勻分布,可以計算的資料量很大,而且符合線性期望時間。

        舉例來說,現在有一組資料[7, 36, 65, 56, 33, 60, 110, 42, 42, 94, 59, 22, 83, 84, 63, 77, 67, 101],怎麼對其按從小到大順序排序呢?

排序算法 時間、空間複雜度

操作步驟說明:

1. 設定桶的數量為5個空桶,找到最大值110,最小值7,每個桶的範圍20.8=(110-7+1)/5 。

2. 周遊原始資料,以連結清單結構,放到對應的桶中。數字7,桶索引值為0,計算公式為floor((7 – 7) / 20.8), 數字36,桶索引值為1,計算公式floor((36 – 7) / 20.8)。

3. 當向同一個索引的桶,第二次插入資料時,判斷桶中已存在的數字與新插入數字的大小,按照左到右,從小到大的順序插入。如:索引為2的桶,在插入63時,桶中已存在4個數字56,59,60,65,則數字63,插入到65的左邊。

4. 合并非空的桶,按從左到右的順序合并0,1,2,3,4桶。

5. 得到桶排序的結構

繼續閱讀