一、交换排序
冒泡排序(BubbleSort)
思想:通过循环依次比较相邻的两个元素,若不符合规则就互换位置。
平均时间复杂度:O(n2)
最坏时间复杂度:O(n2)
最好时间复杂度:O(n)
Java代码实现:
public static int [] BubbleSort(int [] a){
for(int i=0;i<a.length-1;i++){
for(int j=0;j<a.length-1-i;j++){
if(a[j]>a[j+1]){
int temp = a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
return a;
}
快速排序(QuickSort)
1、简单的快速排序
思想:采用分治法对序列依次拆分来进行排序,通过一次排序后将序列分成两个子序列,再分别对子序列进行同样的操作
平均时间复杂度:O(nlog2n)
最坏时间复杂度:O(n2)
最好时间复杂度:O(nlog2(n))
Java代码实现:
//使用方法:定义一个数组,然后直接调用下方的QuickSort函数
//拆分序列函数:根据分界点拆分序列
public static int Partition(int [] a,int left,int right){
int base=a[left];
while(left!=right){
//从右边依次寻找比当前基数小的元素
while(a[right] >= base && left<right){
right--;
}
//若找到与left互换值
a[left] = a[right];
//从左边依次寻找比当前基数大的元素
while(a[left] <= base && left<right){
left++;
}
//若找到与right互换值
a[right] = a[left];
}
//将当前left所在的重复值换成基数base
a[left]=base;
//返回当前停止的地方,返回left或者right都行,因为此时left=right
return left;
}
//快速排序:递归调用拆分函数
public static void QuickSort(int [] a,int left,int right){
//判断函数是否结束
if(left > right)
return ;
//获取当前拆分一次后停止的地方
int index = Partition(a,left,right);
//采用递归的方式对拆分出来的子序列进行再次拆分
QuickSort(a,left,index-1);
//减1的目的是为了避免子序列有重复元素
QuickSort(a,index+1,right);
}
2、快速排序算法的改进
改进的方法即基数的选取标准:固定切分,随机切分和三取样切分。上述快速排序即采用固定切分。
(跟上面的代码比较只是增加了第6-19行代码,采用三数取中来选择基数)
Java代码实现:
//根据分界点拆分序列
/*
* 快速排序的改进:改进的关键在于基数的选取,在此选用三数取中法选取基数
* */
public static int Partition(int [] a,int left,int right){
//获取中间数的索引
int mid = (left+right)/2;
//对左中右三个数进行排序,并按照从左到右递增的顺序互换位置
if(a[left]>a[mid]){
swap(a,left,mid);
}
if(a[left]>a[right]){
swap(a,left,right);
}
if(a[right]<a[mid]){
swap(a,right,mid);
}
//交换之后将选中的基数a[mid]跟第一个元素即a[left]互换位置,目的是方便记下来的排序
swap(a,left,mid);
//此时基数为a[left]
int base=a[left];
while(left!=right){
//从右边依次寻找比当前基数小的元素
while(a[right] >= base && left<right){
right--;
}
//若找到与left互换值
a[left] = a[right];
//从左边依次寻找比当前基数大的元素
while(a[left] <= base && left<right){
left++;
}
//若找到与right互换值
a[right] = a[left];
}
//将当前left所在的重复值换成基数base
a[left]=base;
//返回当前停止的地方,返回left或者right都行,因为此时left=right
return left;
}
//快速排序
public static void QuickSort(int [] a,int left,int right){
//判断函数是否结束
if(left > right)
return ;
//获取当前拆分一次后停止的地方
int index = Partition(a,left,right);
//采用递归的方式对拆分出来的子序列进行再次拆分
QuickSort(a,left,index-1);
QuickSort(a,index+1,right);
}
//通用的数值交换函数
private static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[a] = arr[b];
arr[b] = temp;
}
二、选择排序
简单选择排序(SelectSort)
思想:从剩余未排序的序列中选择最小(或最大的)依次插入已排序列的末尾(开始时已排序列为空,剩余序列为当前序列)。
平均时间复杂度:O(n2)
最坏时间复杂度:O(n2)
最好时间复杂度:O(n2)
注:因为无论如何寻找最小值的for循环都会执行一遍(n次),再加上外层循环(n次)所以时间复杂度最好最坏都是O(n2)
public static int [] SelectSort(int [] a){
for(int i = 0;i<a.length;i++){
//min代表当前未排序序列中最小值的索引,初始值为i即未排序序列的第一个元素
int min=i,temp;
//这个for循环目的是在未排序序列中找到最小值的索引并赋值给min
for(int j=i+1;j<a.length;j++){
if(a[min]>a[j]){
min=j;
}
}
//在已排序序列的末尾处让刚才在未排序序列中找到的a[min]互换值,以达到排序的目的
temp = a[i];
a[i]=a[min];
a[min]=temp;
}
return a;
}
三、插入排序
插入排序
算法思想:将元素逐个插入到前面已经排好序的子序列中(默认第一个元素已经排序)
平均时间复杂度:O(n2)
最坏时间复杂度:O(n2)
最好时间复杂度:O(n)
public static int[] InsertSort(int [] a){
for(int i=1;i<a.length;i++){
int pre = i-1;
int cur = a[i];
//将当前元素依次跟已排好序的元素比较,找到该元素应该的位置
while(pre>=0 && cur < a[pre]){
//将大于该元素的元素往后挪一位
a[pre+1]=a[pre];
//比较下一个元素
pre--;
}
//将当前元素赋给空出来的位置
a[pre+1]=cur;
}
return a;
}