在学计算机的最开始的时候我们就被告知常用的一些排序算法,冒泡,选择,插入,快排等等,随着工作的深入,这些东西都慢慢的被抛到脑后了,最近几年开始做架构方面的工作,慢慢的对于很多系统的性能开始进行分析,慢慢的开始需要运用到一些比价深入的知识,于是重新开始拿起算法,打磨自己的知识。
最近把常见的一些算法查看了一遍,对于快排心声疑虑,名字叫做快排,那么运行起来是不是真的有这么快?
索性自己写个程序对比一下试试:
package sort.compare;
import java.util.Random;
import com.alibaba.fastjson.JSON;
public class SortCompare {
/**
* 冒泡: 1. 外层循环保证每次冒泡出一个最大值 2. 内层循环保证每次从长度减1冒泡出最大值
*
* @param toSort
*/
public static void bubbleSort(int[] toSort) {
for (int i = 0; i < toSort.length; i++) {
for (int j = 0; j < toSort.length - i - 1; j++) {
if (toSort[j] > toSort[j + 1]) {// 大的往后
int tmp = toSort[j];
toSort[j] = toSort[j + 1];
toSort[j + 1] = tmp;
}
}
}
}
/**
* 鸡尾酒排序: 1. 双向比较, 2. 每次找出来一个最大值跟一个最小值
*
* @param toSort
*/
public static void cocktail(int[] toSort) {
for (int i = 0; i < toSort.length / 2; i++) { // 中间数即可
// 大的往后调
for (int n = i; n < toSort.length - i - 1; n++) {// 每次循环末尾前进一个位置
if (toSort[n] > toSort[n + 1]) {
int tmp = toSort[n];
toSort[n] = toSort[n + 1];
toSort[n + 1] = tmp;
}
}
for (int m = toSort.length - i - 1; m > i; m--) {
if (toSort[m] < toSort[m - 1]) {
int tmp = toSort[m];
toSort[m] = toSort[m - 1];
toSort[m - 1] = tmp;
}
}
}
}
/**
* 选择排序: 1.外层循环保证每次选择一个最大值,2,内存循环保证每次冲长度建议选择最大值
*
* @param toSort
*/
public static void selectSort(int[] toSort) {
for (int i = 0; i < toSort.length; i++) {
int pos = 0;
for (int j = 1; j < toSort.length - i; j++) {
if (toSort[pos] < toSort[j]) {// 如果小于pos
pos = j;
}
}
int tmp = toSort[pos];
toSort[pos] = toSort[toSort.length - i - 1];
toSort[toSort.length - i - 1] = tmp;
}
}
public static void selectSort2(int[] toSort) {
for (int i = 0; i < toSort.length / 2; i++) {
int bigPos = i;
int smallPos = i;
for (int n = i; n < toSort.length - i; n++) {// 每次循环末尾前进一个位置
if (toSort[n] > toSort[bigPos]) {
bigPos = n;
}
if (toSort[n] < toSort[smallPos]) {
smallPos = n;
}
}
int small = toSort[smallPos];
// 交换最大值
int tmp = toSort[bigPos];
toSort[bigPos] = toSort[toSort.length - i - 1];
toSort[toSort.length - i - 1] = tmp;
// 交换最小值
if (small - toSort[smallPos] == 0) { // 如果不一致,说明被最大值替换过了
tmp = toSort[smallPos];
toSort[smallPos] = toSort[i];
toSort[i] = tmp;
}else{
tmp = toSort[bigPos];
toSort[bigPos] = toSort[i];
toSort[i] = tmp;
}
}
}
/**
* 快排: 同时从左边找到第一个
*
* @param toSort
* @param l
* @param r
*/
public static void quickSort(int[] toSort, int l, int r) {
if (l >= r)
return;
int flag = toSort[l];// 取第一个值为flag
int i = l;
int j = r;
while (i < j) {// 往中间扫描,保证中间结束
while (i < r) {
if (toSort[i] > flag) { // 找到一个大于目标的值
break;
}
i++;
}
while (j > l) {
if (toSort[j] < flag) { // 找到一个小于目标的值
break;
}
j--;
}
// 交换二者的位置
int tmp = toSort[i];
toSort[i] = toSort[j];
toSort[j] = tmp;
}
toSort[l] = toSort[i];
toSort[i] = flag;
quickSort(toSort, l, i - 1);
quickSort(toSort, i + 1, r);
}
public static void main(String[] args) {
int length = 50000;
int[] toSort = buildRandomArray(length);
toSort = buildRandomArray(length);
long start = System.currentTimeMillis();
cocktail(toSort);
long end = System.currentTimeMillis();
System.out.println("---------------->" + length + " bubble sort : " + (end - start));
toSort = buildRandomArray(length);
start = System.currentTimeMillis();
selectSort2(toSort);
end = System.currentTimeMillis();
System.out.println("---------------->" + length + " select sort : " + (end - start));
toSort = buildRandomArray(length);
start = System.currentTimeMillis();
selectSort(toSort);
end = System.currentTimeMillis();
System.out.println("---------------->" + length + " quick sort : " + (end - start));
}
private static int[] buildRandomArray(int length) {
int[] toSort = new int[length];
Random r = new Random();
for (int i = 0; i < length; i++) {
toSort[i] = r.nextInt(length);
}
return toSort;
}
}
然后分别使用不同的数组测试了一下,下面是测试的结果:
|
因为所使用的数据均为完全随机的数据,所以实际上的数据分布应该基本满足n*log(n)的曲线。
为了可以简要的说明情况,生成下面的图,
这个是1-10万,每隔1万的测试结果。
很明显,冒泡比较慢,但是看起来选择跟快排的曲线基本是重合的,貌似快排要稍微好一些,但是并不明显
这个是10w-40w的测试,再往后需要的时间越来越多,所以就不继续了。
可以看到跟前面的结论基本相同,按照上面图的规律,看起来冒泡的优化空间看来还是蛮大的。