推荐几个动画演示的网站:
冒泡排序
- 从数组头开始,比较相邻的元素。如果第一个比第二个大(小),就交换它们两个
- 对每一对相邻元素作同样的工作,从开始第一对到尾部的最后一对,这样在最后的元素应该会是最大(小)的数
- 重复步骤1~2,重复次数等于数组的长度,直到排序完成
代码实现
对下面数组实现排序:{24, 7, 43, 78, 62, 98, 82, 18, 54, 37, 73, 9}
代码实现
public class BubbleSort {
public static final int[] ARRAY = {
24, 7, 43, 78, 62, 98, 82, 18, 54, 37, 73, 9};
public static void main(String[] args) {
print(ARRAY);
System.out.println("============================================");
print(sort(ARRAY));
}
public static int[] sort(int[] array) {
if (array.length == 0) {
return array;
}
for (int i = 0; i < array.length; i++) {
//array.length - 1 -i 已经冒泡到合适位置无需在进行排序,减少比较次数
for (int j = 0; j < array.length - 1 -i; j++) {
//前面的数大于后面的数交换
if (array[j + 1] < array[j]) {
int temp = array[j + 1];
array[j + 1] = array[j];
array[j] = temp;
}
}
}
return array;
}
public static void print(int[] array) {
for (int i : array) {
System.out.print(i + " ");
}
System.out.println("");
}
}
时间复杂度
对于上面12个数据项,从第一个元素开始,第一趟比较了11次,第二趟比较了10次,依次类推,一直到最后一趟,就是:
11 + 10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 = 66次
若有n个元素,则第一趟比较为(n-1)次,第二趟比较为(n-2)次,依次类推:
(n-1) + (n-2) + (n-3) + ...+ 2 + 1 = n * (n-1)/2
在大O表示法中,去掉常数系数和低阶项,该排序方式的时间复杂度为:O(n2)
算法稳定性
假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。——百度百科
在代码中可以看到,array[j + 1] = array[j]
的时候,我们可以不移动array[i]
和array[j]
,所以冒泡排序是稳定的。
选择排序
- 找到数组中最大(或最小)的元素
- 将它和数组的第一个元素交换位置(如果第一个元素就是最大(小)元素那么它就和自己交换)
- 在剩下的元素中找到最大(小)的元素,将它与数组的第二个元素交换位置。如此往复,直到将整个数组排序。
代码实现
对下面数组实现排序:{87, 23, 7, 43, 78, 62, 98, 81, 18, 53, 73, 9}
动图演示
代码实现
public class SelectionSort {
public static final int[] ARRAY = {
87, 23, 7, 43, 78, 62, 98, 81, 18, 53, 73, 9};
public static int[] sort(int[] array) {
if (array.length == 0) {
return array;
}
for (int i = 0; i < array.length; i++) {
//最小数的下标,每个循环开始总是假设第一个数最小
int minIndex = i;
for (int j = i; j < array.length; j++) {
//找到最小索引
if (array[j] < array[minIndex]) {
//保存最小索引
minIndex = j;
}
}
//最小索引的值
int temp = array[minIndex];
array[minIndex] = array[i];
array[i] = temp;
}
return array;
}
public static void print(int[] array) {
for (int i : array) {
System.out.print(i + " ");
}
System.out.println("");
}
public static void main(String[] args) {
print(ARRAY);
System.out.println("============================================");
print(sort(ARRAY));
}
}
时间复杂度
很明显,和冒泡排序相比,在查找最小(或最大)元素的索引,比较次数仍然保持为O(n2)
,但元素交换次数为O(n)。
算法稳定性
选择排序是给每个位置选择当前元素最小的,比如给第一个位置选择最小的,在剩余元素里面给第二个元素选择第二小的,依次类推,直到第n-1个元素,第n个元素不用选择了,因为只剩下它一个最大的元素了。那么,在一趟选择,如果一个元素比当前元素小,而该小的元素又出现在一个和当前元素相等的元素后面,那么交换后稳定性就被破坏了。举个例子,数组5,8,5,2,9,我们知道第一遍选择第1个元素5会和2交换,那么原序列中两个5的相对前后顺序就被破坏了,所以选择排序是一个不稳定的排序算法。
插入排序
当我们在玩扑克牌的时候,总是在牌堆里面抽取最顶部的一张然后按顺序在手中排列。
插入排序是指在待排序的元素中,假设前面n-1(其中n>=2)个数已经是排好顺序的,现将第n个数插到前面已经排好的序列中,然后找到合适自己的位置,使得插入第n个数的这个序列也是排好顺序的。
- 对于未排序数据(一般取数组的二个元素,把第一个元素当做有序数组),在已排序序列中从左往右扫描,找到相应位置并插入。
- 为了给要插入的元素腾出空间,需要将插入位置之后的已排序元素在都向后移动一位。
代码实现
对下面数组实现排序:{15, 51, 86, 70, 6, 42, 26, 61, 45, 81, 17, 1}
动图演示
代码实现
public class InsertionSort {
public static final int[] ARRAY = {
15, 51, 86, 70, 6, 42, 26, 61, 45, 81, 17, 1};
public static int[] sort(int[] array) {
if (array.length == 0) {
return array;
}
//待排序数据,改数据之前的已被排序
int current;
for (int i = 0; i < array.length - 1; i++) {
//已被排序数据的索引
int index = i;
current = array[index + 1];
//将当前元素后移一位
while (index >= 0 && current < array[index]) {
array[index + 1] = array[index];
index--;
}
//插入
array[index + 1] = current;
}
return array;
}
public static void print(int[] array) {
for (int i : array) {
System.out.print(i + " ");
}
System.out.println("");
}
public static void main(String[] args) {
print(ARRAY);
System.out.println("============================================");
print(sort(ARRAY));
}
}
时间复杂度
在上面图示中,第一趟循环比较一次,第二趟循环两次,依次类推,则最后一趟比较n-1次:
1 + 2 + 3 +… + n-1 = n*(n-1)/2
也就是说,在最坏的情况下(逆序),比较的时间复杂度为O(n2)
在最优的情况下,即while循坏总是假的,只需当前数跟前一个数比较一下就可以了,这时一共需要比较n-1次,时间复杂度为O(n)。
算法稳定性
在比较的时候,过两个数相等的话,不会进行移动,前后两个数的次序不会发生改变,所以插入排序是稳定的。
希尔排序
一种基于插入排序的快速的排序算法。简单插入排序对于大规模乱序数组很慢,因为元素只能一点一点地从数组的一端移动到另一端。例如,如果主键最小的元素正好在数组的尽头,要将它挪到正确的位置就需要n-1次移动。
希尔排序为了加快速度简单地改进了插入排序,也称为缩小增量排序。
希尔排序是把待排序数组按一定的数量分组,对每组使用直接插入排序算法排序;然后缩小数量继续分组排序,随着数量逐渐减少,每组包含的元素越来越多,当数量减至 1 时,整个数组恰被分成一组,排序便完成了。这个不断缩小的数量,就构成了一个增量序列,这里的数量称为增量。
代码实现
public class ShellSort {
public static final int[] ARRAY = {
12, 9, 6, 11