快速排序,单路,双路

本文详细介绍了快速排序的基本思想、时间空间复杂度和稳定性,并通过图解展示了单路和双路快速排序的过程。单路快速排序通过一趟排序将数组分成两部分,而双路快速排序在分区时从两端同时进行。文章还提供了优化方案和代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

快速排序

       基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分小,比另一部分的关键字大,则可分别对这两部分记录继续进行排序,以达整个序列有序。

        1.时间复杂度:O(nlogn)。

        2.空间复杂度:O(1)。

3.稳定性:不稳定。

图解:

注解:给一组数字,选取最左边数字作为主元,也就是将要放在中间的值,将这组数字分为左边都比主元小,右边都比主元大,如下图:

注解:此时分为左边比主元小,右边比主元大的情况,主元4的位置就确定在这儿了,下来将主元左边部分及右边部分继续按照刚才的办法去排序,即选取2为当前主元,对2,1,3这组数字进行排序,右边选取7作为当前主元,对7,6,8,5这组数字进行排序,划分好之后,最左右再进行同理划分,结果如下:

 

 此时的结果已经是一组有序的数字。

单路快速排序

目标:arr [ l...p - 1] < arr [ p ] < [ p + 1... r ]

//:p是主元的下标,l是指当前数组最左边元素下标,r是指当前数组最右边元素下标。

操作:arr [ l + 1... j ] < v < arr [ j + 1 ... i ]

//:让左边元素都比主元小,右边元素都比主元大。

图解:

如图所示一组数字,确定最左边,也就是L对应的元素为主元,R指向最右边元素,j从L开始,i从j+1开始,要满足,从L+1到j都是小于V的,从j+1到i的值都是大于v的。

我们先开始判断i的对应的值,它比v小,那么就让i和j+1进行交换,此时,i和j+1对应的是同一个元素,所以不动,然后进行++操作,即i++,向后移,j++,说白了j就是主元左边第一个小于主元的元素,i就是最后一个大于主元的元素,如下图:

 

此时i对应的元素大于主元,直接把i向后移,然后7也大于主元,再次向后移,此时i移到了2所在的位置,对应的值小于主元,我们让i对应的值和j+1进行交换,交换之后j++,结果如下:

 

接下来再次进行相同操作,将i向后移,找到一个小于住院的元素,移到3对应的位置,将i对应值与j+1进行交换, 然后i++,j++,结果如下:

此时满足,从L+1到j都是比主元小的,从j+1都是比主元大的,最后将v与j进行交换。把4确定到中间,分成了左右两个部分,然后对两边再进行同理的排序,直到有序即可。下面我们进行代码操作一下:

package p4;

import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Random;

public class QuickSort {
    //单路快速排序
    public static void main(String[] args) {
        //随机生成数组
        int[] arr = new int[20];
        Random random = new Random();
        for(int i = 0; i < arr.length; i++){
            arr[i] = random.nextInt(50 );
        }
        //中间用递归
        quickSort(arr,0,arr.length - 1);


        //打印看是否排好
        System.out.println(Arrays.toString(arr));
    }

    private static void quickSort(int[] arr, int l, int r) {
        //考虑是否只有一个元素,要是只有一个元素就不用往下继续了
        if (l >= r){
            return;
        }
        //分解,将原来的分为左右两部分,然后再将分好的左右两部分继续分。
        int p = partition(arr,l,r);
        quickSort(arr,l,p - 1);
        quickSort(arr,p + 1,r);
    }

    private static int partition(int[] arr, int l, int r) {
        int v = arr[l];
        int j = l;
        //i是主动的,j是被动的
        for (int i = l + 1; i <= r; i++){
            //判断i是否小于v,v就是主元对应的值,若小于,则让i和j+1的值进行交换
            if (arr[i] < v){
                swap(arr,j + 1,i);
                j++;
            }
        }
        swap(arr,l,j);
        return j;
    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[i];
        arr[j] = temp;
    }
}

 结果如图所示,由于产生的结果是随机的,所以我们在这里随便产生一个结果如下:

优化:

1.判断数组长度

private static void quickSort(int[] arr, int l, int r) {
        //考虑是否只有一个元素,要是只有一个元素就不用往下继续了
//        if (l >= r){
//            return;
//        }
        if (r - 1 <= 10){
            ShellSort.shellSort(arr,l,r);
            return;
        }

2.在partition处优化:

先让l值和后面的一个角标交换,叫叫换的当做目标值,然后进行交换。

 private static int partition(int[] arr, int l, int r) {
        //优化
        swap(arr,l, (int) (Math.random() * (r - 1 + 1) + 1));
        int v = arr[l];
        int j = l;
        //i是主动的,j是被动的
        for (int i = l + 1; i <= r; i++){
            //判断i是否小于v,v就是主元对应的值,若小于,则让i和j+1的值进行交换
            if (arr[i] < v){
                swap(arr,j + 1,i);
                j++;
            }
        }
        swap(arr,l,j);
        return j;
    }

到此我们的单路快速排序就完啦!

双路快速排序

目标:arr[l ... p - 1] <= arr[p] =< arr[p + 1... r]

操作:arr[l + 1 ... i] <= v <= arr[j ... r]

注解:i从前往后走,j从后往前走 ,在极端情况下,i可能会走到r。先判断i是否走到r以及i对应的元素值是否小于主元元素的值,都不满足则i++,即i向右移,直到找到一个不小于主元的元素,再看j,看j是否移动到l+1的位置以及判断j对应位置的值是否大于v,满足交换,不满足就j--,直到找到一个不大于主元的元素,即j向左移,此时判断i和j是否碰头,没碰头,则把i和j的值进行交换,如图所示:

 此时交换了3和4,接下来i继续向后移,找到一个不小于主元的元素停,再移动j,找到一个不大于主元的元素停,如图所示:

 接下来,i++,j--,让v对应的值和j对应的值交换,此时j对应的值就是确定好的中间值,此时这组数也被分为两部分,然后再对两部分的数进行排序,同理,直到有序即可。

代码实现部分:

package p4;

import java.util.Arrays;
import java.util.Random;

public class QuickSort02 {
    public static void main(String[] args) {
        //随机生成数组
        int[] arr = new int[20];
        Random random = new Random();
        for(int i = 0; i < arr.length; i++){
            arr[i] = random.nextInt(50 );
        }
        //中间用递归
        quickSort(arr,0,arr.length - 1);


        //打印看是否排好
        System.out.println(Arrays.toString(arr));
    }

    private static void quickSort(int[] arr, int l, int r) {
        if (l >= r){
            return;
    }
        int p = partition(arr,l,r);
        quickSort(arr,l,p - 1);
        quickSort(arr,p + 1,r);
}

    private static int partition(int[] arr, int l, int r) {
        swap(arr,l, (int) (Math.random() * (r - 1 + 1) + 1));
        int v = arr[l];
        int i = l + 1;
        int j = r;
        while(true){
            while(i <= r && arr[i] < v){
                i++;
            }
            while (j >= l + 1 && arr[j] > v){
                j--;
            }
            if (i > j){
                break;
            }
            swap(arr,i,j);
            i++;
            j--;
        }
        return j;
    }
    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[i];
        arr[j] = temp;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值