資料結構與演算法之美專欄學習筆記-排序(上)
排序方法
氣泡排序、插入排序、選擇排序、快速排序、歸併排序、計數排序、基數排序、桶排序。
複雜度歸類
氣泡排序、插入排序、選擇排序 O(n^2)
快速排序、歸併排序 O(nlogn)
計數排序、基數排序、桶排序 O(n)
演算法的執行效率
1. 最好、最壞、平均情況時間複雜度。
2. 時間複雜度的係數、常數和低階。
3. 比較次數,交換(或移動)次數。
排序演算法的穩定性
穩定性概念
如果待排序的序列中存在值相等的元素,經過排序之後,相等元素之間原有的先後順序不變。
穩定性重要性
可針對物件的多種屬性進行有優先順序的排序。
排序演算法的記憶體損耗
原地排序演算法:特指空間複雜度是O(1)的排序演算法。
氣泡排序
氣泡排序只會操作相鄰的兩個資料。每次冒泡操作都會對相鄰的兩個元素進行比較,看是否滿足大小關係要求,如果不滿足就讓它倆互換。
穩定性
氣泡排序是穩定的排序演算法。
空間複雜度
氣泡排序是原地排序演算法。
時間複雜度
最好情況:O(n)。
最壞情況:O(n^2)。
平均情況:平均時間複雜度為O(n^2)。
C#程式碼演示
public void BubbleSort(int[] data,int n) { if (n <= 1) return;//長度小於1的陣列直接返回for(int j = 0; j < n; j++)//遍歷陣列 { Boolean flag = false;//設標誌位 for (int i = 0; i < n-j-1; i++)//遍歷陣列未被排序的部分 { if (data[i] > data[i + 1])//比較元素大小 { int temp = data[i];//交換資料 data[i] = data[i + 1]; data[i+ 1] = temp; flag = true; } } if (!flag) return;//如果一次冒泡無交換說明全部有序,返回 } }
插入排序
插入排序將陣列資料分成已排序區間和未排序區間。初始已排序區間只有一個元素,即陣列第一個元素。
在未排序區間取出一個元素插入到已排序區間的合適位置,直到未排序區間為空。
穩定性
插入排序是穩定的排序演算法。
空間複雜度
插入排序是原地排序演算法。
時間複雜度
1. 最好情況:O(n)。
2. 最壞情況:O(n^2)。
3. 平均情況:O(n^2)。
C#程式碼演示
public void InsertionSort(int[] data,int n) { if (n <= 1) return; for(int i = 1; i < n; i++)//遍歷陣列 { int value = data[i];//記錄要進行排序的第i個數組成員 int j = i - 1;//從第i個數組成員前的成員開始遍歷到頭 for (; j >= 0; --j) { if (data[j] > value)//如果其比第i個數組成員大,將其前移 data[j + 1] = data[j]; else//否則就結束,因為前面的資料必然是有序的 break; } data[j + 1] = value;//在前移結束後停下的位置前插入記錄的資料 } }
選擇排序
選擇排序將陣列分成已排序區間和未排序區間。初始已排序區間為空。
每次從未排序區間中選出最小的元素插入已排序區間的末尾,直到未排序區間為空。
穩定性
選擇排序不是穩定的排序演算法。
空間複雜度
選擇排序是原地排序演算法。
時間複雜度
都是O(n^2))
C#程式碼演示
public static void SelectionSort(int[] data,int n) { if (n <= 1) return; for(int i = 0; i < n; i++)//遍歷陣列 { int temp = data[i];//記錄要排序的陣列成員 int pos = i;//記錄最終的交換位置 for(int j = i; j < n; j++)//遍歷陣列未交換的部分,找到最小的成員並記錄其位置 { if (data[j]<data[i])//如果未交換的部分比第i個成員小 { data[i] = data[j];//將其提到前面 pos = j;//記錄交換的位置 } } data[pos] = temp;//互換資料 } }
思考
氣泡排序和插入排序的時間複雜度相同都是O(n^2),為什麼插入排序比氣泡排序更受歡迎?
因為氣泡排序的交換操作需要執行三次操作。
如果資料儲存在連結串列中,三種排序方法的時間複雜會變成怎樣?
假定只能改變節點位置
氣泡排序,比較次數不變,因為指標,交換資料更加複雜。
插入排序,比較次數不變,但可以直接插入資料,不需要一個個地後移資料。
選擇排序,比較次數不變,因為指標,交換資料更加複雜。