常见的分类算法还可以根据排序方式分为两大类:比较排序和非比较排序,比较排序就是常见的几种。
非比较排序的特点是时间复杂度很低,都是线性复杂度O(n),但是非比较排序受到的限制比较多,不是通用的排序算法,有如下几种:
1)计数排序(Count Sort)(复杂度O(n+k)(其中k是待排序的n个数字中最大值)
2)基数排序(Bucket Sort)(复杂度O(nk)(其中k是最大数字的位数)
3)桶排序(Radix Sort)(复杂度O(n+k)(其中k是待排序的n个数字中最大值)
目录
时间及空间复杂度如下:
稳定性:两个相等的数在排序之前和排序之后的相对位置不变。
一.快速排序。
算法思想如下:
选取一个基准值,将小于基准值的放在基准值前面,大于基准值的放在基准值后面。
算法模板如下:
#include<bits/stdc++.h>
using namespace std;
void quick_sort(int a[],int l,int r)
{
if(l>=r) return ;
int i=l-1,j=r+1,x=a[l+r>>1];
while(i<j)
{
do i++;while(a[i]<x);
do j--;while(a[j]>x);
if(i<j) swap(a[i],a[j]);
}
quick_sort(a,l,j),quick_sort(a,j+1,r);
}
例题:https://www.acwing.com/problem/content/787/
模板应用:
#include<bits/stdc++.h>
using namespace std;
void quick_sort(vector<int>& res,int l,int r){
if(l>=r) return;
int mid = res[l+r>>1];
int i=l-1,j=r+1;
while(i<j){
while(res[++i]<mid);
while(res[--j]>mid);
if(i<j) swap(res[i],res[j]);
}
quick_sort(res,l,j);
quick_sort(res,j+1,r);
}
int main(){
int n;
cin>>n;
vector<int> res(n);
for(int i=0;i<n;i++) cin>>res[i];
quick_sort(res,0,n-1);
for(int i=0;i<n;i++)
cout<<res[i]<<" ";
}
二.归并排序。
算法思想:
利用分治思想将一个数组分成两段,将两端较小的放在前面。
模板如下:
void merge_sort(int a[],int l,int r)
{
if(l>=r) return ;
int mid=l+r>>1;
merge_sort(a,l,mid);
merge_sort(a,mid+1,r);
int k=0,i=l,j=mid+1;
while(i<=mid&&j<=r)
{
if(a[i]<a[j]) tmp[k++]=a[i++];
else tmp[k++]=a[j++];
}
while(i<=mid) tmp[k++]=a[i++];
while(j<=r) tmp[k++]=a[j++];
for(int i=l,j=0;i<r;i++,j++) q[i]=tmp[i];
}
例题:https://www.acwing.com/problem/content/789/
模板应用:
#include <bits/stdc++.h>
using namespace std;
int temp[100010];
void merge_sort(vector<int>& res,int l,int r){
if(l>=r) return ;
int mid = l+r>>1;
int i=l,j=mid+1;
int k=0;
merge_sort(res,l,mid);
merge_sort(res,mid+1,r);
while(i<=mid&&j<=r){
if(res[i]<=res[j]) temp[k++]=res[i++];
else temp[k++] = res[j++];
}
while(i<=mid) temp[k++]=res[i++];
while(j<=r) temp[k++] = res[j++];
for(int i=l,j=0;i<=r;i++,j++) res[i]=temp[j];
}
int main(){
int n;
cin>>n;
vector<int> res(n);
for(int i=0;i<n;i++)
cin>> res[i];
merge_sort(res,0,n-1);
for(int i=0;i<n;i++)
cout<<res[i]<<" ";
}
三.冒泡排序。
算法思想:
比较相邻的元素,如果反序则交换,经过第一趟后就能找出最大的元素,然后重复便可。
模板如下:
void Bubble_sort(int arr[],int n){
int temp;
for(int i=0;i<n-1;i++){
for(int j=0;j<n-i-1;j++){
if(arr[j]>arr[j+1]){
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
适用场景:
冒泡排序思路简单,代码也简单,特别适合小数据的排序。但是,由于算法复杂度较高,在数据量大的时候不适合使用。
四.选择排序.
算法思想:
先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾,重复第二步,直到所有元素均排序完毕。
模板如下:
void select_sort(vector<int>&arr){
int x = arr[0];
int temp;
for(int i=0;i<arr.size()-1;i++){
int min = i;
for(int j=i+1;j<arr.size();j++){
if(arr[i]>arr[j]){
min = j;//比较的次数少
}
}
swap(arr[i],arr[j]);
}
}
虽然数组实现的选择排序不稳定,但链表实现的选择排序是稳定的。
五.插入排序。
算法思想:
先把待排序的数组分成已排序和未排序两部分,初始的时候把第一个元素认为是已排好序的,从第二个元素开始,在已排好序的子数组中寻找到该元素合适的位置并插入该位置,重复上述过程直到最后一个元素被插入有序子数组中。
模板如下:
void insertion_sort(vector<int>&arr){
for(int i=0;i<arr.size()-1;i++){
for(int j=i;j>0;j--){
if(arr[j]<arr[j-1]
swap(arr[j],arr[j-1]);
}
}
}
使用场景
插入排序由于O( n2 )的复杂度,在数组较大的时候不适用。但是,在数据比较少的时候,是一个不错的选择,一般做为快速排序的扩充。例如,在STL的sort算法和stdlib的qsort算法中,都将插入排序作为快速排序的补充,用于少量元素的排序。又如,在JDK 7 java.util.Arrays所用的sort方法的实现中,当待排数组长度小于47时,会使用插入排序。