快速排序算法:快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。
快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。
算法步骤:
1 从数列中挑出一个元素,称为 “基准”(pivot),
2 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
3 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
(以上解释来至于:https://www.runoob.com/w3cnote/the-friendship-algorithm-the-big-bang-theory.html)
在给算法比较效率的时候,选择了System.nanoTime()方法:
在jdk6的参考文档中时这样解释这个方法的:
public static long nanoTime()返回最准确的可用系统计时器的当前值,以毫微秒为单位。
此方法只能用于测量已过的时间,与系统或钟表时间的其他任何时间概念无关。返回值表示从某一固定但任意的时间算起的毫微秒数(或许从以后算起,所以该值可能为负)。此方法提供毫微秒的精度,但不是必要的毫微秒的准确度。它对于值的更改频率没有作出保证。在取值范围大于约 292 年(263 毫微秒)的连续调用的不同点在于:由于数字溢出,将无法准确计算已过的时间。
例如,测试某些代码执行的时间长度:
long startTime = System.nanoTime();
// … the code being measured …
long estimatedTime = System.nanoTime() - startTime;
返回:
系统计时器的当前值,以毫微秒为单位。
由于时间的单位是极其的小,计算机的cpu工作速度有那么快,如果我们测试的数组不足够大的话,实验的结果可能会不明显。所以我们在这里设置了一个容量为1000的数组以供检验。
1、快速排序算法:
/**
* @author Administrator
* @time 2019年7月12日 上午9:46:48
*
* 快速排序算
*/
public class QuickSort {
public static void quickSort(int[] arr,int left,int right) {
//定义i,j并且将传递来的参数分别赋值给i,j
int i = left;
int j = right;
//判断:当数组最左边的数的下标反而大于最右边的数的下标时,则参数传递存在错误
if(i > j) {
return;
}
//设置基准数
int temp = arr[left];
//判断:当数组下标左边不等于右边的时候,才可以进行查找与替换
while(i != j) {
/*
* 1.先在基准数右边进行查找比基准数小的数
* 2.找不到,便一直向数组的源头逐步向上找
* 3.找到时,便停止继续查找
*/
while(arr[j] >= temp && i < j) {
j--;
}
/*
* 1.先在基准数左边边进行查找比基准数大的数
* 2.找不到,便一直向数组的末尾逐步向下找
* 3.找到时,便停止继续查找
*/
while(arr[i] <= temp && i < j) {
i++;
}
/*
* 当满足最左下标小于最右下标时,将刚刚在数组右端比基准数小的数
* 与在数组中左端比基准数大的数,进行相互替换,以达到排序的作用
*/
if(i < j) {
int tp = arr[j];
arr[j] = arr[i];
arr[i] = tp;
}
}
/*
* 现将i=j时,i所对应的数组中的数的赋值在原基准数的位置
* (本例中默认是数组的第一个位置)
* 将基准数放置在刚才i=j的位置上
*/
arr[left] = arr[i];
arr[i] = temp;
//执行递归操作,分别对刚刚第一步排序的产生在基准书左边与右边的数组进行进一步的排序
quickSort(arr,left,i-1);
quickSort(arr,i+1,right);
}
public static void main(String[] args) {
int[] arr = new int[1000];;
/*系统需要产生一个足够大的数组
* 因为System.nanoTime()是以毫微秒为单位的时间计算
* 计算机运行速度很快,我们需要产生一个足够大的数组来排序,
* 从而来观察不同算法之间效率的差异
*/
for(int i=0;i<1000;i++) {
if(i%2 == 0) {
arr[i] = 2*i-i;
}
else {
arr[i] = 2*i;
}
}
//遍历原数组
System.out.print("原始数组:");
for(int x:arr) {
System.out.print(x+" ");
}
long start = System.nanoTime();
quickSort(arr,0,arr.length-1);
long end = System.nanoTime();
//遍历排序后数组
System.out.print("\n快速排序后:");
for(int x : arr) {
System.out.print(x+" ");
}
System.out.println("\n快速排序算法消耗时间:"+(end-start));
}
}
/**
* @author Administrator
* @time 2019年7月12日 上午9:55:51
*
* 冒泡排序
*/
public class BubbleSort {
//进行冒泡排序
public static void bubbleSort(int[] arr) {
int len = arr.length;
//冒泡排序排序次数为length-1次,
for(int i=0;iarr[j+1]){
//进行位置的互换
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
}
public static void main(String[] args) {
int[] arr = new int[1000];
// 系统自己产生一个足够大的数组
for (int i = 0; i < 1000; i++) {
if (i % 2 == 0) {
arr[i] = 2 * i - i;
} else {
arr[i] = 2 * i;
}
}
//遍历原数组
System.out.print("原始数组:");
for(int x:arr) {
System.out.print(x+" ");
}
long start = System.nanoTime();
bubbleSort(arr);
long end = System.nanoTime();
//遍历排序后数组
System.out.print("\n冒泡排序后:");
for(int x : arr) {
System.out.print(x+" ");
}
System.out.println("\n冒泡排序算法消耗时间:"+(end-start));
}
}
结论:在此实验中,对于相同的实验数组,在快速排序算法和冒泡排序两种算法的在同样环境下运行结果差异比较明显,在这个实验中,快速排序算法处理的效率大约是冒泡排序效率的十倍。
在相关算法书中也指出:快速排序算法的时间复杂度是: O = f(nlogn);
而冒泡排序的时间复杂度是: O = f(n^2).
而快速排序算法在最坏情况下,可能会退化到冒泡排序(可能性极低)。