1. 程式人生 > >數據結構與算法學習筆記之如何分析一個排序算法?

數據結構與算法學習筆記之如何分析一個排序算法?

編號 height href eight 代碼 [] www. 價值 它的

前言

現在IT這塊找工作,不會幾個算法都不好意思出門,排序算法恰巧是其中最簡單的,我接觸的第一個算法就是它,但是你知道怎麽分析一個排序算法麽?有很多時間復雜度相同的排序算法,在實際編碼中,那又如何選擇呢?下面我們帶著問題一起學習一下。

正文

一、常見經典的排序方法

(圖片來自於一像素)

插入排序

技術分享圖片 希爾排序(遞減增量排序算法) 技術分享圖片

歸並排序

技術分享圖片

快速排序

技術分享圖片

冒泡排序

技術分享圖片

選擇排序

技術分享圖片

計數排序

技術分享圖片

計數排序

技術分享圖片

堆排序

技術分享圖片

二、 按照時間復雜度歸類

時間復雜度O(n2): 冒泡排序、插入排序、選擇排序 時間復雜度O(nlogn):

快速排序、歸並排序 時間復雜度O(n): 計數排序、基數排序、桶排序

三、如何分析一個“排序算法”?

從三個方面入手

a、算法的執行效率

1.最好、最壞、平均情況時間復雜度。 從算法的核心,復雜度入手,給出最好最壞,平均情況下的時間復雜度,便於分析
2. 時間復雜度的系數、常數和低階。 時間復雜度表示的是規模很大的一種增漲趨勢,很容易就忽略系數,低階,常數等,實際開發中排序的規模都是像10.100.1000這種小規模
3. 比較次數,交換(或移動)次數。 排序算法執行過程中,涉及兩種操作,一種是元素比較大小,一種是元素交換或移動位置,所以比較次數,交換次數都得考慮進去。

b、排序算法的內存消耗

算法消耗可以通過空間復雜度來衡量

原地排序算法:特指空間復雜度是O(1)的排序算法。

c、排序算法的穩定性

1. 穩定性概念:如果待排序的序列中存在值相等的元素,經過排序之後,相等元素之間原有的先後順序不變。
2. 穩定性重要性:可針對對象的多種屬性進行有優先級的排序。
3. 舉例:給電商交易系統中的“訂單”排序,按照金額大小對訂單數據排序,對於相同金額的訂單以下單時間早晚排序。用穩定排序算法可簡潔地解決。先按照下單時間給訂單排序,排序完成後用穩定排序算法按照訂單金額重新排序。


四、詳解冒泡排序

冒泡排序只會操作相鄰的兩個數據。每次冒泡操作都會對相鄰的兩個元素進行比較,看是否滿足大小關系要求,如果不滿足就讓它倆互換。
冒泡排序只涉及相鄰數據的交換,只需要常量級的臨時空間,所以它的空間復雜度未O(1)是原地排序算法 穩定性:當有相鄰的兩個元素大小相等時,不做交換,冒泡排序是穩定的排序算法。 引入兩個概念:
默認從小到大未有序
有序度:數組中具有有序關系的元素對的個數。 滿有序度:完全有序的數組 逆序度:數組中具有無序關系的元素對的個數。
逆序度=滿有序度-有序度 排序的過程實際上就是增加有序度,減少逆序度的過程
時間復雜度:
1. 最好情況(滿有序度):O(n)。
2. 最壞情況(滿逆序度):O(n^2)。
3. 平均情況:
“有序度”和“逆序度”:對於一個不完全有序的數組,如4,5,6,3,2,1,有序元素對為3個(4,5),(4,6),(5,6),有序度為3,逆序度為12;對於一個完全有序的數組,如1,2,3,4,5,6,有序度就是n*(n-1)/2,也就是15,稱作滿有序度;逆序度=滿有序度-有序度;冒泡排序、插入排序交換(或移動)次數=逆序度。
最好情況下初始有序度為n*(n-1)/2,最壞情況下初始有序度為0,則平均初始有序度為n*(n-1)/4,即交換次數為n*(n-1)/4,因交換次數<比較次數<最壞情況時間復雜度,所以平均時間復雜度為O(n2)。 代碼實現:
// 冒泡排序,a 表示數組,n 表示數組大小
public void bubbleSort(int[] a, int n) {
  if (n <= 1) return;
 
 for (int i = 0; i < n; ++i) {
    // 提前退出冒泡循環的標誌位
    boolean flag = false;
    for (int j = 0; j < n - i - 1; ++j) {
      if (a[j] > a[j+1]) { // 交換
        int tmp = a[j];
        a[j] = a[j+1];
        a[j+1] = tmp;
        flag = true;  // 表示有數據交換      
      }
    }
    if (!flag) break;  // 沒有數據交換,提前退出
  }
}

