快速排序法

快速排序法

思路详解

排序目标

 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);
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值