快速排序算法

快速排序

在了解快速排序前,我们先通过了解一个问题:如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T6WxhrgA-1686740233028)(E:\zy-o\Vue\image-20221122163432880.png)]

荷兰国旗问题一

我们首先来实现荷兰国旗的问题一:

原数组数据为:3 5 6 7 4 3 5 8

使用图解,我们来解决荷兰国旗问题:如下图所示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YRyfT0BO-1686740233030)(E:\zy-o\Vue\image-20221122165947304.png)]

核心算法:

 // partition 分层
    // arr[L...R]部分进行分层操作,也就是划分操作
    public static void partition(int[] arr,int L,int R){
        // 假定按照整个数据的最后一个数据值以依据来划分,记作p 则 p = arr[R];
        // 定义<=p 区域的边界
        int less = L - 1; //小于区域的边界一开始是整个数据的左侧,因此是L - 1;

        // 开始操作:
//        因此具体实现算法分为了两个过程:
//        1) 如果arr[i] <= 5,我们要将它放到红色区域中,也就是<=5的区域中
//        具体操作为: 使arr[i] 与 红色区域后的下一个元素进行互换,然后红色区域右扩1,i++;
//        2) 如果arr[i] > 5,那么无需移动,i++
        // L所在的左边,就是指针开始位置,也就是上述i的含义
        while (L < R){ //如果L 一直向右移动,直到整个数据的右边界后就表名已经排好序了.
            if (arr[L] <= 5){
                // 交换arr[L] 和 红色区域的下一个元素,然后L++
                // 红色区域右扩
                swap(arr,L++,++less);
//                System.out.println(Arrays.toString(arr));
            }
            if(arr[L] > 5){
                // 原地不动,L++
                L++;
//                System.out.println(Arrays.toString(arr));
            }
        }
        // 至此,partition算法完成
    }

swap算法

// 交换数组中的两个数
public static void swap(int []arr, int i,int j){
    int temp;
    temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

荷兰国旗问题二

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jctOJBAC-1686740233030)(E:\zy-o\Vue\image-20221122172034155.png)]

我们首先来实现荷兰国旗的问题二:

原数组数据为:3 5 6 7 4 3 5 8

使用图解,我们来解决荷兰国旗问题二:如下图所示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a1KKIejN-1686740233030)(E:\zy-o\Vue\image-20221122183518211.png)]

核心算法:

 // partition 分层
    // arr[L...R]部分进行分层操作,也就是划分操作
    public static void partition(int[] arr,int L,int R){
        // 假定按照整个数据的最后一个数据值以依据来划分,记作p 则 p = arr[R];
        // 定义<=p 区域的边界
        int less = L - 1; //小于区域的边界一开始是整个数据的左侧,因此是L - 1;
        int more = R + 1; //大于区域的边界一开始是整个数据的右侧,因此是R + 1。
        int p = arr[R]; //因为在变化过程中,arr[R]是会发生变化的,因此需要先记录下来
        // 开始操作:
//        因此具体实现算法分为了三个过程:
//        1) 如果arr[i] < 5,我们要将它放到红色区域中,也就是<5的区域中
//        具体操作为: 使arr[i] 与 红色区域后的下一个元素进行互换,然后红色区域右扩1,i++;
//        2) 如果arr[i] > 5,我们要将它放到蓝色区域中,也就是>5的区域中
//        具体操作为: 使arr[i] 与 蓝色区域前的前一个元素进行互换,然后蓝色区域左扩1,i不变;
//        因为 互换过来的数据我们不知道其大小,因此i不能变,应该继续判断
//        3)如果arr[i] == 5,无需移动,直接让i++即可
//        实质就是 < 区域推动着==区域,当与>区域撞上时,就表示分配完成了
//        那就是L 与 more撞上的时候,就表示分配完成了
        // L所在的左边,就是指针开始位置,也就是上述i的含义
        while (L < more){ //如果L 一直向右移动,直到整个数据的右边界后就表名已经排好序了.
            if (arr[L] < p){
                // 交换arr[L] 和 红色区域的下一个元素,然后L++
                // 红色区域右扩
                swap(arr,L++,++less);
//                System.out.println(Arrays.toString(arr));
            }else if(arr[L] > p) {
                swap(arr, L, --more);
            }else {
//                如果相等原地不动,指针后移
                L++;
            }
        }
        // 至此,partition算法完成
    }

