1、冒泡排序
1)原理
a、比较相邻两个元素
b、如果满足交换要求(视排序规定而定,本文均按照从左到右、从小到大排序),则交换位置
c、向后移动一位,回到a步骤继续
在算法执行时,最大的数据项总是“冒泡”到数组的最后端,因此被称为冒泡排序。
2)代码
//冒泡排序
public void BubbleSort(long[] a){
int out, in;
int nElems = a.length;
System.out.println("排序前: ");
display(a);
for (out =nElems-1; out>1; out -- ){
//外层for是控制冒泡的总体次数
//out>1是因为之前的n-2次排序是有序的,所以最后一个元素无需再排序即为有序
for(in = 0; in<out; in++){//内层则是冒泡的一次过程
if( a[in] > a[in + 1]){
//相邻两数之间进行比较,如果条件成立,则交换两数
swap(a, in, in+1);
}// end of if
}// end of for(in)
System.out.print(nElems - out+":");
display(a);
}//end of for(out)
}//end of BubbleSort
实际上,使用swap函数来交换式为了让程序更加直观,调用swap函数会增加开销,所以在实际使用中,可以视情况而定将交换过程直接写在判断语句块中。
测试结果:
现在测试冒泡排序:
排序前:
5 4 1 3 6 8 2 9 0 7
1:4 1 3 5 6 2 8 0 7 9
2:1 3 4 5 2 6 0 7 8 9
3:1 3 4 2 5 0 6 7 8 9
4:1 3 2 4 0 5 6 7 8 9
5:1 2 3 0 4 5 6 7 8 9
6:1 2 0 3 4 5 6 7 8 9
7:1 0 2 3 4 5 6 7 8 9
8:0 1 2 3 4 5 6 7 8 9
运行时间为:4毫秒
3)复杂度
假设数组中有N个数据项,那么第一趟排序进行了N-1次比较,第二趟进行了N-2次比较,以此类推,可以得到:
(N-1)+(N-2)+(N-3)+....+1=N*(N-1)/2
而交换次数显然少于比较的次数,得出结论,冒泡排序运行需要O(n2)时间级别。
2、选择排序
1)原理
注:依旧是从左到右从小到大排序
a、从数组无序部分左到右,元素依次与最小值比较(初始的最小值为第一个元素,即标志flag为0),若元素比当前最小值小,则更新标志flag为当前最小的元素的下标
b、遍历完之后,将a[flag]元素与有序部分的后一个元素交换
c、回到步骤a直至数组全部有序
2)代码
public void SelectSort(long[] a){
int nElems = a.length;
int out, in,min;
System.out.println("排序前: ");
display(a);
for(out=0; out<nElems-1; out++){
min = out; //记录最小值的下标
for(in=out+1; in<nElems; in++){
if(a[in] < a[min]){
min = in;
}//end of if
}//end of for(in)
if(min != out){
//减少交换次数,消耗的时间更少
swap(a,out,min);
}
System.out.print((out+1)+":");
display(a);
}//end of for(out)
}//end of SelectSort
测试结果:
现在测试选择排序:
排序前:
5 4 1 3 6 8 2 9 0 7
1:0 4 1 3 6 8 2 9 5 7
2:0 1 4 3 6 8 2 9 5 7
3:0 1 2 3 6 8 4 9 5 7
4:0 1 2 3 6 8 4 9 5 7
5:0 1 2 3 4 8 6 9 5 7
6:0 1 2 3 4 5 6 9 8 7
7:0 1 2 3 4 5 6 9 8 7
8:0 1 2 3 4 5 6 7 8 9
9:0 1 2 3 4 5 6 7 8 9
运行时间为:3毫秒
3)复杂度
a、首先,在选择排序中,下标小于等于out的位置的数据项总是有序的
b、选择排序和冒泡排序执行了相同次数的比较,但是交换次数更少。
c、虽然选择和冒泡一样,复杂度都是O(n2),但是当N值较小时,而且交换时间级比比较时间级大得多时,选择排序还是要快得多的
3、插入排序
1)原理
a、局部有序:在冒泡和选择排序中,数据项在某个时刻是完全有序的,而在插入排序中,某组数据仅仅是局部有序的
b、被标记的元素:在这个元素的左侧(依据本文的情况)是有序的,从这个元素开始之后的元素是尚未被排序的。
c、插入排序:从被标记元素开始将其按照顺序插入前面的有序序列。被标记的元素依次与前面的有序的元素比较,若有序元素比该标记大,则向右移动一位,直到有序序列中的元素不大于被标记元素或数组到头。然后将这个被标记的元素插入相应位置
2)代码
public void InsertSort(long[] a){
//具有局部有序的特征
//被标记的元素(比如,规定被标记的元素右边是无序的,其左边是有序的
int out,in;
int nElems=a.length;
System.out.println("排序前: ");
display(a);
for(out = 1; out<nElems; out++){
long tmp = a[out];//tmp就是标记元素
in = out;
while(in >0 && a[in-1] >= tmp){
//每一趟循环将把比标记元素大的元素后移一位,比较跟移位同步进行,节省了时间
//当in=0或遇到比标记元素小的元素时跳出循环,此时in即为标记元素在有序序列的位置
a[in] = a[in-1];
System.out.print("被标记的元素为="+tmp+"内循环(比较的有序元素) i="+in+":");
display(a);
in--;
}
a[in] = tmp;//插入被标记元素
System.out.print(out+":");
display(a);
}//end of for(out)
}//end of InsertSort
测试结果
现在测试插入排序:
排序前:
5 4 1 3 6 8 2 9 0 7
被标记的元素为4;比较的有序元素> i=1:5 5 1 3 6 8 2 9 0 7
1:4 5 1 3 6 8 2 9 0 7
被标记的元素为1;比较的有序元素> i=2:4 5 5 3 6 8 2 9 0 7
被标记的元素为1;比较的有序元素> i=1:4 4 5 3 6 8 2 9 0 7
2:1 4 5 3 6 8 2 9 0 7
被标记的元素为3;比较的有序元素> i=3:1 4 5 5 6 8 2 9 0 7
被标记的元素为3;比较的有序元素> i=2:1 4 4 5 6 8 2 9 0 7
3:1 3 4 5 6 8 2 9 0 7
4:1 3 4 5 6 8 2 9 0 7
5:1 3 4 5 6 8 2 9 0 7
被标记的元素为2;比较的有序元素> i=6:1 3 4 5 6 8 8 9 0 7
被标记的元素为2;比较的有序元素> i=5:1 3 4 5 6 6 8 9 0 7
被标记的元素为2;比较的有序元素> i=4:1 3 4 5 5 6 8 9 0 7
被标记的元素为2;比较的有序元素> i=3:1 3 4 4 5 6 8 9 0 7
被标记的元素为2;比较的有序元素> i=2:1 3 3 4 5 6 8 9 0 7
6:1 2 3 4 5 6 8 9 0 7
7:1 2 3 4 5 6 8 9 0 7
被标记的元素为0;比较的有序元素> i=8:1 2 3 4 5 6 8 9 9 7
被标记的元素为0;比较的有序元素> i=7:1 2 3 4 5 6 8 8 9 7
被标记的元素为0;比较的有序元素> i=6:1 2 3 4 5 6 6 8 9 7
被标记的元素为0;比较的有序元素> i=5:1 2 3 4 5 5 6 8 9 7
被标记的元素为0;比较的有序元素> i=4:1 2 3 4 4 5 6 8 9 7
被标记的元素为0;比较的有序元素> i=3:1 2 3 3 4 5 6 8 9 7
被标记的元素为0;比较的有序元素> i=2:1 2 2 3 4 5 6 8 9 7
被标记的元素为0;比较的有序元素> i=1:1 1 2 3 4 5 6 8 9 7
8:0 1 2 3 4 5 6 8 9 7
被标记的元素为7;比较的有序元素> i=9:0 1 2 3 4 5 6 8 9 9
被标记的元素为7;比较的有序元素> i=8:0 1 2 3 4 5 6 8 8 9
9:0 1 2 3 4 5 6 7 8 9
运行时间为:8毫秒
3)复杂度
第一趟比较最多需要1次,第二趟最多2次,以此类推,有
1+2+3+...+(N-1)=N*(N-1)/2
而在发现插入点之前,平均只有数据量的一半进行了比较,所有又得到比较次数为
N*(N-1)/4
复制移动数据的次数大致等于比较的次数,然而复制移动与交换的时间消耗不同,所以插入排序虽然时间复杂度是O(N2)级别的,但是要比冒泡排序快一倍,比选择排序略快一些(PS:测试结果中由于加入了很多输出语句,所以实际运行时间较多)。
4、三个排序算法的小结与比较
排序方法 | 平均情况 | 最好情况 | 最坏情况 | 空间复杂度 | 稳定性 | 复杂性 |
---|---|---|---|---|---|---|
插入排序 | O(N2) | O(N2) | O(N) | O(1) | 稳定 | 简单 |
冒泡排序 | O(N2) | O(N2) | O(N) | O(1) | 稳定 | 简单 |
选择排序 | O(N2) | O(N2) | O(N2) | O(1) | 不稳定 | 简单 |