1、冒泡排序(Bubble Sort)
冒泡排序算法的原理如下:
(1)比较相邻的元素。如果第一个比第二个大,就交换他们两个
(2)对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
(3)针对所有的元素重复以上的步骤,除了最后一个。
(4)持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
我们要对一组数据 4,5,6,3,2,1,从小到大进行排序。第一次冒泡操作的详细过程就是这样:
可以看出,经过一次冒泡操作之后,6 这个元素已经存储在正确的位置上。要想完成所有数据的排序,我们只要进行 6 次这样的冒泡操作就行了。
详细代码如下:
public static int[] bubbleSort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
return arr;
}
由代码片段可知冒泡排序时间复杂度为O(n^2)。
实际上上述冒泡过程在第5次时就已经得到我们所需要的结果了,所以上述代码还可继续优化
public static int[] bubbleSort2(int[] arr) {
// 提前退出冒泡循环的标志位
boolean flag = false;
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
flag = true; // 表示有数据交换
}
}
if (!flag) break; // 没有数据交换,提前退出
}
return arr;
}
2、插入排序(Insertion Sort)
插入排序的基本思想:将数组中的数据分为两个区间,已排序区间和未排序区间。初始已排序区间只有一个元素,就是数组的第一个元素。插入算法的核心思想是取未排序区间中的元素,在已排序区间中找到合适的插入位置将其插入,并保证已排序区间数据一直有序。重复这个过程,直到未排序区间中元素为空,算法结束。
如图所示,要排序的数据是 4,5,6,1,3,2,其中左侧为已排序区间,右侧是未排序区间。
插入排序包含两种操作,一种是元素的比较,一种是元素的移动。当我们需要将一个数据 a 插入到已排序区间时,需要拿 a 与已排序区间的元素依次比较大小,找到合适的插入位置。找到插入点之后,我们还需要将插入点之后的元素顺序往后移动一位,这样才能腾出位置给元素 a 插入。
详细代码如下:
public static int[] insertSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int value = arr[i];
int j = i - 1;
for (; j >= 0; j--) {
if (arr[j] > value) {
arr[j + 1] = arr[j];//移动数据位置
} else {
break;
}
}
arr[j + 1] = value;//插入数据
}
return arr;
}
由代码片段可知冒泡排序时间复杂度为O(n^2)。
3、选择排序(Selection Sort)
选择排序算法的实现思路有点类似插入排序,也分已排序区间和未排序区间。但是选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。
详细代码如下:
public static int[] selectSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
int index = i;
//获取未排序区间最小值
for (int j = i+1; j < arr.length; j++) {
if (arr[j] < arr[index]) {
index = j;
}
}
//将未排序区间最小值与已排序区间末尾元素交换
int tmp = arr[i];
arr[i] = arr[index];
arr[index] = tmp;
}
return arr;
}
由代码片段可知冒泡排序时间复杂度为O(n^2)。
4、小结:
由上述分析可知冒泡排序、插入排序、选择排序时间复杂度都是O(n^2 ),但在实际应用中插入排序却更受欢迎,比如给定一个有序数组,冒泡排序和插入排序的最好时间复杂度为O(n),而选择排序的时间复杂度仍然为为O(n^2),而且如果碰到大小相等元素选择排序仍然会移动元素的位置即选择排序是一种不稳定算法。分析代码可知冒泡排序需要 3 个赋值语句,而插入排序只有一个,所以交换操作总耗时冒泡排序是插入排序的3倍,另外插入排序的优化空间也更大,比如折半插入排序(二分插入排序)、希尔排序。
【1】参考文献:https://time.geekbang.org/column/article/41802