快排(拓展:partition、荷兰国旗问题)

6.快速排序(量大时特快,不稳定,对基础类型 随机快排最常用)

快排的几种写法:

(基本快排,随机快排,优化随机快排,双轴快排)

时空分析:

平均时间复杂度:n*logn ;空间复杂度logN (因为要记录断点【放在数组的位置】,用数组记录相等的范围)

如果不用随机快排,最坏时间复杂度为N*2,随机快排最坏为nlogN

简单快排:

选第一个数字作为基准,分两个区

核心代码:

public static void qucikSort1(int[] A, int L, int R){
    if(L < R){
        int pivot = A[L];//最左边的元素作为中轴元素
        int i = L;//初始化时小于等于pivot的部分,元素个数为0
        int j = L+1; //大于pivot的部分,元素个数也为0
        while(j <= R){
            if(A[j] <= pivot){
                swap(A, ++i, j++);
            }else{
                j++;//大于pivot的元素增加一个
            }
        }
        //A[i]及A[i]以前的都小于等于pivot
        //循环结束后A[i+1]及它以后的都大于pivot
        //所以交换A[L]和A[i],这样我们就将中轴元素放到了适当的位置
        swap(A, L, i);
        qucikSort1(A, L, i-1);
        qucikSort1(A, i+1, R);
    }
}

优化随机快排:

1.范围内随机选一个数字做基准,小于它的放左边,大于它的放右边,等于它的放小于区右边;

2.利用小于区推动着等于区向大于区靠拢;同时大于区也会扩大.

2.当等于区与大于区相遇时,遍历完整个数组,将原数组分成了三个区域:小于区,等于区,大于区

3.对小于区和大于区再使用快排

其中partition的过程很重要,将数组分为三个区域.对应荷兰国旗问题.

public  static void qucikSort(int [] a){
    qucikSort(a,0,a.length-1);
}
public  static void qucikSort(int [] a,int left,int right){
    if(left<right){
        swap(a, left + (int) (Math.random() * (right - left)), right);
        int[] p = partition(a, left, right);
        qucikSort(a, left, p[0] - 1);
        qucikSort(a, p[1] + 1, right);
    }
}
public  static int[] partition(int [] a,int l,int r){
    int less=l-1;//为l-1,因为小于区初始需要为0个
    int more=r;//为r,因为最后要换
    while(l<more){
        if(a[l]<a[r]){
            swap(a,l++,++less);//交换小于基准数时,小于区,等于区指针同时后移
        }else if(a[l]>a[r]){
            swap(a,l,--more);//交换大于基准数时,大于区扩大,指针左移
        }else {
            l++;//相等时,等于区指针后移
        }
    }
    swap(a,more,r);//最后将基准数与等于区最后一个元素替换
    return  new int[]{less+1,more};//返回等于区的起始坐标和结束坐标
}
static  void  swap(int[] a,int i,int j){//快排的交换不能用位运算,因为小于区扩大时,可能涉及到自己与自己交换,位运算会将数交换成0
    int temp=a[i];
    a[i]=a[j];
    a[j]=temp;
}

快排的拓展(partition)

问题一:(分两个区)

给定一个数组arr,和一个数num,请把小于等于num的数放在数组的左边,大于num的数放在数组的右边。
要求额外空间复杂度O(1),时间复杂度O(N)

public static void partition(int[] a,int num) {
    int R = a.length - 1;
    int less = 0;
    int more =  1;
    while (more <= R) {
        if (a[more] <= num) {
            swap(a, ++less, more++);
        } else {
            more++;
        }
    }
}
static void swap(int[] a, int i, int j) {
    int tmp = a[i];
    a[i] = a[j];
    a[j] = tmp;
}

问题二(荷兰国旗问题):(分三个区)

给定一个数组arr,和一个数num,请把小于num的数放在数组的左边,等于num的数放在数组的中间,大于num的数在数组的右边。
要求额外空间复杂度O(1),时间复杂度O(N)

public static void partition(int[] a,int num) {
    int more = a.length;
    int less = -1;
    int mid = 0;
    while (mid < more) {
        if (a[mid] < num) {
            swap(a, ++less, mid++);
        } else if(a[mid]>num){
            swap(a,--more,mid);
        }else{
            mid++;
        }
    }
}

static void swap(int[] a, int i, int j) {
    int tmp = a[i];
    a[i] = a[j];
    a[j] = tmp;
}
全部评论

相关推荐

牛马人的牛马人生:一开始看成了网吧
点赞 评论 收藏
分享
01-09 11:18
门头沟学院 Java
作者先叠个甲:本人双非本,秋招拿到了多个大厂offer,这个过程也不容易,但是在看到很多秋招胜利之后说自己一路有多艰辛的文章,总感觉有一点不对劲,想了很久打算写一篇文章分析一下,本文仅代表作者观点,不认同的可以在评论区大家一起理性讨论。&nbsp;秋招已经结束,各类社交平台出现一大批“大厂上岸”胜利结算。文章的叙事逻辑高度相同,开篇就渲染焦虑和困惑,学习时的挑灯夜读、投递时的屡屡碰壁、面试时的如履薄冰,将过往经历包装成一步艰辛的“奋斗史”,然后最终以大厂offer的胜利结尾,字里行间全是苦尽甘来的优越感。但是在我看来,这类文章的本质是结果导向的、带有浮夸的叙事,因为其内核不是分享经验,而是借“苦难”之名...
创作小队长:你的批判视角非常犀利,尤其“结果决定叙事权”的洞察非常精准,哈哈想邀请你来成为我们的创作者🫰 但我想补充一个视角:许多分享者的初衷并非炫耀结果或者苦难,我更愿意相信他们在这个过程中付出了很多,在这场战役结束后,他们迫不及待地想被看到,记录和分享都是给自己的一个交代,而非真的教会别人什么,他们的初衷未必是想制造焦虑。求职市场的残酷、经济环境的下行、世俗价值观才是这种叙事流行的土壤,作为一个普通人无法抵抗洪流。 感谢你发起这场讨论。理想的社区,既需要这样锐利的批判来保持清醒,你的洞察非常犀利,也许会启发一些人,能逐渐改变这种叙事~
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务