快速排序(Quick Sort)
基本思想:
任意选择一数作为个基准数,通过一趟排序将要排序的数据分割成两个独立的部分。其中一部分所有数都小于基准数,另一部分所有数都大于基准数。然后按照同样的方法对分别对两部分数据进行排序,直到最后所有数据都变得有序。
基准数可以任意选取。
假设现在有这样一个初始序列:
首先,我们选取第一个数 5 作为基准数,并在开头和结尾分安插上一个哨兵 l 和 r。
哨兵 r 先出动,一步步向左移动,如果碰到了一个数比基准数要小,那么他就停下来。此时,换哨兵 l 出动,他也一步一步向右移动,直到碰到一个数比基准数大,然后他也停下。
现在交换哨兵 l 和哨兵 r 所处位置的值。
接下来,哨兵 r 又出动,一步步左移,当他再次碰到一个小于基准数的数,他又停下来。此时,换哨兵 l 出动,他也一步一步右移,直到又碰到一个大于基准数的数,他也停下。
现在交换哨兵 l 和哨兵 r 所处位置的值。
然后,重复上面的步骤,哨兵 r 再次向左移动直到遇到一个小于基准数的数。这时候,哨兵 r 已经与哨兵 l 碰头,将基准数和碰头位置的数交换后,第一趟排序就结束了。
与基准数交换交换,将基准数归位。此时基准数 5 左边的部分都比它小,右边的部分都比它大。
于是,第一趟排序就结束了。
现在,我们分别对左右两部分进行排序。
左边部分为2、3、1,以基准数 2 进行排序,排序完成后结果为 1、2、3。此时, 基准数 2 左边部分为 1,右边部分为 3,左右两边都只有一个数,不用排序。
右边部分为 7、9、6、9、8、10,以基准数 7 进行排序,排序结束后结果为 6、7、9、9、8、10。此时,基准数 7 左边只有一个数,不用排序。基准数 7 右边用同样的方法,最终排序结束后为 8、9、9、10。
这时候,原始序列已经变成了 1、2、3、5、6、7、8、9、9、10。到此,排序完全结束。
话不多说,上代码。
算法实现:
/**
* 进行快速排序,[left, right]为将要进行排序的区间,经排序后此区间才有序
* @param arr 待排序数组
* @param left 数组起始下标
* @param right 数组结束下标
*/
public static void quickSort(int[] arr, int left, int right) {
if (left >= right) // 如果起始下标 >= 结束下标,直接返回
return;
int l = left;
int r = right;
int base = arr[left]; // 基准数
while (l < r) {
while (l < r && arr[r] >= base) // 从右往左遍历,直到找到一个比基准数小的数,得到其下标
r--;
while (l < r && arr[l] <= base) // 从左往右遍历,直到找到一个比基准数大的数,得到其下标
l++;
if (l < r)
swap(arr, l, r); // 交换两个数在数组中的位置
}
arr[left] = arr[l];
arr[l] = base; // 基准数放置在正确的位置,即最终 l 和 r 相遇的位置
quickSort(arr, left, l - 1); // 继续处理左边的
quickSort(arr, l + 1, right); // 继续处理右边的
}
/**
* 交换数组中的两个元素
* @param arr 数组
* @param i 元素下标
* @param j 另一个元素下标
*/
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
package algorithms;
import java.util.Random;
/**
* 快速排序
* @author Wll
*
*/
public class QuickSortTest {
public static void main(String[] args) {
int[] arr = getRandomArray(130, 10000);
System.out.println("======快速排序======");
System.out.println("原始数组:");
printArray(arr);
long start = System.currentTimeMillis();
quickSort(arr, 0, arr.length - 1);
long end = System.currentTimeMillis();
System.out.println("耗时 " + (end - start)+ " 毫秒");
}
/**
* 进行快速排序,[left, right]为将要进行排序的区间,经排序后只有此区间才是有序的
* @param arr 待排序数组
* @param left 数组起始下标
* @param right 数组结束下标
*/
private static void quickSort(int[] arr, int left, int right) {
if (left >= right) // 如果起始下标 >= 结束下标,直接返回
return;
int l = left;
int r = right;
int base = arr[left]; // 基准数
while (l < r) {
while (l < r && arr[r] >= base) // 从右往左遍历,直到找到一个比基准数小的数,得到其下标
r--;
while (l < r && arr[l] <= base) // 从左往右遍历,直到找到一个比基准数大的数,得到其下标
l++;
if (l < r)
swap(arr, l, r); // 交换两个数在数组中的位置
}
arr[left] = arr[l];
arr[l] = base; // 基准数放置在正确的位置,即最终 l 和 r 相遇的位置
quickSort(arr, left, l - 1); // 继续处理左边的
quickSort(arr, l + 1, right); // 继续处理右边的
}
/**
* 交换数组中的两个元素
* @param arr 数组
* @param i 元素下标
* @param j 另一个元素下标
*/
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
/**
* 获得一个随机整型数组
* @param size 数组大小
* @param limit 数组元素大小的上限
* @return 一个随机数组
*/
private static int[] getRandomArray(int size, int limit) {
int[] arr = new int[size];
Random random = new Random();
for (int i = 0; i < size; i++)
arr[i] = random.nextInt(limit);
return arr;
}
/**
* 打印数组,默认只打印前一百个
* @param arr 待打印数组
*/
private static void printArray(int[] arr) {
int length = arr.length;
int count = 1;
for (int i = 0; i < length; i++) {
System.out.printf("%-10d", arr[i]);
if (count++ % 10 == 0)
System.out.println();
if (count == 101 && count < length) {
System.out.print("......");
break;
}
}
System.out.println("\n");
}
}