五、詳解插入排序

將數據分為兩個區間,已排序區間和未排序區間,初始已排序區間只有一個元素(即第一個數據),我們取未排序區間的元素,在已排序的區間中找到合適的位置插入位置插入,並保證已排序區間數據一直有序,重復過程,直到未排序區間中沒有元素

運行過程中看得出來,不需要額外的存儲空間,所以空間復雜度為0(1),也是原地排序算法

同樣值的元素,前後順序保持不變,是穩定的排序算法

時間復雜度:

最好時間復雜度為O(n)

最壞時間復雜度為O(n2)

平均時間復雜度為O(n2)

代碼實現:

// 插入排序,a 表示數組,n 表示數組大小
public void insertionSort(int[] a, int n) {
  if (n <= 1) return;

  for (int i = 1; i < n; ++i) {
    int value = a[i];
    int j = i - 1;
    // 查找插入的位置
    for (; j >= 0; --j) {
      if (a[j] > value) {
        a[j+1] = a[j];  // 數據移動
      } else {
        break;
      }
    }
    a[j+1] = value; // 插入數據
  }
}

六、詳解選擇排序

選擇排序將數組數據分成已排序區間和未排序區間。初始已排序區間只有一個元素,即數組第一個元素。在未排序區間找到最小的數據,將其放在已排序區間的末尾
空間復雜度為O(1),選擇排序是原地排序算法。 未排序區間的元素和已排序區間的元素相同時,它可以放在已排序區間相同值的前或後,所以為不穩定的排序
時間復雜度:
1. 最好情況:O(n2)。
2. 最壞情況:O(n2)。
3. 平均情況:O(n2)(往數組中插入一個數的平均時間復雜度是O(n),一共重復n次)。

七、各種排序方法的匯總比較

技術分享圖片

八、選擇排序和插入排序的時間復雜度相同,都是O(n^2),在實際的軟件開發中,為什麽我們更傾向於使用插入排序而不是冒泡排序算法呢?

答:它們的元素比較次數以及交換元素的次數都是原始數據的逆序度,是一個固定值,但是從代碼實現上來看,冒泡排序的數據交換要比插入排序的數據移動要復雜,冒泡排序需要3個賦值操作,而插入排序只需要1個,他們 的時間復雜度上都是O(n2),但是為了追求極致的性能,所以首選插入排序算法

結尾

大家不妨試著分析一下其他的幾種算法。

看再多遍都不如寫一篇來得深刻,建議大家多敲。

相關文章

數據結構與算法學習筆記之寫鏈表代碼的正確姿勢(下)

數據結構與算法學習筆記之 提高讀取性能的鏈表(上)

數據結構與算法學習筆記之 從0編號的數組

數據結構與算法學習筆記之後進先出的“桶”

數據結構與算法學習筆記之先進先出的隊列

數據結構與算法學習筆記之高效、簡潔的編碼技巧“遞歸”

以上內容為個人的學習筆記,僅作為學習交流之用。

技術分享圖片

歡迎大家關註公眾號,不定時幹貨,只做有價值的輸出

作者:Dawnzhang
出處:https://www.cnblogs.com/clwydjgs/p/9815690.html

版權:本文版權歸作者
轉載:歡迎轉載,但未經作者同意,必須保留此段聲明;必須在文章中給出原文連接;否則必究法律責任

數據結構與算法學習筆記之如何分析一個排序算法?