1. 程式人生 > >[一步步學資料結構與演算法 11]-排序(上)

[一步步學資料結構與演算法 11]-排序(上)

一、排序方法與複雜度歸類

(1)幾種最經典、最常用的排序方法:氣泡排序、插入排序、選擇排序、快速排序、歸併排序、計數排序、基數排序、桶排序。 (2)複雜度歸類 氣泡排序、插入排序、選擇排序 O(n^2) 快速排序、歸併排序 O(nlogn) 計數排序、基數排序、桶排序 O(n)

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

<1>演算法的執行效率

  1. 最好、最壞、平均情況時間複雜度。
  2. 時間複雜度的係數、常數和低階。
  3. 比較次數,交換(或移動)次數。

<2>排序演算法的穩定性

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

<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(n^2)。

四、插入排序

插入排序將陣列資料分成已排序區間和未排序區間。初始已排序區間只有一個元素,即陣列第一個元素。在未排序區間取出一個元素插入到已排序區間的合適位置,直到未排序區間為空。 空間複雜度:插入排序是原地排序演算法。 時間複雜度:

  1. 最好情況:O(n)。
  2. 最壞情況:O(n^2)。
  3. 平均情況:O(n^2)(往陣列中插入一個數的平均時間複雜度是O(n),一共重複n次)。

穩定性:插入排序是穩定的排序演算法。

五、選擇排序

選擇排序將陣列分成已排序區間和未排序區間。初始已排序區間為空。每次從未排序區間中選出最小的元素插入已排序區間的末尾,直到未排序區間為空。 空間複雜度:選擇排序是原地排序演算法。 時間複雜度:(都是O(n^2))

  1. 最好情況:O(n^2)。
  2. 最壞情況:O(n^2)。
  3. 平均情況:O(n^2)。

穩定性:選擇排序不是穩定的排序演算法。

六、思考

選擇排序和插入排序的時間複雜度相同,都是O(n^2),在實際的軟體開發中,為什麼我們更傾向於使用插入排序而不是氣泡排序演算法呢? 答:從程式碼實現上來看,氣泡排序的資料交換要比插入排序的資料移動要複雜,氣泡排序需要3個賦值操作,而插入排序只需要1個,所以在對相同陣列進行排序時,氣泡排序的執行時間理論上要長於插入排序。

七、程式碼實現

public static void bubbleSort(int[] a) {
int n = a.length;
if (n <= 1) {
return;
}
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n - i - 1; ++j) {
if (a[j] > a[j + 1]) {
swap(a, j, j + 1);
}
}
}
}

public static void insertionSort(int[] a) {
int n = a.length;
if (n <= 1) {
return;
}
for (int i = 1; i < n; ++i) {
int value = a[i];
int j = i - 1;
while (j >= 0) {
if (a[j] > value) {
a[j + 1] = a[j];
j--;
} else {
break;
}

}
a[j + 1] = value;
}
}

public static void selectionSort(int[] a) {
int n = a.length;
if (n <= 1) {
return;
}
for (int i = 0; i < n; ++i) {
int min = i;
for (int j = i + 1; j < n; ++j) {
if (a[min] > a[j]) {
min = j;
}
}
swap(a, min, i);
}
}

private static void swap(int[] a, int i, int j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}