算法分析(2)經(jīng)典排序算法對(duì)比

概述

上一篇文章分析了一下基本的排序算法以及Java的實(shí)現(xiàn)院促,不過沒有比較深入的去分析醋虏,因?yàn)閷?duì)于O(n^2)的算法實(shí)現(xiàn)比較簡單,但是對(duì)于O(nLogn)的算法本身有些復(fù)雜谭胚,所以就分為兩篇文章來寫徐块。評(píng)價(jià)算法的標(biāo)準(zhǔn)有很多,時(shí)間復(fù)雜度灾而,空間復(fù)雜度以及穩(wěn)定性等等胡控。下面從兩個(gè)方面來對(duì)經(jīng)典排序算法進(jìn)行總結(jié)一下:

正文

時(shí)間復(fù)雜度

時(shí)間復(fù)雜度分類

經(jīng)典排序算法的時(shí)間復(fù)雜度大致可以分為以上兩種,下面來通過一個(gè)表格來看一下:

對(duì)比 O(n^2) O(nLogn) 快的倍數(shù)
n = 10 100 100 3
n=100 10000 664 15
n=1000 10^6 9966 100
n=10000 10^8 132877 753
n=100000 10^10 1660964 6020

當(dāng)數(shù)據(jù)量比較小的時(shí)候旁趟,O(nLogn)的優(yōu)勢并不明顯昼激,當(dāng)數(shù)據(jù)量越來越大的時(shí)候,優(yōu)勢才更加明顯

先寫一個(gè)測試的通用的工具類SortUtils

public class SortUtils {
// 生成有n個(gè)范圍在[rangeL, rangeR]的數(shù)組
static Integer[] generateRandomArray(int n, int rangeL, int rangeR) {
    assert rangeL <= rangeR;
    Integer[] arr = new Integer[n];
    for (int i = 0; i < n; i++)
  arr[i] = (int) (Math.random() * (rangeR - rangeL + 1) + rangeL);
    return arr;
}

// 生成一個(gè)有序數(shù)組, 之后隨機(jī)交換swapTimes對(duì)
static Integer[] generateOrderedArray(int n, int swapTimes) {
    Integer[] arr = new Integer[n];
    for (int i = 0; i < n; i++)
        arr[i] = i;
    for (int i = 0; i < swapTimes; i++) {
        int a = (int) (Math.random() * n);
        int b = (int) (Math.random() * n);
        int t = arr[a];
        arr[a] = arr[b];
        arr[b] = t;
    }
    return arr;
}
}

通過反射測試排序算法:

 // 通過反射獲取調(diào)用相應(yīng)的方法
static void testSort(String sortClassName, Comparable[] arr) {
    // 通過Java的反射機(jī)制锡搜,通過排序的類名橙困,運(yùn)行排序函數(shù)
    try {
        // 通過sortClassName獲得排序函數(shù)的Class對(duì)象
        Class sortClass = Class.forName(sortClassName);
        // 通過排序函數(shù)的Class對(duì)象獲得排序方法
        Method sortMethod = sortClass.getMethod("sort", new Class[]{Comparable[].class});
        // 排序參數(shù)只有一個(gè),是可比較數(shù)組arr
        Object[] params = new Object[]{arr};
        long startTime = System.currentTimeMillis();
        // 調(diào)用排序函數(shù)
        sortMethod.invoke(null, params);
        long endTime = System.currentTimeMillis();
        System.out.println(sortClass.getSimpleName() + " : " + (endTime - startTime) + "ms");
    } catch (Exception e) {
        e.printStackTrace();
    }
}

O(n^2)排序算法

編寫測試方法

    Integer[] arr1 = SortUtils.generateRandomArray(N, 0, 20000);
    Integer[] arr2 = Arrays.copyOf(arr1, arr1.length);
    Integer[] arr3 = Arrays.copyOf(arr1, arr1.length);
    Integer[] arr4 = Arrays.copyOf(arr1, arr1.length);
    SortUtils.testSlow("com.wustor.slow.SelectionSort", arr1);
    SortUtils.testSlow("com.wustor.slow.InsertionSort", arr2);
    SortUtils.testSlow("com.wustor.slow.BubbleSort", arr3);
    SortUtils.testSlow("com.wustor.ShellSort", arr4);
測試隨機(jī)數(shù)據(jù)

500條數(shù)據(jù):

SelectionSort : 7ms
InsertionSort : 14ms
BubbleSort : 14ms
ShellSort : 0ms

1000條數(shù)據(jù):

SelectionSort : 18ms
InsertionSort : 16ms
BubbleSort : 24ms
ShellSort : 1ms

5000條數(shù)據(jù):

