本部分内容借鉴于OI Wiki,详情可查阅该网站
插入排序
#include<bits/stdc++.h>
using namespace std;
void Isort(vector<int>& a,int len){
for(int i = 1;i < len;i++){
int key = a[i];//基准数
int j = i - 1;
while(j >= 0 && a[j] > key){//从基准数往左找,找插入位置
a[j + 1] = a[j];
j--;
}
a[j + 1] = key;//break条件:1)已经找到数组边界
}//2)已经找到与基准数相比较小等于数,查找结束
}
int main(){
int n;
cin>>n;
vector<int>a(n);
for(int i = 0;i < n;i++)
cin>>a[i];
Isort(a,n);
for(int i = 0;i < n;i++)
cout<<a[i]<<' ';
return 0;
}
- 在数组几乎有序的情况下时间复杂度为O(n)
- 最差O(n²)
- 稳定
选择排序
#include<bits/stdc++.h>
using namespace std;
void Ssort(vector<int>& a,int len){
for(int i = 0;i < len-1;i++){
int tem = i;
for(int j = i+1;j < len;j++){//寻找基准数i右边的最小数
if(a[j] < a[tem])
tem = j;//更新
}
swap(a[i],a[tem]);//交换 i处以前为已排序部分
}
}
int main(){
int n;
cin>>n;
vector<int>a(n);
for(int i = 0;i < n;i++)
cin>>a[i];
Ssort(a,n);
for(int i = 0;i < n;i++)
cout<<a[i]<<' ';
return 0;
}
- 最差,最好,平均时间复杂度都是O(n²)
- 不稳定
冒泡排序
#include<bits/stdc++.h>
using namespace std;
void Bsort(vector<int>& a,int len){
int flag = 1;
while(flag){
flag = 0;//当扫描一遍发现已经全部有序时就可以结束
for(int i = 0;i < len-1;i++){
if(a[i] > a[i+1]){
flag = 1;
swap(a[i],a[i+1]);
}
}
}
}
int main(){
int n;
cin>>n;
vector<int>a(n);
for(int i = 0;i < n;i++)
cin>>a[i];
Bsort(a,n);
for(int i = 0;i < n;i++)
cout<<a[i]<<' ';
return 0;
}
- 稳定
- 平均时间复杂度是O(n²)
计数排序
#include<bits/stdc++.h>
using namespace std;
void print(vector<int>& a,int len){
for(int i = 0;i < len;i++)
cout<<a[i]<<' ';
}
void Csort(vector<int>& a,int len){
int MAX = a[0];
int MIN = a[0];
for(int i = 1;i < len;i++){
MAX = max(a[i],MAX);
MIN = min(a[i],MIN);
}
int w = MAX - MIN;//大小差值分块
vector<int>t(w+1);//0 - w
for(int i = 0;i < len;i++)
t[a[i] - MIN]++;
int sum = 0;
for(int i = 0;i <= w;i++){
sum += t[i];
t[i] = sum;
}//数a[i]应存储在下标为t[i]的位置
vector<int>ans(len);
for(int i = len-1;i >= 0;i--){
ans[t[a[i] - MIN] - 1] = a[i];//注意
t[a[i] - MIN]--;//注意
}
print(ans,len);
}
int main(){
int n;
cin>>n;
vector<int>a(n);
for(int i = 0;i < n;i++)
cin>>a[i];
Csort(a,n);
return 0;
}
- 前缀和的使用优化保证了排序稳定
- 时间复杂度是O(n + w),n是元素个数,w是最大最小值的差(也就是分块个数)
快速排序
#include<bits/stdc++.h>
using namespace std;
void QuickSort(vector<int> &nums, int l, int r){//给nums[l] - nums[r] 排序
if(l + 1 >= r)
return;
int fir = l;
int last = r;
int key = nums[fir];//第一个数为基准数
while(fir < last){
while(fir < last && nums[last] >= key)//从右往左找比基准数小的元素
last--;
//swap(nums[fir], nums[last]);
while(fir < last && nums[fir] <= key)
fir++;
//swap(nums[fir], nums[last]);
if(fir < last)
swap(nums[fir] ,nums[last]);
}
swap(nums[fir], nums[l]);//nums[last] = key;同理
QuickSort(nums, l ,fir);
QuickSort(nums, fir + 1, r); //fir处元素已排好位置
}
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;
return 0;
}
- 平均时间复杂度是O(nlogn),最差情况下为O(n²)(优化情况:三路快排,内省排序)
- 不稳定
优化情况:三路快排
#include<bits/stdc++.h>
using namespace std;
void quicksort(int arr[],int len){
if(len <= 1)
return;
int key = arr[rand() % len];//随机化
int i = 0;//表示当前元素
int j = 0;//小于key
int k = len;//大于key
while(i < k){
if(arr[i] < key)//让小于key的右一元素跟等于key的右一元素交换
swap(arr[i++],arr[j++]);//小于key部分增加
else if(arr[i] > key)//让大于key的左一元素跟等于key的右一元素交换
swap(arr[i],arr[--k]);//大于key部分增加
else//等于时不做操作
i++;
}
quicksort(arr,j);
quicksort(arr+k,len-k);
}
int main(){
int n;
cin>>n;
int arr[100];
for(int i = 0;i < n;i++)
cin>>arr[i];
quicksort(arr,n);
for(int i = 0;i < n;i++)
cout<<arr[i]<<' ';
return 0;
}
/*
本质是将数组分成三部分
小于key || 等于key || 大于key
0 -- j j+1 -- i k -- len-1
利用随机化排除重复元素堆积导致的时间复杂度升高情况
这种写法的partition还可用于寻找数组中第k大元素(O(n))
*/
归并排序
#include<bits/stdc++.h>
using namespace std;
int t[101], a[101];
void merge(int l, int r){//给a[l]~a[r-1]的数组排序
if(r - l == 1)//长度为0返回空
return;
int mid = l + (r - l) / 2;
merge(l, mid);
merge(mid, r);
int p = l, q = mid, s = l;//p为左侧数组指针,q为右侧数组指针,s为已复制的位置
while(s < r){
if(p >= mid||(a[p] > a[q] && q < r))//对应两种情况:1)左侧已经复制完,复制右侧 2)右元素小于左边,正常复制右元素
t[s++] = a[q++];
else//否则就是复制左侧元素
t[s++] = a[p++];
}
for(int i = l;i < r;i++)//复制已排序好的组合进原数组
a[i] = t[i];
}
int main(){
memset(t, 0, sizeof(t));
for(int i = 0;i < 5;i++)
cin>>a[i];
merge(0, 5);
for(int i = 0;i < 5;i++)
cout<<a[i]<<endl;
return 0;
}
- 最优,平均,最差时间复杂度都是O(nlogn)
- 需要O(n)的空间
- 稳定
- 可用来求逆序对
堆排序
#include<bits/stdc++.h>
using namespace std;
void heapify(int arr[], int st, int en){//大根堆,节点下标从0开始,向下调整
int root = st;
int right = root*2+1;
while(right <= en){
if(right + 1 <= en && arr[right+1] > arr[right]){
right++;
}
if(arr[root] > arr[right])
break;
else{
swap(arr[root],arr[right]);
root = right;
right = root*2+1;
}
}
}
void Hsort(int arr[], int len){
for(int i = len/2-1;i >= 0;i--)//从最后一个父节点逐步向上进行向下调整
heapify(arr,i,len-1);
for(int i = len - 1;i > 0;i--){//由于大根堆性质,arr[0]为部分数组的最大值
swap(arr[0],arr[i]);//下标为i位置的节点已经排好
heapify(arr,0,i-1);//右边界减一,对0-i-1位置继续调整
}
}
int main(){
int n;
int arr[101];
cin>>n;
for(int i = 0;i < n;i++)
cin>>arr[i];
Hsort(arr,n);
for(int i = 0;i < n;i++)
cout<<arr[i]<<' ';
return 0;
}
- 最优,平均,最差时间复杂度都是O(nlogn)
- 不稳定
桶排序
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;//元素个数
int w;//最大最小差值,用于分桶
int a[N];//存数数组
vector<int>bucket[N];//桶
void Isort(vector<int>& A){//在每个桶中进行插入排序
for(int i = 1;i < A.size();i++){
int key = A[i];
int j = i-1;
while(j >= 0 && A[j] > key){
A[j+1] = A[j];
j--;
}
A[j+1] = key;
}
}
void bucketsort(){
int MIN = a[0];
int MAX = a[0];
for(int i = 1;i < n;i++){
MIN = min(a[i],MIN);
MAX = max(a[i],MAX);
}
w = MAX - MIN;
int size = w/n + 1;//桶大小
for(int i = 0;i < n;i++)
bucket[i].clear();//清桶
for(int i = 0;i < n;i++){
bucket[(a[i]-MIN)/size].push_back(a[i]);
}
int p = 0;
for(int i = 0;i < n;i++){
Isort(bucket[i]);
for(int j = 0;j < bucket[i].size();j++){
a[p++] = bucket[i][j];//桶内元素已保证相对顺序 复制回原数组
}
}
}
int main(){
cin>>n;
for(int i = 0; i < n;i++)
cin>>a[i];
bucketsort();
for(int i = 0;i < n;i++)
cout<<a[i]<<" ";
}
- 内部排序使用了插入排序保证了稳定性
- 在数据分布均匀时,时间复杂度为O(n)
- 最差情况的时间复杂度为O(n²)