掘金 后端 ( ) • 2024-04-05 18:30

故事开始了

假设有这样一个场景,有个小学生给了你10000个数字,要你在一天之内把这些数字排好序,然后交给他.身为一个程序猿,你想到了排序算法,进而又想到了快速排序.那么快速排序是怎么实现的呢?

既然10000个数字能排序,那么6个数字也能排序,只要6个数字时,运行结果是准确的,那么这个排序算法就能通用任意个数字的排序.假设我们有这么6个数字:

3 7 2 1 4 6 .首先,快速排序要找个基准点,一般是开头的数字,在我们这个例子中是3,然后想办法把3放到数列的中间位置,使得左边的数字比3小,右边的数字比3大,然后再递归3左右两边的数组,继续使用快速排序,最后就能全局排序.所以要搞清楚,有什么办法可以做到我们要的效果.一个可行的方法是这样的:在数组的头部和尾部各放置一个指针,然后让尾巴和3进行比较,如果大于等于3,那么就往左移动,如果碰到了比3大的数,那么就停下来.另外,如果碰到了左边的指针,也要停下来,这说明我们要的效果已经达到了.说这么多,可能还不够直观,我们用图的方式来模拟这个过程.

起初,整个指针的位置是这样的:

quicksort_1

指针继续向左移动,6比3大,4也比3大,直到遇到了1,比3小,这个时候右边的指针就停下来,并且在移动过程中并未碰到左边的指针.现在指针的位置是这样的:

quicksort_2

同理,左边的指针如果小于等于3,也要向右移动,直到遇到比3大的数,或者遇到了右指针也要停下来.3等于3,向右移动,7大于3,停下来,现在指针的位置是这样的:

quicksort_3.png

此时,我们把两个指针所指的数字交换一下,为什么要交换呢?因为我们要使得左边的数比3小,右边的数比3大,然后就形成了下面这张图:

quicksort_4.png

此时,两个指针还未碰到,但是我们能看到的是:右指针右边的数据全部比3大,左指针左边的数字全部比3小.这是一个很直观的现象.

接着,我们继续移动右指针,这里的7还要和3再比较一次,当然肯定是比3大,所以一定会左移.然后2比3小,右指针又停下来了:

quicksort_5.png

然后左指针用1和3比,肯定要向右移动,然后移动过去时,发现碰到了右指针:

quicksort_6.png

它们撞到一起去了,这个时候我们已经达到了我们的目的,这就是左右指针停下来的一个标志,我们把开头的3和此时这个位置上的2进行一个交换,那么就变成了:

quicksort_7.png

卧槽!我们达到我们的终极目的:使得3左边的数字比3小,右边的数字比3大.

接下来的故事是,保持3这个位置不动,将左边的2,1当成一个数组,右边的7,4,6当成一个数组,然后再重新对每个数组搞两个指针进行移动,切记:撞到了就是成功了.那么我们可以使用递归的方式来做剩余的事情.如果数组只剩下一个元素,那么一定要结束递归,此时的结束条件是i>=j.下面将展示快速排序的代码,也就是模拟我们现在的整个过程.

快速排序代码

   public static void quickSort(int [] nums,int i,int j) {
        if(i>=j){
            return;
        }
        int start = i;
        int end = j;
        int basic = nums[i];
        while(i < j) {
            while(i<j && nums[j] >= basic) {
                j--;
            }
            while(i< j && nums[i] <= basic) {
                i++;
            }
            if(i<j) {
                ArrayUtil.swap(nums, i, j);
            }
        }
        ArrayUtil.swap(nums,i,start);
        quickSort(nums,start,i-1);
        quickSort(nums,i+1,end);
    }

首先代码的开始是判断是不是要结束递归,紧接着,搞出两个指针,开始和开头位置的数字进行比较.代码里面始终要判断i<j,因为撞到了就是成功了,无需再进行下去,另外再没撞到之前,如果双方都停下来一次之后,要交换一下所指的数字,这是为了确保右边的数字比开头的数字大,左边的数字比开头的数字小.

然后,如果i和j相遇,那么一次完整的快速排序就结束了,在遇到的地方和开头的数字进行一次交换,接着再进行左右两个数组的排序,如此递归下去,直到排完.

故事的最后

你轻轻松松的排完了10000个数字,写下代码的过程只用了59秒,接着你拿起了桌上那瓶95年的可乐喝了一口,顺手掏出了手机,开始刷起了你养了多年的B站号,进入了传说中的工作休息区.