SelectionSort : 75ms
InsertionSort : 164ms
BubbleSort : 195ms
ShellSort : 9ms

20000條數(shù)據(jù):

SelectionSort : 1265ms
InsertionSort : 1361ms
BubbleSort : 2999ms
ShellSort : 22ms

總結(jié)一下

隨機(jī)數(shù)據(jù) 500條 1000條 5000條 20000條
SelectionSort 7ms 18ms 75ms 1265ms
InsertionSort 14ms 16ms 164ms 1361ms
BubbleSort 14ms 24ms 195ms 2999ms
ShellSort 0ms 1ms 9ms 22ms
測試有序數(shù)據(jù)

500條數(shù)據(jù):

SelectionSort : 11ms
InsertionSort : 2ms
BubbleSort : 1ms
ShellSort : 3ms

1000條數(shù)據(jù):

SelectionSort : 6ms
InsertionSort : 10ms
BubbleSort : 10ms
ShellSort : 1ms

5000條數(shù)據(jù):

SelectionSort : 98ms
InsertionSort : 8ms
BubbleSort : 82ms
ShellSort : 3ms

20000條數(shù)據(jù):

SelectionSort : 624ms
InsertionSort : 28ms
BubbleSort : 991ms
ShellSort : 20ms

總結(jié)一下

有序數(shù)據(jù) 500條 1000條 5000條 20000條
SelectionSort 11ms 6ms 98ms 624ms
InsertionSort 2ms 10ms 8ms 28ms
BubbleSort 1ms 24ms 82ms 991ms
ShellSort 3ms 1ms 3ms 20ms

通過對(duì)比可以發(fā)現(xiàn)耕餐,不管是測試有序還是無序的數(shù)據(jù)纷宇,希爾排序都是效率最高的。

O(nlogn)排序算法

編寫測試方法

     public static void main(String[] args) {
        int T = 1000000;
        int N = 20000;
        // 比較 HeapSort蛾方、Shell Sort 和 Merge Sort 和 三種 Quick Sort 的性能效率
         Integer[] arr1 = SortUtils.generateRandomArray(T, 0, N);
       // Integer[] arr1 = SortUtils.generateOrderedArray(T, N);
        Integer[] arr2 = Arrays.copyOf(arr1, arr1.length);
        Integer[] arr3 = Arrays.copyOf(arr1, arr1.length);
        Integer[] arr4 = Arrays.copyOf(arr1, arr1.length);
        long time1 = SortUtils.testFast("com.wustor.ShellSort", arr1);
        long time2 = SortUtils.testFast("com.wustor.fast.HeapSort", arr2);
        long time3 = SortUtils.testFast("com.wustor.fast.MergeSort", arr3);
        long time4 = SortUtils.testFast("com.wustor.fast.QuickSort", arr4);
        System.out.println("Sorting " + N + " elements " + T + " times. Calculate theRun Time.");
        System.out.println("Shell Sort       Run Time: " + time1 + " ms");
        System.out.println("Heap  Sort       Run Time: " + time2 + " ms");
        System.out.println("Merge Sort       Run Time: " + time3 + " ms");
        System.out.println("Quick Sort       Run Time: " + time4 + " ms");

    }
測試隨機(jī)數(shù)據(jù)

因?yàn)橹挥袛?shù)據(jù)量涉及到幾十萬甚至上百萬的時(shí)候,才能體體現(xiàn)出O(nlogn)的優(yōu)勢
10000條數(shù)據(jù):

Shell Sort       Run Time: 0 ms
Heap  Sort       Run Time: 0 ms
Merge Sort       Run Time: 93 ms
Quick Sort       Run Time: 0 ms

20000條數(shù)據(jù):

Shell Sort       Run Time: 16 ms
Heap  Sort       Run Time: 15 ms
Merge Sort       Run Time: 0 ms
Quick Sort       Run Time: 0 ms

100000條數(shù)據(jù):

Shell Sort       Run Time: 75 ms
Heap  Sort       Run Time: 44 ms
Merge Sort       Run Time: 110 ms
Quick Sort       Run Time: 62 ms

1000000條數(shù)據(jù):

Shell Sort       Run Time: 1062 ms
Heap  Sort       Run Time: 734 ms
Merge Sort       Run Time: 281 ms
Quick Sort       Run Time: 282 ms

測試隨機(jī)數(shù)據(jù)

隨機(jī)數(shù)據(jù) 10000條 20000條 100000條 1000000條
Shell Sort 0ms 16ms 75ms 1062ms
Heap Sort 0ms 15ms 44ms 734ms
Merge Sort 93ms 0ms 110ms 281ms
Quick Sort 0ms 0ms 62ms 282ms
測試有序數(shù)據(jù)

