今天对四种常见的排序算法冒泡排序、选择排序、插入排序和希尔排序进行简单分析以及时间复杂度的测试。
一、冒泡排序
冒泡排序是排序算法中较为简单的一种,它遍历所有的数据,每次对相邻元素进行两两比较,如果顺序和预先规定的顺序不一致,则进行位置交换;这样一次遍历会将最大或最小的数据上浮到顶端,之后再重复同样的操作,直到所有的数据有序。
如果有N个数据,则需要N^2次比较。时间复杂度较高,但稳定性较好。
动态演示:
Java代码实现
public class 冒泡排序_BubbleSort {
public static void main(String[] args) {
int arr[] = { 12, 76, 37, 27, 98, 10, 67, 44, 35, 70 };
for (int i = 0; i < arr.length - 1; i++) {
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[i]) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
for (int a : arr)
System.out.println(a);
}
}
二、选择排序
选择排序看起来很直观,先在数据中找出最大或最小的元素,放到序列的起始;然后再从余下的数据中继续寻找最大或最小的元素,依次放到排序序列中,直到所有数据排序完成。很显然,对于N个数而言,无论N为多大,比较次数也为N^2次。不适宜大量数据的排序。
动态演示:
Java代码实现
public class 选择排序_SelectionSort {
public static void main(String[] args) {
int arr[] = { 12, 76, 37, 27, 98, 10, 67, 44, 35, 70 };
// 选择排序的优化
for (int i = 0; i < arr.length - 1; i++) {
int k = i;
for (int j = k + 1; j < arr.length; j++) {// 选最小的记录
if (arr[j] < arr[k]) {
k = j; // 记下目前找到的最小值所在的位置
}
}
// 在内层循环结束,也就是找到本轮循环的最小的数以后,再进行交换
if (i != k) {
int temp = arr[i];
arr[i] = arr[k];
arr[k] = temp;
}
}
for (int a : arr) {
System.out.println(a);
}
}
}
三、插入排序
插入排序通过构建有序序列,对于未排序的数据序列,在已排序序列中从后向前扫描,找到相应的位置并插入,类似打扑克牌时的码牌。插入排序有一种优化的算法,可以进行拆半插入。
基本思路是先将待排序序列的第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列;然后从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置,直到所有数据都完成排序;如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。
动态演示
Java代码实现
public class 插入排序_InsertionSort {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int arr[] = { 12, 76, 37, 27, 98, 10, 67, 44, 35, 70 };
int i, j;
for (i = 1; i < arr.length; i++) {
// 第一个元素无需比较交换,因此index从1开始遍历
int temp = arr[i];
for (j = i - 1; j >= 0; j--) {
// 后面的数字要和前面的数字进行比较,因此index从i-1开始
if (arr[j] > temp) {
arr[j + 1] = arr[j];
} else {
break;
}
}
arr[j + 1] = temp;
}
for (int b : arr)
System.out.println(b);
}
}
四、希尔排序
希尔排序(是插入排序的一种又称“缩小增量排序”,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
算法图解
Java代码实现
public class 希尔排序_ShellSort {
public static void main(String[] args) {
int arr[]=new int [50000];
for(int c=0;c<50000;c++) {
arr[c]=50000-c;
}
int i, j, k;
for (int val = arr.length / 4; val > 0; val=val/4) {
// 将要排序的数组进行分组,然后对本次的所有分组做直接插入排序
for (k = 0; k <4; k++) {
for (i = val + k; i <= arr.length - val; i = val+i) {
int temp = arr[i];
for (j = i; j >= val && temp < arr[j - val]; j =j-val) {
arr[j] = arr[j - val];
}
arr[j] = temp;
}
}
}
for (int a : arr)
System.out.println(a);
}
}
四种排序算法复杂度比较
算法时间复杂度检验
对于四种常见的算法时间复杂度进行测试检验。
测试时,按最坏的情况定义数组,初始数组是一个从大到小排列的数组,要求从小到大重新排序。数据量分别取1万,5万,10万个。对于每组数据,反复进行6次排序,纪录每次排序所用时间,剔除最大值和最小值后取平均值,以避免粗大误差。
具体实验数据统计如下:
通过本次不太严谨的实验暂且可以得出结论:
当数据量较小时,优先使用冒泡排序或者希尔排序;
当数据量较大时,首选希尔排序。
注:以上结论仅对于本文中四种排序算法而言