快速排序算法
author kingsea 2014.8.27 10:37am
基本思想
快速排序(Quicksort)是对冒泡排序的一种改进。由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
算法介绍
设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
一趟快速排序的算法是:
- 设置两个变量i、j,排序开始的时候:i=0,j=N-1;
- 以第一个数组元素作为关键数据,赋值给key,即key=A[0];
- 从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]互换;
- 从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换;
- 重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。
排序演示
假设用户输入了如下数组:
下标 | 0 | 1 | 2 | 3 | 4 | 5 |
数据 | 6 | 2 | 7 | 3 | 8 | 9 |
创建变量i=0(指向第一个数据), j=5(指向最后一个数据), k=6(赋值为第一个数据的值)。
我们取走了下标0的数据,于是,我们需要找到一个数字来替换他。由于我们要把所有比6小的数移动到左面,所以我们可以开始寻找比6小的数并从右往左找。别急,我们要按顺序找哦。不断递减j的值,我们发现下标3的数据比6小,于是把3移到下标0(实际是i指向的位置。代码中要用i,因为后面还会循环这个步骤,不用i的话第二次循环:
下标 | 0 | 1 | 2 | 3 | 4 | 5 |
数据 | 3 | 2 | 7 | 6 | 8 | 9 |
i=0 j=3 k=6
由于变量k已经储存了下标0的数据,所以我们可以放心的把下标0覆盖了。如此一来,下标3虽然有数据,但是相当于没有了,因为数据已经复制到别的地方了。于是我们再找一个数据来替换他。这次要变成找比k大的了,而且要从前往后找了。递加变量i,发现下标2是第一个比k大的,于是用下标2的数据7替换j指向的下标3的数据,数据状态变成下表:
下标 | 0 | 1 | 2 | 3 | 4 | 5 |
数据 | 3 | 2 | 6 | 7 | 8 | 9 |
i=2 j=3 k=6
重复上面的步骤,递减变量j。这时,我们发现i和j“碰头”了:他们都指向了下标2。于是,循环结束,把k填回下标2里,即得到结果。
如果i和j没有碰头的话,就递加i找大的,还没有,就再递减j找小的,如此反复,不断循环。注意判断和寻找是同时进行的。
注意:快速排序不会直接得到最终结果,只会把比k大和比k小的数分到k的两边。(你可以想象一下i和j是两个机器人,数据就是大小不一的石头,先取走i前面的石头留出回旋的空间,然后他们轮流分别挑选比k大和比k小的石头扔给对面,最后在他们中间把取走的那块石头放回去,于是比这块石头大的全扔给了j那一边,小的全扔给了i那一边。只是这次运气好,扔完一次刚好排整齐。)为了得到最后结果,需要再次对下标2两边的数组分别执行此步骤,然后再分解数组,直到数组不能再分解为止(只有一个数据),才能得到正确结果。
package com.sea.sort.demo;
/**
* quick sort
*
* @author sea
*
*/
public class QuickSort {
public void sort(int arr[], int low, int high) {
int i = low;
int j = high;
int key = arr[low];
//end the loop when i == j
while( i != j){
//end the loop when i == j
while (j > 0) {
if (i == j) {
break;
}
//find the number which is lower than key from the right
if (arr[j] < key) {
arr[i] = arr[j];
arr[j] = key;
break;
}
j--;
}
//end the loop when i == j
while (i < arr.length) {
if (i == j) {
break;
}
//find the number which is greater than key from the left
if (arr[i] > key) {
arr[j] = arr[i];
arr[i] = key;
break;
}
i++;
}
}
if (i > low) {
sort(arr,low,i-1);
}
if (i < high) {
sort(arr,i+1,high);
}
}
public static void main(String[] args) {
int arrLength = 100000;
int[] arr = new int[arrLength];
for (int i = 0; i < arrLength; i++) {
arr[i] = (int) (Math.random() * arrLength);
}
System.out.println("The generated arr order is:");
System.out.println(Arrays.toString(arr));
QuickSort qs = new QuickSort();
/**
* Start sorting at: 1478188841345
* Complete sorting at: 1478188841346
*/
long startTime = new Date().getTime();
System.out.println("Start sorting at: " + startTime);
qs.sort(arr, 0, arr.length - 1);
long endTime = new Date().getTime();
System.out.println("Complete sorting at: " + endTime);
System.out.println("The total used time is: " + (endTime - startTime));
System.out.println("After sorting, the new order is: ");
System.out.println(Arrays.toString(arr));
}
性能测试
单位是毫秒级
数量\排序 | 冒泡排序 | 快速排序 | 插入排序 | 选择排序 |
---|---|---|---|---|
一万 | 403 | 7 | 45 | 237 |
十万 | 41475 | 65 | 3232 | 17101 |
一百万 | 412 | |||
一千万 | 3490 |