默認都是從小到大排序。有必要註釋。
總結
分類 | 時間 | 空間 | 穩定性 |
---|---|---|---|
選擇 | 好、壞、平均都是O(n^2) | O(1) | 不穩定 |
插入 | 平均、壞O(n^2),好O(n) | O(1) | 穩定 |
冒泡 | 平均、壞O(n^2),好O(n) | O(1) | 穩定 |
快排 | 平均、好O(nlogn),壞O(n^2) | O(logn) | 不穩定 |
堆 | 好、壞、平均都是O(nlogn) | O(1) | 不穩定 |
歸併 | 好、壞、平均都是O(nlogn) | O(n) | 穩定 |
選擇排序
思想
每次從未排序集合中選出最大的,與未排序集合末端元素交換,直到未排序集合空。
cpp實現
void select_sort(vector<int>& arr){
for(int len = arr.size(); len > 0; --len){//len表示未排序集合大小
int max = 0;//max用來記錄未排序集合中最大數的下標
for(int i = 0; i < len; ++i){
if(arr[i]>arr[max]) max = i;
}
swap(arr[max], arr[len-1]);//將最大數與未排序集合末端元素交換
}
}
主要優點
數據移動次數。
若某個元素位於正確的最終位置上,則該元素不會被移動。
複雜度分析
一般是說不穩定。(本實現也是。不過,對於數組,把本實現中的內層for循環倒過來,即從後往前找最大,就穩定了…)
空間O(1) 原地操作(優點);
時間O(n^2) 好、壞、平均都一樣。
主要受比較次數制約:n(n-1)/2,與初始狀態無關。
移動(交換)次數最好0次,最多n-1次。
與冒泡比較:n值較小時,選擇排序快於冒泡。
因爲冒泡移動次數爲n^2,一般來說,一次移動(交換)時間長於一次比較時間。
插入排序
思想
將無序區間中的元素依次插入有序區間中的適當位置,直到無序區間長度變爲零。
cpp實現
void insert_sort(vector<int>& arr){
for(int len = 1; len < arr.size(); ++len){
int i = 0;
while(i<len && arr[len]>arr[i]) ++i;
if(i<len){
int t = arr[len];
for(int j = len; j > i; --j) arr[j] = arr[j-1];
arr[i] = t;
}
}
}
快排
常考中的常考,要會手寫,且做到快速、無bug。
思想
白話:“挖洞填數+分治”
(0)首先選擇哨兵元素,將該元素“挖”出來賦給x,並將該位置作爲第一個“洞”(同一時間只會存在一個“洞”)。本實現選擇左界元素爲哨兵。
(1)先從右往左,找比哨兵x小的元素a,將其“挖”出,填入“洞”中,此時a的位置變成了新的“洞”。
(2)再從左往右,找比哨兵x大的元素b,將其“挖”出,填入“洞”中,此時b的位置變成了新的“洞”。
(3)重複執行(1-2)直到左右相遇,此時相遇位置即最後一個“洞”就是哨兵元素x最終有序位置。
(4)將哨兵元素x最終有序位置左右分別進行快排,即分治,直到當前區段長度爲零。
cpp實現
void quick_sort(vector<int>& arr, int l, int h){
if(l>=h) return;
int i = l, j = h;
int x = arr[l];
while(i<j){
while(i<j && arr[j]>=x) --j;
if(i<j) arr[i++] = arr[j];
while(i<j && arr[i]<=x) ++i;
if(i<j) arr[j--] = arr[i];
}
arr[i] = x;
quick_sort(arr, l, i-1);
quick_sort(arr, i+1, h);
}