一篇搞懂快速排序

原理-总览

1、从待排序区间选择一个数,作为基准值privot;

2、Partition:遍历整个待排序区间,将比基准值小的(可以包含相等的)放到基准值的左边,将比基准值大的(可以包含相等的)放到基准值的右边

3、采用分治思想,对左右两个校区间按照同样的方式处理,直到小区间的长度==1,代表已经有序,或者小区间的长度==0,代表没有数据。

---------------------------------------------------------------------------------------------------------------------------------搞明白原理之后,咱们就可以逐步解决,基准值我们最先能想到的一般都是首元素或者尾元素

 

 

 

 注意:如果我们每次唯一的选择首元素或者尾元素,但是如果这个数组是一个近乎有序的数组,快排的时间复杂度其实无异于O(n^2),随意我们通过随机选数来解决这一问题。

代码:

/**
     * 在arr[l..r]上进行快速排序
     * @param arr
     * @param l
     * @param r
     */
    private static void quickSortInternal(int[] arr, int l, int r) {
     
        // 先获取分区点
        // 所谓的分区点就是经过分区函数后,某个元素落在了最终的位置
        // 分区点左侧全都是小于该元素的区间,分区点右侧全都是 >= 该元素的区间
        int p = partition(arr,l,r);
        // 重复在左区间和右区间上重复上述流程
        quickSortInternal(arr,l,p - 1);
        quickSortInternal(arr,p + 1,r);
    }

    /**
     * 在arr[l..r]上的分区函数,返回分区点的索引
     * @param arr
     * @param l
     * @param r
     * @return
     */
    private static int partition(int[] arr, int l, int r) {
        // 随机在当前数组中选一个数
        int randomIndex = random.nextInt(l,r);
        swap(arr,l,randomIndex);
        int v = arr[l];
        // arr[l + 1..j] < v
        // arr[j + 1..i) >= v
        // i表示当前正在扫描的元素
        int j = l;
        for (int i = l + 1; i <= r; i++) {
            if (arr[i] < v) {
                swap(arr,j + 1,i);
                j ++;
            }
        }
        // 将基准值和最后一个 < v的元素交换,基准值就落在了最终位置
        swap(arr,l,j);
        return j;
    }
private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

还有一种情况:如果所有元素都是相同的呢?你随机选择的话时间复杂度也变成了O(n^2),成了一个极度不平衡的树,只能通过遍历解决。

既然问题在于重复元素过多,那我们就在多设置一个区间存储相等的元素。

 

 

代码:

  private static void quickSortInternal2(int[] arr, int l, int r) {
        int p = partition2(arr,l,r);
        quickSortInternal2(arr,l,p - 1);
        quickSortInternal2(arr,p + 1,r);
    }

    /**
     * 二路快排的分区
     * 在arr[l..r]上进行分区处理
     * @param arr
     * @param l
     * @param r
     * @return
     */
    private static int partition2(int[] arr, int l, int r) {
        int randomIndex = random.nextInt(l,r);
        swap(arr,l,randomIndex);
        int v = arr[l];
        // arr[l + 1..i) <= v
        // [l + 1..l + 1) = 0
        int i = l + 1;
        // arr(j..r] >= v
        // (r...r] = 0
        int j = r;
        while (true) {
            // i从前向后扫描,碰到第一个 >= v的元素停止
            while (i <= j && arr[i] < v) {
                i ++;
            }
            // j从后向前扫描,碰到第一个 <= v的元素停止
            while (i <= j && arr[j] > v) {
                j --;
            }
            if (i >= j) {
                break;
            }
            swap(arr,i,j);
            i ++;
            j --;
        }
        // j落在最后一个 <= v的元素身上
        swap(arr,l,j);
        return j;
    }
private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

 最后给介绍一下三路快排

 

 

代码: 

private static void quickSortInternal3(int[] arr, int l, int r) {
     
        int randomIndex = random.nextInt(l,r);
        swap(arr,l,randomIndex);
        int v = arr[l];
        // 这些变量的取值,一定是满足区间的定义,最开始的时候,所有区间都是空
        // arr[l + 1..lt] < v
        // lt是指向最后一个<v的元素
        int lt = l;
        // arr[lt + 1..i) == v
        // i - 1是最后一个 = v的元素
        int i = lt + 1;
        // arr[gt..r] > v
        // gt是第一个 > v的元素
        int gt = r + 1;
        // i从前向后扫描和gt重合时,所有元素就处理完毕
        while (i < gt) {
            if (arr[i] < v) {
                // arr[l + 1..lt] < v
                // arr[lt + 1..i) == v
                swap(arr,i,lt + 1);
                i ++;
                lt ++;
            }else if (arr[i] > v) {
                // 交换到gt - 1
                swap(arr,i,gt - 1);
                gt --;
                // 此处i不++,交换来的gt - 1还没有处理
            }else {
                // 此时arr[i] = v
                i ++;
            }
        }
        // lt落在最后一个 < v的索引处
        swap(arr,l,lt);
        // arr[l..lt - 1] < v
        quickSortInternal3(arr,l,lt - 1);
        // arr[gt..r] > v
        quickSortInternal3(arr,gt,r);
    }
  private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值