各种排序算法总结
1、冒泡排序
从第一位开始,依次比较相邻的元素,如果后面一位的数字比这一位小,那就进行调换。经过这样一轮下来,最大的元素就在最后一位了。
重复以上过程,除了最后一位。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
上代码:
// 分类 -------------- 内部比较排序 // 数据结构 ---------- 数组 // 最差时间复杂度 ---- O(n^2) // 最优时间复杂度 ---- 如果能在内部循环第一次运行时,使用一个旗标来表示有无需要交换的可能,可以把最优时间复杂度降低到O(n) // 平均时间复杂度 ---- O(n^2) // 所需辅助空间 ------ O(1) // 稳定性 ------------ 稳定
1 #include <iostream> 2 #include <vector> 3 using namespace std; 4 void swap(vector<int> &a,int i,int j){ 5 int temp = a[i]; 6 a[i]= a[j]; 7 a[j] = temp; 8 } 9 int main(){ 10 int n; 11 cin>>n; 12 vector<int> a(n); 13 for(int i=0;i<n;i++){ 14 cin>>a[i]; 15 } 16 for(int i=0;i<n-1;i++){ 17 for(int j=0;j<n-1-i;j++){ 18 if(a[j] > a[j+1]){ 19 swap(a,j,j+1); 20 } 21 } 22 } 23 for(int i=0;i<n;i++){ 24 cout<<a[i]<<endl; 25 } 26 }
2、鸡尾酒排序
从低到高再从高到低,前半轮将最大元素放在最后,后半轮将最小元素放在前面。
上代码:
// 分类 -------------- 内部比较排序 // 数据结构 ---------- 数组 // 最差时间复杂度 ---- O(n^2) // 最优时间复杂度 ---- 如果序列在一开始已经大部分排序过的话,会接近O(n) // 平均时间复杂度 ---- O(n^2) // 所需辅助空间 ------ O(1) // 稳定性 ------------ 稳定
1 #include <iostream> 2 #include <vector> 3 using namespace std; 4 void swap(vector<int> &a,int i,int j){ 5 int temp = a[i]; 6 a[i]= a[j]; 7 a[j] = temp; 8 } 9 int main(){ 10 int n; 11 cin>>n; 12 vector<int> a(n); 13 for(int i=0;i<n;i++){ 14 cin>>a[i]; 15 } 16 int left = 0; 17 int right = n-1; 18 while(left < right){ 19 for(int i=left;i<right;i++){ 20 if(a[i] > a[i+1]){ 21 swap(a,i,i+1); 22 } 23 } 24 right--; 25 for(int i=right;i>left;i--){ 26 if(a[i] > a[i+1]){ 27 swap(a,i,i+1); 28 } 29 } 30 left++; 31 } 32 for(int i=0;i<n;i++){ 33 cout<<a[i]<<endl; 34 } 35 }
3、选择排序
从第一位开始,找出序列最小的元素,标记。 再与第一位比较,如果比第一位小的话,进行置换操作。
循环以上步骤,直至最后一位。
上代码:
// 分类 -------------- 内部比较排序 // 数据结构 ---------- 数组 // 最差时间复杂度 ---- O(n^2) // 最优时间复杂度 ---- O(n^2) // 平均时间复杂度 ---- O(n^2) // 所需辅助空间 ------ O(1) // 稳定性 ------------ 不稳定
1 #include <iostream> 2 #include <vector> 3 using namespace std; 4 void swap(vector<int> &a,int i,int j){ 5 int temp = a[i]; 6 a[i]= a[j]; 7 a[j] = temp; 8 } 9 int main(){ 10 int n; 11 cin>>n; 12 vector<int> a(n); 13 for(int i=0;i<n;i++){ 14 cin>>a[i]; 15 } 16 for(int i=0;i<n;i++){ 17 int min = i; 18 for(int j=i+1;j<n;j++){ 19 if(a[i] > a[j]){ 20 min = j; 21 } 22 } 23 if(i != min){ 24 swap(a,i,min); 25 } 26 } 27 for(int i=0;i<n;i++){ 28 cout<<a[i]<<endl; 29 } 30 }
4、插入排序
从第一个元素开始,该元素被认为是排序好的。
取下一个元素,从后往前遍历比较,如果该元素大于新元素,则该元素移到下一个位置。重复此步骤,直至找到已排序的元素比新元素小或者等,将新元素插入到该元素位置之后。
重复以上步骤。
上代码:
// 分类 ------------- 内部比较排序 // 数据结构 ---------- 数组 // 最差时间复杂度 ---- 最坏情况为输入序列是降序排列的,此时时间复杂度O(n^2) // 最优时间复杂度 ---- 最好情况为输入序列是升序排列的,此时时间复杂度O(n) // 平均时间复杂度 ---- O(n^2) // 所需辅助空间 ------ O(1) // 稳定性 ------------ 稳定
1 #include <iostream> 2 #include <vector> 3 using namespace std; 4 void swap(vector<int> &a,int i,int j){ 5 int temp = a[i]; 6 a[i]= a[j]; 7 a[j] = temp; 8 } 9 int main(){ 10 int n; 11 cin>>n; 12 vector<int> a(n); 13 for(int i=0;i<n;i++){ 14 cin>>a[i]; 15 } 16 for(int i=1;i<n;i++){ 17 int get = a[i]; 18 int j = i - 1; 19 while(j >= 0 && a[j] > get){ 20 a[j+1] = a[j]; 21 j--; 22 } 23 a[j+1] = get; 24 } 25 for(int i=0;i<n;i++){ 26 cout<<a[i]<<endl; 27 } 28 }
拓展:二分插入排序(思想不再赘述)
上代码
// 分类 -------------- 内部比较排序 // 数据结构 ---------- 数组 // 最差时间复杂度 ---- O(n^2) // 最优时间复杂度 ---- O(nlogn) // 平均时间复杂度 ---- O(n^2) // 所需辅助空间 ------ O(1) // 稳定性 ------------ 稳定
1 #include <iostream> 2 #include <vector> 3 using namespace std; 4 void swap(vector<int> &a,int i,int j){ 5 int temp = a[i]; 6 a[i]= a[j]; 7 a[j] = temp; 8 } 9 int main(){ 10 int n; 11 cin>>n; 12 vector<int> a(n); 13 for(int i=0;i<n;i++){ 14 cin>>a[i]; 15 } 16 for(int i=1;i<n;i++){ 17 int get = a[i]; 18 int left = 0; 19 int right = i - 1; 20 while(left <= right){ 21 int mid = (left + right)/2; 22 if(a[mid] > get){ 23 right = mid - 1; 24 }else{ 25 left = mid + 1; 26 } 27 } 28 for(int j=i-1;j>=left;j--){ 29 a[j+1] = a[j]; 30 } 31 a[left] = get; 32 } 33 for(int i=0;i<n;i++){ 34 cout<<a[i]<<endl; 35 } 36 }
5、快速排序(递归和非递归)
递归思想:
- 从序列中挑出一个元素,作为"基准"(pivot).
- 把所有比基准值小的元素放在基准前面,所有比基准值大的元素放在基准的后面(相同的数可以到任一边),这个称为分区(partition)操作。
- 对每个分区递归地进行步骤1~2,递归的结束条件是序列的大小是0或1,这时整体已经被排好序了。
- 上代码:
// 分类 ------------ 内部比较排序 // 数据结构 --------- 数组 // 最差时间复杂度 ---- 每次选取的基准都是最大(或最小)的元素,导致每次只划分出了一个分区,需要进行n-1次划分才能结束递归,时间复杂度为O(n^2) // 最优时间复杂度 ---- 每次选取的基准都是中位数,这样每次都均匀的划分出两个分区,只需要logn次划分就能结束递归,时间复杂度为O(nlogn) // 平均时间复杂度 ---- O(nlogn) // 所需辅助空间 ------ 主要是递归造成的栈空间的使用(用来保存left和right等局部变量),取决于递归树的深度,一般为O(logn),最差为O(n) // 稳定性 ---------- 不稳定
1 #include <iostream> 2 #include <vector> 3 using namespace std; 4 void swap(vector<int> &a,int i,int j){ 5 int temp = a[i]; 6 a[i]= a[j]; 7 a[j] = temp; 8 } 9 int getPartition(vector<int> &a,int low,int height){ 10 int i = low - 1; 11 for(int j=low;j<height;j++){ 12 if(a[j] < a[height]){ 13 i = i + 1; 14 swap(a,i,j); 15 } 16 } 17 swap(a,i+1,height); 18 return i+1; 19 } 20 void quickSort(vector<int> &a,int low,int height){ 21 if(low < height){ 22 int mid = getPartition(a,low,height); 23 quickSort(a, low, mid - 1); 24 quickSort(a, mid + 1, height); 25 } 26 } 27 int main(){ 28 int n; 29 cin>>n; 30 vector<int> a(n); 31 for(int i=0;i<n;i++){ 32 cin>>a[i]; 33 } 34 quickSort(a,0,n-1); 35 for(int i=0;i<n;i++){ 36 cout<<a[i]<<endl; 37 } 38 }非递归思想:
-
#include <iostream> #include <vector> #include <stack> using namespace std; void swap(vector<int> &a,int i,int j){ int temp = a[i]; a[i]= a[j]; a[j] = temp; } int getPartition(vector<int> &a,int low,int height){ int i = low - 1; for(int j=low;j<height;j++){ if(a[j] <= a[height]){ i = i + 1; swap(a,i,j); } } swap(a,i+1,height); return i+1; } void quickSort(vector<int> &a,int low,int height){ stack<int> nums; if(low < height){ int mid = getPartition(a,low,height); if(mid - 1 > low){ nums.push(low); nums.push(mid-1); } if(mid + 1 < height){ nums.push(mid+1); nums.push(height); } while(!nums.empty()){ int qheight = nums.top(); nums.pop(); int plow = nums.top(); nums.pop(); int pqmid = getPartition(a, plow, qheight); if (pqmid - 1 > plow) { nums.push(plow); nums.push(pqmid - 1); } if (pqmid + 1 < qheight) { nums.push(pqmid + 1); nums.push(qheight); } } } } int main(){ int n; cin>>n; vector<int> a(n); for(int i=0;i<n;i++){ cin>>a[i]; } quickSort(a,0,n-1); for(int i=0;i<n;i++){ cout<<a[i]<<endl; } }
- 归并排序(递归)
归并排序是创建在归并操作上的一种有效的排序算法,效率为O(nlogn),1945年由冯·诺伊曼首次提出。
归并排序的实现分为递归实现与非递归(迭代)实现。递归实现的归并排序是算法设计中分治策略的典型应用,我们将一个大问题分割成小问题分别解决,然后用所有小问题的答案来解决整个大问题。非递归(迭代)实现的归并排序首先进行是两两归并,然后四四归并,然后是八八归并,一直下去直到归并了整个数组。
归并排序算法主要依赖归并(Merge)操作。归并操作指的是将两个已经排序的序列合并成一个序列的操作,归并操作步骤如下:
- 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
- 设定两个指针,最初位置分别为两个已经排序序列的起始位置
- 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
- 重复步骤3直到某一指针到达序列尾
- 将另一序列剩下的所有元素直接复制到合并序列尾
// // main.cpp // test // // Created by didi on 2018/4/9. // Copyright © 2018年 didi. All rights reserved. // //递归的归并排序 #include <iostream> #include <vector> using namespace std; void Merge(vector<int> &a,int left,int mid, int right){ int len = right -left + 1; vector<int> temp(len); int i = left; int j = mid+1; int k = 0; while(i <= mid && j <= right){ temp[k++] = a[i] <= a[j] ? a[i++] : a[j++]; } while(i <= mid){ temp[k++] = a[i++]; } while (j <= right){ temp[k++] = a[j++]; } for(int l=0;l<len;l++){ a[left++] = temp[l]; } } void MergeSort(vector<int>&a,int left,int right){ if(left == right) return; int mid = (left + right) /2; MergeSort(a,left,mid); MergeSort(a,mid+1,right); Merge(a,left,mid,right); } int main() { int n; cin>>n; vector<int> a(n); for(int i=0;i<n;i++){ cin>>a[i]; } MergeSort(a,0,n-1); for(int i=0;i<n;i++){ cout<<a[i]; } }