1.如何分析一个排序算法
1.排序算法的执行效率
1)最好情况,最坏情况,平均情况时间复杂度
2)时间复杂度的系数,常数和阶数
3)比较次数和交换次数
2.排序算法的内存消耗
内存消耗可以引入空间复杂度来衡量,原地排序算法,特指空间复杂度为O(1)的算法
3.排序算法的稳定性
稳定性是指,如果待排序的序列中有相等的元素,经过排序之后,相等元素之间原有的先后顺序不变。
2. 冒泡排序,插入排序 ,选择排序
1.冒泡排序
private static void test2(int[] arrs) {
if(arrs.length<2){
return;
}
for(int i=0;i<arrs.length-1;i++){
boolean flag=true;//用来判断如果之间有一次没有进行位置互换,那么说明顺序已经排好没有必要再走之后的排序了
for (int j=i+1;j<arrs.length;j++){
if(arrs[i]>arrs[j]){
arrs[i]=arrs[i]^arrs[j];//做变量切换
arrs[j]=arrs[j]^arrs[i];
arrs[i]=arrs[i]^arrs[j];
flag=false;
}
}
if (flag){
break;
}
}
}
分析:
1.冒泡排序是原地排序算法吗?
是原地排序算法,他的排序过程中,可以不引入其他的变量,就算上boolean flag变量,他也是个空间复杂度为O(1)的算法
2.冒泡排序是稳定的排序算法吗?
是稳定的排序算法,因为当相邻的两个元素是相同的时候,我们不调换位置,所以冒泡是稳定的
3.冒泡排序的时间复杂度是多少?
最好情况下,本来就是有序的,我们只需要排序一次就可以了,所以最好复杂度是O(n)
最坏情况下,完全倒序的,我们需要排序N次,所以最坏复杂度是O(n^2)
平均情况下,如何计算呢?
用有序度和逆序度来描述。
有序度时数组中具有有序关系的元素的大小。比如[2,4,3,1,5,6] 有序度是11
而我们称[1,2,3,4,5,6]这种数组,它们的有序度是n(n-1)/2,是满有序
而一个数组从初始刀满有序,需要进行交换,交换一次有序度就加一。
所以交换次数=满有序-有序度
可以看出最差的情况就是有序度是0,然后到满有序需要交换n(n-1)/2次,而最好的情况是满有序一次都不交换。
所以我们取它们的中间 n(n-1)/2/2 =n(n-1)/4 也就是约等于n^2,所以平均时间复杂度就是n^2
2.插入排序
//插入排序(插入排序主要分为两个区域,第一是有序的区域,第二是无序的区域,拿着无序区域中的第一个跟所有有序区的区比,如果
// 无序区域的第一个值小于有序区的值,那就将有序区的值向后移动一位,再比较,直到数组到0或者有序区的数据小于无序区的数据,
// break 最后将无序区的第一个值插入到最新的位置。)
public static void test3(int[] arrs){
if(arrs.length==1){
return;
}
for (int i=1;i<arrs.length;i++){
int value=arrs[i];//记录无序区的第一个元素,从下标1开始,默认下标0就是有序区
int j=i-1;
for (;j>=0;j--){
if (arrs[j]>value){//判断是否小于有序区的值,小于就换位置
arrs[j+1]=arrs[j];
}else{break;}
}
arrs[j+1]=value;//将无序区的数据赋值过去
}
}
分析:1.插入排序是不是原地排序呢?
是原地排序
2.插入排序是稳定的排序算法么?
是稳定的排序算法,如果相邻的俩个值相等,那么是不会调换位置的
3.插入排序的时间复杂度是多少?
最好时间复杂度是n
最坏时间复杂度是n
而它的平均时间复杂度计算,我们可以考虑一下,在数组中插入一个值的话,他的时间复杂度是n,因为要移动n次
那插入n个值,他的时间复杂度当然就是n^2了。
3.选择排序
//选择排序,分为有序区和无序区,从无序区中选择一个最小的放到有序区的末尾。
public static void test4(int[] arrs){
System.out.println(Arrays.toString(arrs));
if(arrs.length==1){
return;
}
for(int i=0;i<arrs.length;i++){
int low=i;//i代表的是无序区的第一个元素,初始以这个元素为最小值
for(int j=i;j<arrs.length;j++){
if(arrs[j]<arrs[low]){//如果索引j的值比最小值还小,那么记录当前索引为最小值索引
low=j;
}
}
if(low!=i){//如果最小值索引不是无序区的第一个值,那么说明中间出现了赋值操作,需要进行最小值交换操作
int tmp=arrs[i];
arrs[i]=arrs[low];
arrs[low]=tmp;
}
}
System.out.println(Arrays.toString(arrs));
}
分析:1.选择排序是不是原地排序呢?
是原地排序,并没有新增空间,空间复杂度是O(1)
2.选择排序是稳定的排序算法么?
不是稳定的排序算法,因为会存在交换的可能
比如[2,4,4,3,1] ,最小值是3,会和第一个4换位置,这时候第一个4到了第二个4的后面,所以不是稳定排序
3.选择排序的时间复杂度是多少?
最好时间复杂度是n
最坏时间复杂度是n
而它的平均时间复杂度计算,是n^2,他的时间复杂度当然就是n^2了。
4.为什么插入排序要比冒泡排序好一些?
1.插入排序和冒泡排序的时间复杂度都是n^2,而且都是稳定算法,而且都是原地排序,那为什么插入排序要好一些呢?
可以仔细的观察,冒泡排序的话,它比较完了元素之后进行交换的时候,至少需要3步才能实现
int tmp=a;
a=b;
b=tmp;
而插入排序的话,只需要移动一次即可
a[j+1]=a[j];
所以可以看出来,比如一次赋值操作需要耗费m的时间,冒泡排序耗费3m的时间,而插入排序耗费k的时间,这样来看,那就是插入时间要少一些。