10000條數(shù)據(jù):

Shell Sort       Run Time: 0 ms
Heap  Sort       Run Time: 0 ms
Merge Sort       Run Time: 16 ms
Quick Sort       Run Time: 0 ms

20000條數(shù)據(jù):

Shell Sort       Run Time: 16 ms
Heap  Sort       Run Time: 0 ms
Merge Sort       Run Time: 15 ms
Quick Sort       Run Time: 16 ms

100000條數(shù)據(jù):

Shell Sort       Run Time: 77 ms
Heap  Sort       Run Time: 52 ms
Merge Sort       Run Time: 62 ms
Quick Sort       Run Time: 31 ms

1000000條數(shù)據(jù):

Shell Sort       Run Time: 1086 ms
Heap  Sort       Run Time: 606 ms
Merge Sort       Run Time: 302 ms
Quick Sort       Run Time: 257 ms

測試有序數(shù)據(jù)

有序數(shù)據(jù) 10000條 20000條 100000條 1000000條
Shell Sort 0ms 16ms 77ms 1086ms
Heap Sort 0ms 0ms 52ms 606ms
Merge Sort 16ms 15ms 62ms 302ms
Quick Sort 0ms 16ms 31ms 282ms

對(duì)比分析上陕,發(fā)現(xiàn)快排不管是對(duì)于有序的還是無序的數(shù)組桩砰,排序效率都比較高。

穩(wěn)定性

定義:假定在待排序的記錄序列中释簿,存在多個(gè)具有相同的關(guān)鍵字的記錄亚隅,若經(jīng)過排序,這些記錄的相對(duì)次序保持不變庶溶,即在原序列中煮纵,ri=rj懂鸵,且ri在rj之前,而在排序后的序列中行疏,ri仍在rj之前匆光,則稱這種排序算法是穩(wěn)定的;否則稱為不穩(wěn)定的酿联。
根據(jù)這個(gè)定義來分析一下經(jīng)典排序算法:

  • 簡單選擇排序:由于需要交換元素的位置终息,所以有可能會(huì)破壞原來的順序,比如說7571贞让,第一次交換之后周崭,兩個(gè)7之間的順序就調(diào)換了。不穩(wěn)定
  • 堆排序:在數(shù)據(jù)結(jié)構(gòu)分析之二叉樹中分析過二叉堆的數(shù)據(jù)結(jié)構(gòu)喳张,在堆排序之前需要將數(shù)組是生成一個(gè)二差堆续镇,如果給定的二叉堆是數(shù)組是37556,那么進(jìn)行生成二叉堆的時(shí)候?qū)哟伪闅v是35756销部,兩個(gè)5相對(duì)順序不變摸航,但是取出最小值的時(shí)候,底部的元素進(jìn)行上濾操作柴墩,后面的5會(huì)置頂忙厌,相對(duì)順序被破壞。不穩(wěn)定
  • 直接插入排序:只有當(dāng)后面的元素大于前面的元素的時(shí)候才會(huì)進(jìn)行交換江咳,所以不會(huì)破壞相同元素的順序逢净。穩(wěn)定
  • 希爾排序:分組的插入排序,雖然插入排序都是穩(wěn)定的歼指,但是每列之間的元素在排序之間會(huì)相互移動(dòng)爹土,這樣就有可能導(dǎo)致相同元素之間的位置發(fā)生變化。不穩(wěn)定
  • 冒泡排序:第一個(gè)比第二個(gè)大踩身,才進(jìn)行交換胀茵,所以元素相同的元素之間不會(huì)進(jìn)行交換,相對(duì)位置也就不會(huì)發(fā)生變化挟阻。穩(wěn)定
  • 快速排序:在分組的時(shí)候很容易把兩個(gè)元素相同的位置進(jìn)行交換琼娘,對(duì)于6225,分組的時(shí)候22之間的順序會(huì)被打亂附鸽。不穩(wěn)定
  • 歸并排序:分組進(jìn)行遞歸脱拼,遞歸到最后,每一個(gè)數(shù)組都是有序的坷备,合并的時(shí)候熄浓,也是從左到右的,相同的元素不會(huì)進(jìn)行交換省撑,所以可以保證穩(wěn)定性赌蔑。穩(wěn)定

下面用圖片總結(jié)一下


算法穩(wěn)定性分類

對(duì)比