swap算法:

// 交换数组中的两个数
public static void swap(int []arr, int i,int j){
    int temp;
    temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

完成了上述荷兰问题的解析后,接下来我们就可以引出快速排序了。

快排v1.0

首先先来说一下快排v1.0版本,基于荷兰问题的想法,再加上排序,怎么排序呢?

快排v1.0是基于荷兰国旗问题一得思想来排序的。

具体过程如下:使用图解的形式给出

原数组数据为:3 5 6 7 4 3 5 5

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KnTLWyaf-1686740233031)(E:\zy-o\Vue\image-20221122210710344.png)]

快排v2.0

再来说一下快排v2.0版本,基于荷兰问题的想法,再加上排序,怎么排序呢?

快排v2.0是基于荷兰国旗问题一得思想来排序的。

具体过程如下:使用图解的形式给出

原数组数据为:3 5 6 7 4 3 5 5

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-28TnnbXg-1686740233031)(E:\zy-o\Vue\image-20221122213316682.png)]

快排v3.0

上面讲述了快排v1.0和快排v2.0

但是这两种快速排序的时间复杂度为O(n^2) ,因为这两种快速排序每次都是选取最后一个位置的元素作为基准,来进行partition(分层)

不可避免的会遇上最坏情况的数据。

于是快排v3.0就来改进这种情况,快排v3.0主要解决的是不总是选取最后一个位置的元素的值作为基准。来进行partition

具体操作为: 随机选取数据中的一个元素,使其与最后一个位置的元素进行交换.

然后再以新的最后一个位置的元素为基准,来进行partition

具体图解如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zqN1U7IT-1686740233031)(E:\zy-o\Vue\image-20221122214156503.png)]

核心算法:

public static void quicksort(int[] arr,int L,int R){
    if (arr == null || arr.length < 2) return;
    process(arr,0,arr.length - 1);
}

public static void process(int[] arr,int L,int R){

   if (L < R){
       //随机获取元素
       int p = L + (int)(Math.random() * (R - L + 1));
       // 交换元素
       swap(arr,p,R);
       //进行partition
       int []pos = partition(arr,L,R);
       System.out.println(Arrays.toString(pos));
       // 获取左右边界的位置后,再将其根据左右边界,划分成两个独立的部分,再次进行递归排序
       // 不包括左右边界,因此pos[0] - 1 和 pos[1] + 1 
       process(arr,L,pos[0] - 1);
       process(arr,pos[1] + 1,R);
   }

}

public static int[] partition(int []arr, int L,int R){
    // 记录小于区域左边界
    int less = L - 1;
    // 记录大于区域右边界
    //因为有哨兵占用了位置,我们不对哨兵进行操作,因此右边界为R,若没有哨兵,那么应为R + 1
    int more = R;
    // L 表示指向当前元素的指针
    while (L < more){
        if (arr[L] < arr[R]){
            // 如果元素 < 基准值,那么放在小于区域的下一个元素,小于区域+1,指针后移
            swap(arr,L++,++less);
        }else if(arr[L] > arr[R]){
            // 如果元素 > 基准值,那么放在大于区域的前一个元素,大于区域+1,指针不变
            swap(arr,L,--more);
        }else {
            // 如果两者相等,那么无需移动,指针直接++
            L++;
        }
    }
    //碰撞后,四部分已经形成,那么现在只需要将哨兵位置的元素与>区域的第一个元素进行交换即可
    //大于区域进行右减1,因此是more++,因为是索引
    swap(arr,R,more);
    // 返回数组,只有两个元素,分别标记小于区域的左边界,以及大于区域的右边界
    // 记得返回的less 需要 +1 ,因为less记录的是当前小于区域的左边界位置,而需要返回的是
    // == 区域的左边界和右边界,所以需要less + 1
    return new int[]{less + 1,more};
}

swap算法:

public static void swap(int[] arr,int i,int j){
    int temp;
    temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值