一.冒泡排序
算法简述
冒泡排序法(也叫泡沫算法,鸡尾酒算法),是一种简单但运行效率最慢的排序算法,它重复地走访将要排序的元素,对两两相邻的元素进行比较,若发现其顺序与我们将要排列的顺序不符则将这两个元素交换位置,经过每一轮的比较和交换后一个最小的元素会浮(被交换)到排序元素的首位,这也是该算法名字的由来。
算法运作
- 对两个元素进行比较,若前者大于后者,则将这两个元素交换
- 从右至左对数组中两两相邻的元素执行1中的操作
- 经过步骤2的过程后最小的数浮到了排序元素的首位
- 除去步骤3中的最小数,对剩下的元素执行步骤2,3,4中的操作,直到排序元素只剩一个
Java实现代码
//交换数组中两个指定位置的元素
public static void swap(int[] a,int i,int j){
int tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
//冒泡排序,改进版
public static void bubbleSort(int[] a){
//使用一个标志量,一旦发现在一整轮的比较中无交换元素
//则表明其后的元素已经排好序了
boolean flag = true;
for(int i=0;i<a.length-1&&flag;i++){
flag = false;
for(int j=a.length-1;j>i;j--){
if(a[j]<a[j-1]){
swap(a, j-1, j);
//若是有交换元素,则将flag赋值为true
//表明下一轮(如果有的话)需要继续进行
flag = true;
}
}
}
}
算法复杂度分析
- 最好情况下:由于设置了一个标志量,当数组已经按从小到大的顺序排列,那么比较(n-1)次后就退出了循环,因而算法复杂度为:O(n)。
- 平均情况和最差情况: 最差情况是数组按从大到小的顺序排列,需要比较和交换的次数均为:n*(n-1)/2,算法复杂度为:O(n2),而平均情况时间代价为最差情况的一半,那么其交换次数为:(n-1)n/4,而比较次数不变仍为:n(n-1)/2,其时间复杂度仍然为:O(n2)。
二.选择排序
算法简述
选择排序法,是一种简单直观的算法,它通过在排序的元素中选择最小的元素放在排序元素的第一位,一步一步的缩小范围使得数组有序。
算法运作
- 从待排序的元素中选择出最小的元素与排序元素第一位交换
- 排除刚才找出的最小元素,将剩下的元素归为待排序的元素,循环执行步骤1,2直到待排序的元素个数为1
Java实现代码
public static void selectSort(int[] a){
for(int i=0;i<a.length-1;i++){
//使用一个临时量记录当前排序元素中的最小值的下标
int min = i;
for(int j=i+1;j<a.length;j++){
//如果有元素小于临时量记录对应的值,则更新临时量
if(a[j]<a[min]){
min = j;
}
}
//最小值和排序元素的第一位交换
swap(a,min,i);
}
}
算法复杂度分析
无论是最好,平均还是最差情况,其比较次数均为(n-1)*n/2,交换次数为:(n-1),因此时间复杂度为:O(n2)。
三.插入排序
算法简述
插入排序,从字面上可以理解为,将待元素插入到已排序的元素中,即找到待元素在已排序元素中的正确位置,相信大家都有玩过欢乐斗地主,当我们收到牌后,一般情况下都会依次将待排序的牌插入到前面已经排好序的牌组中,这个过程就是我们插入排序的思想。
算法运作
- 从数组第二位开始,依次选择对应位置的元素为待排序元素执行步骤2,直到可选择的元素为空
- 将待排序元素与其前一位元素进行比较,如果前者小于后者,那么交换这两个元素,循环执行步骤2至待排序元素大于或等于前一位元素
Java实现代码
public static void insertSort(int[] a){
int i,j;
//从第二位开始到最后一位
for(i=1;i<a.length;i++){
//内循环和冒泡排序类似,也是需要进行两两比较,只不过这
//里是循环到待排序元素大于或等于前一位元素就终止
//然后进入下一轮(如果有的话)
for(j=i;j>0&&a[j-1]>a[j];j--){
swap(a, j-1, j);
}
}
}
算法复杂度分析
- 最好情况下其比较次数为(n-1),交换次数为0,时间复杂度为:O(n)。
- 平均情况和最差情况:最差情况比较次数为(n-1)*n/2(即1+2+…+(n-1)),而一般认为平均情况的时间代价为最差情况时间代价的一半,即(n-1)*n/4,因此这两种情况的时间复杂度均为O(n2)。
三种算法的比较和总结
编写如下测试代码
public static void main(String... args){
//产生100000个随机数来测试排序算法
int[] a = new int[100000];
int[] b = new int[100000];
int[] c = new int[100000];
Random random = new Random();
for(int i=0;i<a.length;i++){
a[i] = random.nextInt(100000);
c[i] = b[i] = a[i];
}
Date date1 = new Date();
bubbleSort(a);
Date date2 = new Date();
System.out.println("冒泡排序花费的时间为:"+(date2.getTime()-date1.getTime())+"ms");
date1 = new Date();
selectSort(b);
date2 = new Date();
System.out.println("选择排序花费的时间为:"+(date2.getTime()-date1.getTime())+"ms");
date1 = new Date();
insertSort(c);
date2 = new Date();
System.out.println("插入排序花费的时间为:"+(date2.getTime()-date1.getTime())+"ms");
}
测试结果如下
冒泡排序花费的时间为:21982ms
选择排序花费的时间为:3941ms
插入排序花费的时间为:5598ms
测试结果分析
从测试结果很明显地看出,尽管这三种算法在平均情况下的时间复杂度均为:O(n2),但冒泡排序所花费时间要明显多于选择排序和插入排序,而选择排序花费时间又要少于插入排序算法。因而从性能上来说,选择排序>插入排序>冒泡排序。
三种排序算法时间复杂度总结如下表
最好情况 | 平均情况 | 最差情况 | |
---|---|---|---|
冒泡排序 | O(n) | O(n2) | O(n2) |
插入排序 | O(n) | O(n2) | O(n2) |
选择排序 | O(n2) | O(n2) | O(n2) |