算法 最大時(shí)間 平均時(shí)間 最小時(shí)間 輔助空間 穩(wěn)定性
插入排序 O(n^2) O(n^2) O(n) O(1) 穩(wěn)定
冒泡排序 O(n^2) O(n^2) O(n^2) O(1) 穩(wěn)定
選擇排序 O(n^2) O(n^2) O(n^2) O(1) 不穩(wěn)定
希爾排序 O(n^(3/2)) O(n^(3/2)) O(n^(3/2)) O(1) 不穩(wěn)定
快速排序 O(n^2) O(nlogn) O(nlogn) O(logn) 不穩(wěn)定
歸并排序 O(nlogn) O(nlogn) O(nlogn) O(n) 穩(wěn)定
堆排序 O(nlogn) O(nlogn) O(nlogn) O(1) 不穩(wěn)定

排序方法的選擇

數(shù)據(jù)較小

有序
可采用直接插入排序俯在。因?yàn)椴迦肱判蛐蚀藭r(shí)效率最高。
無序
采用直接插入排序或者直接選擇排序

數(shù)據(jù)較大

快速排序在完全有序的情況下娃惯,會(huì)退化到O(n^2)級(jí)別跷乐,效率較低
有序
則應(yīng)采用時(shí)間復(fù)雜度為 O(nLogn)的排序方法:堆排序或歸并排序
無序
則應(yīng)采用時(shí)間復(fù)雜度為 O(nLogn)的排序方法:快速排序、堆排序或歸并排序

以上選擇是在對(duì)排序算法穩(wěn)定性沒有要求的情況下進(jìn)行選擇的石景,如果需要排序穩(wěn)定劈猿,則需要剔除不穩(wěn)定的算法,在排序穩(wěn)定的算法里面進(jìn)行選擇即可潮孽。

源碼下載

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末揪荣,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子往史,更是在濱河造成了極大的恐慌仗颈,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,348評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件椎例,死亡現(xiàn)場離奇詭異挨决,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)订歪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門脖祈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人刷晋,你說我怎么就攤上這事盖高。” “怎么了眼虱?”我有些...
    開封第一講書人閱讀 156,936評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵喻奥,是天一觀的道長。 經(jīng)常有香客問我捏悬,道長撞蚕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,427評(píng)論 1 283
  • 正文 為了忘掉前任过牙,我火速辦了婚禮甥厦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘寇钉。我一直安慰自己矫渔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評(píng)論 6 385
  • 文/花漫 我一把揭開白布摧莽。 她就那樣靜靜地躺著,像睡著了一般顿痪。 火紅的嫁衣襯著肌膚如雪镊辕。 梳的紋絲不亂的頭發(fā)上油够,一...
    開封第一講書人閱讀 49,785評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音征懈,去河邊找鬼石咬。 笑死,一個(gè)胖子當(dāng)著我的面吹牛卖哎,可吹牛的內(nèi)容都是我干的鬼悠。 我是一名探鬼主播,決...
    沈念sama閱讀 38,931評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼亏娜,長吁一口氣:“原來是場噩夢啊……” “哼焕窝!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起维贺,我...
    開封第一講書人閱讀 37,696評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤它掂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后溯泣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體虐秋,經(jīng)...
    沈念sama閱讀 44,141評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評(píng)論 2 327
  • 正文 我和宋清朗相戀三年垃沦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了客给。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,625評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡肢簿,死狀恐怖靶剑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情译仗,我是刑警寧澤抬虽,帶...
    沈念sama閱讀 34,291評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站纵菌,受9級(jí)特大地震影響阐污,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜咱圆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評(píng)論 3 312
  • 文/蒙蒙 一笛辟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧序苏,春花似錦手幢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春监透,著一層夾襖步出監(jiān)牢的瞬間桶错,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國打工胀蛮, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留院刁,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓粪狼,卻偏偏與公主長得像退腥,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子再榄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 概述 排序有內(nèi)部排序和外部排序狡刘,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進(jìn)行排序,而外部排序是因排序的數(shù)據(jù)很大不跟,一次不能容納全部...
    蟻前閱讀 5,168評(píng)論 0 52
  • 排序算法說明 (1)排序的定義:對(duì)一序列對(duì)象根據(jù)某個(gè)關(guān)鍵字進(jìn)行排序颓帝; 輸入:n個(gè)數(shù):a1,a2,a3,…,an輸出...
    BULL_DEBUG閱讀 769評(píng)論 0 3
  • 慧魚閱讀 1,051評(píng)論 2 5
  • 今天看了電影《我的少女時(shí)代》,剛好這段時(shí)間有空而且電影終于免費(fèi)了窝革,可以重復(fù)觀看购城。對(duì)這部電影的記憶還挺深,掛在心里有...
    曉來櫻桃閱讀 204評(píng)論 0 0
  • 今年三月虐译,我來到這座小城 十多天了瘪板,雨一直下 春雨肯定窺視了這座小城的許多秘密 比你多得多吧 雖然你細(xì)數(shù)過每條小巷...
    如言閱讀 118評(píng)論 0 0