由排序演算法引出的資料結構

2021-05-28 13:28:52 字數 2458 閱讀 7801

由排序演算法引出的資料結構

本文較為深入地研究了各類基本的排序演算法,並由此引伸到對於資料結構的認識。

怎樣選擇演算法?哪種排序演算法最好?要回答這些問題首先要建立在資料特徵和對資料的操作要求上,根據不同的資料特徵和操作要求選擇合適的資料儲存結構。

資料儲存結構分類如下:

(1)順序儲存(2)鍊錶結構(3)索引(4)雜湊表

對於順序表和煉表,實際應用中又分棧、佇列、數、圖等。

再回頭來看排序演算法:

直插、希爾、直選、堆、冒泡、快排、歸併、基數排序。為o(n2)的是直插、直選和冒泡。直選的效率最低,其次是冒泡,直插比較好而且穩定,但是若用在順序表上會大量挪動記錄,插入和刪除操作是順序表上無法克服的弱點。直選和冒泡相比,直選比較的次數較多但是最多交換n-2次,而冒泡交換記錄的次數稍微多一點,但是冒泡可改進:採用提前終止位置和交替掃瞄方向能使冒泡適應複雜多樣的資料。但是效率的提公升也是有限的,例如我用2萬個整數去測試時,基本的冒泡用了2.428秒,設定了flag的用了2.279秒,提前終止位置的用了2.153秒,雙向掃瞄用了1.652秒,相對於快排的零點幾秒和堆排序的零點零零幾秒,完全不在乙個數量級。

快排速度最快但是不穩定而且只適合順序表,它用兩個指標i、j左右移動,交換較小的元素到基準的前端,交換量取決於資料的初始有序情況,但我估計它的平均交換量小於改進的氣泡排序。快排有乙個缺點就是資料越有序,那麼它的效率越差,這是因為每次都是選首元素為基準的緣故。現實中有排過序的檔案稍微打亂了一下順序,要再次排序的情況,這時是基本有序,快排就比較吃力。這是就應該改進一下快排演算法,取中間元素作為基準,但是又為了避免運氣黴,抽到的中間那個元素偏偏是極大或者極小的情況,所以用首、中、尾三者取中作為基準的辦法。但是快速排序除了不穩定和只能對順序表進行排序外,還有乙個很大的缺點就是空間開銷大,因為它是用遞迴演算法寫的,需要在記憶體中開闢遞迴棧空間,最壞情況下空間開銷為o(n)。當我測試快排的時候遇到了遞迴棧空間不足的情況,vc預設給棧空間開闢的記憶體最大為2~8m,我用最原始的快排演算法,元素個數約為20w個時遞迴棧空間就不足了,用首、中、尾三者取中的改進的快排,記憶體開銷更大,大約只能排3萬個元素。而且排序的速度上快排並不是最快,要比堆排序慢很多。快速排序的最壞時間複雜度為o(n2),平均時間複雜度和最壞時間複雜度均為nlg(n)。

希爾排序其實就是分段的插入排序,定義乙個初始增量d,然後依次減小d使得每次直插比較的元素增多,但是d變得越小,也就意味著元素變得越來越有序,就有利於直插排序。其交換量應該還是蠻大的,我估計與改進冒泡的交換量相當或者稍好。

堆排序是一種樹形(完全二叉樹)的選擇排序,效果非常好,當資料量較大時(比如1000個)比快速排序要快很多,但是也不穩定。由於建初始堆所需要的比較次數較多,所以堆排序不適合記錄數較少的情況。堆排序的「最壞時間複雜度」為nlg(n)(由此可見它比快排要快),而且堆排序的空間開銷要小於快排。

總之,若順序儲存,假設存的是長度小於16的字串,個數為1000或者10w,則用堆排序或者快排,因為如果用希爾排序或者改進的冒泡演算法,則要多次挪動或交換資料,拷貝字串(strcpy)也是要挺大的開銷吧?但是如果比較的是數量少於300個,那就用直選吧,因為拷貝量大,不宜用希爾排序和氣泡排序以及快速排序,而且資料量確實很小,用快排好像效率也不算高,但如果比較的不是長度為16的字串而是簡單的4位整數,那顯然用希爾排序,移動的資料量不大而且演算法又穩定,但如果是4位的小數呢,用基數排序?還是快排、希爾、堆?沒說過還真不知道!因為我不知道系統完成2>4?與2.125>2.112?的效率之間的差別,也不知道交換一對整數的值和交換一對實數的值時系統開銷的差別,倘若整數之間的比較比實數之間的比較要麻煩很多,而且整數之間的賦值要比實數之間的賦值速度慢、開銷大,則也許用基數排序才是王道!否則,類似於上面的16位字串,用堆排序或者快排!

對於鍊錶結構,鍊錶本身就有很多學問:是動態鍊錶還是靜態鍊錶?是單鏈表還是雙鏈表?是棧還是佇列?是二叉樹還是紅黑樹還是其他神馬樹甚至圖?

於是乎,在此我們只以完全二叉樹為例,則用堆排序較好,也可以用插入排序。如果是雙鏈表則用插入排序吧,如果是其他鍊錶結構,不外乎也是與插入排序和堆排序類似的方法,但是會針對某一種結構優化演算法,例如「敗者樹」「置換-選擇排序」神馬的!

綜上可知,最沒用的是氣泡排序,直選有一點點用處(因為它記錄移動次數少),直插(希爾)、快排和堆排序最有用了,特別是堆排序,速度最給力了,其思想適合用在樹形結構上,分配排序(基數排序)也許有用,雖然打著o(n)的幌子,但是據我分析,其效率一般不及快排,除非你n上幾十億並且關鍵字的位數d、箱子的個數rd都很小(否則d*rd會大於lg(n),lg(n)(2為底的對數)其實是乙個很小的數,當n=1000w時,lg(n)才約為23)。而歸併排序只是一種多檔案歸併的方法,實際上還是以內部排序(例如直插)為主,通常(直插+歸併)搞定要求排序穩定的大鍊錶檔案。

最後分析一下資料結構,首先要熟練掌握(甚至精通)的是(單/雙鏈表、二叉樹,包括在它上面的一些基本操作,必須熟練)。對於排序演算法,要熟練掌握(甚至精通)的是(直插、希爾,直選、堆,快排)。

補充一點:實際應用中,雜湊表查詢演算法是很有用,其o(n)的時間複雜度非常高效,例如輸入法,對於查詢演算法的要求非常苛刻,二叉查詢和樹查詢演算法根本達不到要求。

由排序演算法引出的資料結構

由排序演算法引出的資料結構 本文較為深入地研究了各類基本的排序演算法,並由此引伸到對於資料結構的認識。怎樣選擇演算法?哪種排序演算法最好?要回答這些問題首先要建立在資料特徵和對資料的操作要求上,根據不同的資料特徵和操作要求選擇合適的資料儲存結構。資料儲存結構分類如下 1 順序儲存 2 鍊錶結構 3 ...

資料結構 排序演算法

include include define maxitem 100 typedef char keytype 5 typedef int elemtype typedef struct rec elemnode maxitem 氣泡排序演算法 void bubblesort elemnode r,...

資料結構 排序演算法

排序演算法分為內部排序和外部排序兩大類。內部排序 在計算機記憶體中完成的排序演算法 外部排序 不能再記憶體中文完成,必須在磁碟或者磁帶上完成的排序演算法 內部排序是研究的重點問題,通常我們講的八大排序演算法也主要是講的內部排序演算法。排序演算法的穩定性和時間空間複雜度 本文重點介紹以下幾種排序演算法...