在平均情况下,快速排序最快;
在最好情况下,插入排序和冒泡排序最快;
在最坏情况下,堆排序和归并排序最快。
一.选择排序
时间复杂度:O(n²)
从数组中选择最小元素,将它与数组的第一个元素交换位置。再从数组剩下的元素中选择出最小的元素,将它与数组的第二个元素交换位置。不断进行这样的操作,直到将整个数组排序。
public static int[] SelectionSort(int arr[]){
for(int i=0;i<arr.length-1;i++){
int min=i;
for (int j=i+1;j<arr.length;j++){
if(arr[j]<arr[min])
min=j;
}
arr=Exchange(arr,min,i);
}
return arr;
}
public static int[] Exchange(int arr[],int a,int b){ //交换两个数
int flag=arr[a];
arr[a]=arr[b];
arr[b]=flag;
return arr;
}
二.冒泡排序
时间复杂度 :O(n²)
比较相邻的元素。如果第一个比第二个大,就交换他们两个 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。 针对所有的元素重复以上的步骤,除了最后一个。 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
public static int[] BubbleSort(int arr[]){
for(int i=0;i<arr.length-1;i++){
for(int j=1;j<arr.length-i;j++){
if(arr[j]<arr[j-1])
arr=Exchange(arr,j,j-1);
}
}
return arr;
}
public static int[] Exchange(int arr[],int a,int b){ //交换两个数
int flag=arr[a];
arr[a]=arr[b];
arr[b]=flag;
return arr;
}
三.插入排序
时间复杂度:O(n²)
将数组分成两部分, 一部分是已经排好的, 另一部分挨个访问, 插入到前一部分合适的地方
public static int[] InsertionSort(int arr[]) {
for (int i = 1; i < arr.length ; i++) {
for (int j = i-1; j >= 0 && arr[j] > arr[j+1]; j--) {
arr=Exchange(arr,j,j+1);
}
}
return arr;
}
public static int[] Exchange(int arr[],int a,int b){ //交换两个数
arr[a]=arr[a]^arr[b];
arr[b]=arr[a]^arr[b];
arr[a]=arr[a]^arr[b]; //选择排序不能用,a=b时arr[a]=arr[b],指向同一个,方法错误
return arr;
}
四.归并排序
时间复杂度:O(N*log2N)
用递归方法,取中点,左右分别排序(详细见注释)
public static int[] MergeSort(int arr[],int left,int right){
int mid=(left+right)/2;
if (left==right){ //递归出口
return arr;
}
MergeSort(arr,left,mid); //左部分使用归并方法
MergeSort(arr,mid+1,right); //右部分使用归并方法
Merge(arr,mid,right,left); //递归调用
return arr;
}
public static int[] Merge(int arr[],int mid,int right,int left){ // 归并方法
int tmp[]=new int[right-left+1]; //中介数组
int i=0; //中介数组的指针
int leftStart=left,leftEnd=mid,rightStart=mid+1,rightEnd=right;//左右部分的头末节点
while(leftStart<=leftEnd&&rightStart<=rightEnd){ //判断左右部分所在指针哪个数小,数小的赋值到中介数组中,直到左右任一部分到达末尾
if (arr[leftStart]<=arr[rightStart]){
tmp[i++]=arr[leftStart++];
}else
tmp[i++]=arr[rightStart++];
}
while(leftStart<=leftEnd){ //如果右部分到达了末尾,但左部分还没到的情况
tmp[i++]=arr[leftStart++];
}
while(rightStart<=rightEnd){ //如果左部分到达了末尾,但右部分还没到的情况
tmp[i++]=arr[rightStart++];
}
for(int j=0;j<i;j++){ //将tmp中介数组赋值回原数组中
arr[left+j]=tmp[j];
}
return arr;
}
五.快速排序
时间复杂度:O(N*log2N)
将数组最后一个数与数组中随机一个数互换(为了绝对随机,防止特殊情况),使用左右边界,将比互换后最后一个数小的放在左边,大的放在右边,相等的在中间(并不是完全排好序),再将最后一个数与右边界的数互换,这样中间部分就排好序了,再递归执行,将数组所有部分排好序(详细见注释)
public static int[] QuickSort(int arr[],int left,int right){
if (left>=right){ //递归出口
return arr;
}
Exchange(arr,left+(int)(Math.random()*(right-left+1)),right); //数组最后一个数与left和right之间的随机一个数互换((int)Math.random()*N 代表取【0,N-1】范围的一个随机整数)
int tmp[]=Partition(arr,left,right); //tmp[0]代表排好序范围的左节点 tmp[1]代表排好序范围的右节点
QuickSort(arr,left,tmp[0]-1); //左部分递归调用
QuickSort(arr,tmp[1]+1,right); //右部分递归调用
return arr;
}
public static int[] Partition(int arr[],int left,int right){
int leftp=left-1; //左边界
int num=right; //最后一位数,都需要跟此数比大小
int rightp=right; //右边界
while(left<rightp){ //left为当前要判断的数的指针
if (arr[left]<arr[num]){ //当前要判断的数小于num的情况
Exchange(arr,left++,++leftp); //左边界后移,然后交换左边界的数和判断的数 指针+1
}else if (arr[left]>arr[num]){//当前要判断的数大于num的情况
Exchange(arr,left,--rightp); //右边界前移,然后交换右边界的数和判断的数 指针不动
}else{ //当前要判断的数等于num的情况
left++; //左右边界都不动,指针+1
}
}
Exchange(arr,rightp,num); //交换右边界的数和最后一位数
int tmp[]=new int[2]; //用于返回排好序的左右节点的中介数组
tmp[0]=leftp+1; //排好序的左节点
tmp[1]=rightp; //排好序的右节点
return tmp;
}
public static int[] Exchange(int arr[],int a,int b){ //交换两个数
int flag=arr[a];
arr[a]=arr[b];
arr[b]=flag;
return arr;
}
六.堆排序
未完待续