这里列举了几种常用排序算法:1.选择排序、2.冒泡排序、3.插入排序、4.归并排序、5.快速排序、6.堆排序、7.计数排序,8.基数排序。
目录
时间复杂度 | 空间复杂度 | 稳定性 | |
选择排序 | O(N^2) | O(1) | NO |
冒泡排序 | O(N^2) | O(1) | YES |
插入排序 | O(N^2) | O(1) | YES |
归并排序 | O(N*logN) | O(N) | YES |
快速排序 | O(N*logN) | O(logN) | NO |
堆排序 | O(N*logN) | O(1) | NO |
1.选择排序
即每经过一轮,从中找出最小的放在最前面。每经过一轮,选出一个,N-1轮完成。
第一轮遍历N个数据,第二轮N-1个数据......,由等差数列求和公式得时间复杂度为O(n^2)
Java代码如下:
public static void selectionSort(int[] arr){
if(arr==null||arr.length<2){
return;
}
for(int i=0;i<arr.length-1;i++){
int minIndex=i;
for(int j=i+1;j<arr.length;j++){
minIndex=arr[j]<arr[minIndex]?j:minIndex;
}
swap(arr,i,minIndex);
}
}
//i和j是一个位置的话,会出错;
public static void swap(int[] arr,int i,int j){
arr[i]=arr[i]^arr[j];
arr[j]=arr[i]^arr[j];
arr[i]=arr[i]^arr[j];
}
2.冒泡排序
即两两相互比较,较大的则往前走继续比较,每经过一轮,把一个最大的放在未排好数据的末端,N轮完成。
由等差数列求和公式:易得时间复杂度为O(n^2)
Java代码如下:
public static void bubbleSort(int[] arr){
if(arr==null||arr.length<2){
return ;
}
for(int e=arr.length-1;e>0;e--){
for(int i=0;i<e;i++){
if(arr[i]>arr[i+1]){
swap(arr,i,i+1);
}
}
}
}
3.插入排序
即遍历数组中的每一个值,把它与前面的进行比较,若小于则交换,继续向前比较;
由图像可知,插入排序的效率会随数据而变化,时间复杂度最坏为O(n^2)。
Java代码如下:
public static void insertionSort(int[] arr){
if(arr==null||arr.length<2){
return;
}
//0~0是有序的
//0~i想有序的话
for(int i=1;i<arr.length;i++){
for(int j=i-1;j>=0&&arr[j]>arr[j+1];j--){
swap(arr,j,j+1);
}
}
}
4.归并排序
整体就是一个简单的递归,左边排好序,右边排好序,让其整体有序。
每次把数组一半一半的分开,进行递归,使得左边是有序,右边也是有序,然后进行merge过程,使得数组整体有序。merge过程:左右两边放在一起相互比较大小,小的先进入辅助数组,小的那边继续遍历,直到找到较大的数。
时间复杂度O(N*logN),额外空间复杂度O(N)。
Java代码如下:
public static void porcess(int[] arr,int L,int R){
if(L==R){
return;
}
int mid =L+((R-L)>>1);
process(arr,L,mid);//左边有序
process(arr,mid+1,R);//右边有序
merge(arr,L,R);
}
public static void merge(int[] arr,int L,int M,int R){
int[] help=new int(R-L+1);
int i=0;
int p1=L;
int p2=M+1;
while(p1<=M&&p2<=R){
help[i++]=arr[p1]<=arr[p2]?arr[p1++]:arr[p2++];
}
while(p1<=M){
help[i++]=arr[p1++];
}
while(p2<=M){
help[i++]=arr[p2++];
}
for(i=0;i<help.length;i++){
arr[L+i]=help[i];
}
}
5.快速排序
也是一种递归方法,随机选取一个数组里的一个数字,使得大于他的在一边,小于他的在一边,遇到等于直接跳过。之后递归,使得数组整体有序。
时间复杂度O(N*logN),额外空间复杂度O(logN),最坏为O(N)
Java代码如下:
public static void quickSort(int[] arr,int L,int R){
if(L<R){
swap(arr,L+(int)(Math.random()*(R-L+1)),R);
int[] p= partition(arr,L,R);
quickSort(arr,L,p[0]-1);
quickSOrt(arr,p[1]+1,R);
}
}
//这是一个处理arr[L~R]的函数
//默认以arr[R]作为划分值
// 返回等去区域(左边界,右边界),所以返回一个长度为2的数组。
public static int[] partition(int[] arr,int L,int R){
int less=L-1;
int more=R;
while(L<more){
if(arr[L]<arr[R]){
swap(arr,++less,L++);
}else if(arr[L]>arr[R]){
swap(arr,--more,L);
}else{
L++;
}
}
swap(arr,more,R);
return new int[] {less+1,more};
}
6.堆排序
1.首先将待排序的数组构造成一个大根堆,此时,整个数组的最大值就是堆结构的顶端
2.将顶端的数与末尾的数交换,此时,末尾的数为最大值,剩余待排序数组个数为n-1
3.将剩余的n-1个数再构造成大根堆,再将顶端数与n-1位置的数交换,如此反复执行,便能得到有序数组
时间复杂度O(N*logN),空间复杂度O(1)
Java代码如下:
public static void heapSort(int[] arr){
if(arr==null||arr.length<2){
return ;
}
for(int i=0;i<arr.length;i++){
heapInsert(arr,i);
}
int heapSize=arr.length;
swap(arr,0,--heapsize);
while(heapSize>0){
heapify(arr,0,heapSize);
swap(arr,0,--heapSize);
}
}
public static void heapInsert(int[] arr,int index){
while(arr[index]>arr[(index-1)/2]){
swap(arr,index,(index-1)/2);
index=(index-1)/2;
}
}
public static void heapify(int[] arr,int index,int heapSize){
int left=index*2+1;
while(left>heapSize){
int largest=left+1<heapSize&&arr[left+1]>arr[left]?left+1:left;
largest=arr[largest]>arr[index]?largest:index;
if(lagest==index){
break;
}
swap(arr,largest,index);
index=largest;
left=index*2+1;
}
}
public static void swap(int[] arr,int i,int j){
int tmp=arr[i];
arr[i]=arr[j];
arr[j]=tmp;
}
桶排序思想下的排序都是不基于比较的排序,应用范围有限。
7.计数排序O(N)
eg:给一串年龄,按照大小排序
如 2,2,15,18,18,18,56,65
准备一个数组最小为65,help[65]={0};
遍历到2时,help[2]++;
遍历到15时,help[15]++;
8.基数排序O(N)
用来处理进制问题,通过每个位数分别比较,确实不太好理解,需要好好钻研。
1.首先要找到最大位数,然后把小位数的前面添0。
2.之后准备给出的数据中出现数字的容器(数组,队列......)如 123,23,45,88,64
则需准备[1],[2],[3],[4],[5],[6],[8]号桶
3.按顺序先看个位,进行排序,如123,个位是3,就把他放入3号桶,23,个位也是3,把他也放到3号桶......,之后按照十位,直到最高位。
4.按顺序出桶完成排序;