快速排序法
思路详解
排序目标
int [] arr={7,21,5,9,4,8,2,12}
排序演练
第一步 在数组中随便找个基准数做为参考,比如当前基准 数是第一个数7 ,先从右往左找第一个小于7的数,再从 左往右找到第一个大于7的数,交换它们的位置。这里可 以用两个变量i和j,分别指向序列左边和右边。我们 为这两个变量起个好听的名字“哨兵i”和“哨兵j”。刚开始 的时候让哨兵i指向序列的左边(即i=0),指向数字 7。让哨兵j指向序列的右边(即j=7),指向数字12。
第二步
首先哨兵j开始出动。因为此处设置的基准数是左边的 数,所以需要让哨兵j先出动,这一点非常重要(请自己 想一想为什么)。哨兵j一步一步地向左挪动(即j–-), 直到找到一个小于7的数停下来。接下来哨兵i再一步一 步向右挪动(即i++),直到找到一个数大于7的数停下 来。后哨兵j停在了数字2面前,哨兵i停在了数字21面 前。
现在交换哨兵i和哨兵j所指向的元素的值。交换之后的序 列如下:
7 2 5 9 4 8 21 12
第三步 到此,第一次交换结束。接下来开始哨兵j继续向左挪动 (再友情提醒,每次必须是哨兵j先出发)。他发现了4 (比基准数7要小,满足要求)之后停了下来。哨兵i也 继续向右挪动的,他发现了9(比基准数7要大,满足要 求)之后停了下来。此时再次进行交换:
交换后的结果如下:
7 2 5 4 9 8 21 12
第四步 第二次交换结束,“探测”继续。哨兵j继续向左挪动, 发现碰到哨兵i了,不能再走了,我们将基准数7和4交换 位置,如下:
到此第一轮“探测”真正结束。此时以基准数7为分界点, 7左边的数都小于等于7,7右边的数都大于等于7。回顾 一下刚才的过程,其实哨兵j的使命就是要找小于基准数 的数,而哨兵i的使命就是要找大于基准数的数,直到i和 j碰头为止。 7 2 5 4 9 8 21 12 现在基准数7已经归位,它正好处在序列的第3位。此 时我们已经将原来的序列,以7为分界点拆分成了两个序 列,左边的序列是“4 2 5”,右边的序列是“9 8 21 12”。接下来还需要分别处理这两个序列。因为7左边和 右边的序列目前都还是很混乱的。不过不要紧,我们已 经掌握了方法,接下来只要模拟刚才的方法分别处理7左 边和右边的序列即可。现在先来处理7左边的序列现吧。 左边的序列是“4 2 5 ”。请将这个序列以4为基准数进行 调整,使得4左边的数都小于等于4,4右边的数都大于 等于4。好了开始行动。
哨兵j走到2发现比4小,且碰到哨兵i了, 直接交换位 置:
好了,现在4已经到位,且4左边只有一个数 ,右边也只 有一个数,至此排序结束。 注:假如4排序完后,左边超过1个数,继续执行上面 的步骤,直到排序完成。同理,右边也是如此。 左边排序完成后的结果是:
2 4 5
顺序已经正确,然后按照同样的方法,排序7右边的数, 直到排序完成,后的结果如下:
2 4 5 7 8 9 12 21
快速排序之所比较快,因为相比冒泡排序,每次交换是 跳跃式的。每次排序的时候设置一个基准点,将小于等 于基准点的数全部放到基准点的左边,将大于等于基准 点的数全部放到基准点的右边。这样在每次交换的时候 就不会像冒泡排序一样每次只能在相邻的数之间进行交 换,交换的距离就大的多了。因此总的比较和交换次数 就少了,速度自然就提高了。当然在坏的情况下,仍 可能是相邻的两个数进行了交换。因此快速排序的差 2 4 5 2 4 5 7 8 9 12 21 时间复杂度和冒泡排序是一样的。其实快速排序是基于 一种叫做“二分”的思想.
算法代码实现
public class QuickSort {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] arr = { 7, 21, 5, 9, 4, 8, 2, 12 };
quickSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
/**
* 快速排序
*
* @param tmp 要排序的数组
* @param i 哨兵i
* @param j 哨兵j
*/
static void quickSort(int[] tmp, int i, int j) {
// 每一趟 要求哨兵i小于哨兵j 一旦两个碰头,这一轮结束 停止
//推出递归的条件
if(i>=j)
return;
// 定义基数
int base = tmp[i], start = i, end = j;
while (i < j) {
// 右边的哨兵从右向左走,找到第一个比基数小的数
while (i < j && tmp[j] > base) {
j--;
}
// 左边的哨兵从左向右走,找到第一个比基数大的数
while (i < j && tmp[i] <= base) {
i++;
}
// 交换位置
if (i < j) {
int t = tmp[i];
tmp[i] = tmp[j];
tmp[j] = t;
}
}
// 循环完说明两个哨兵碰头了 交换基数与哨兵i的值
int t = tmp[i];
tmp[i] = base;
tmp[start] = t;
// 位置i坐便的都是小于基数的 递归执行快速排序
quickSort(tmp, 0, i - 1);
// 位置i右边的都是小于基数的 递归执行快速排序
quickSort(tmp, i + 1, end);
}