快速排序之单边和双边循环

文章介绍了快速排序算法的基本原理,包括选择基准点并进行分区,以及两种不同的实现方式:Lomuto分区和Hoare分区。Lomuto方案从右向左寻找小于基准点的元素,而Hoare方案则同时从左右两侧进行查找并交换。此外,文章还提到了快速排序的时间复杂度和稳定性。
摘要由CSDN通过智能技术生成

一、什么是快速排序?

  1. 每一轮排序选择一个基准点(pivot)进行分区

  1. 让小于基准点的元素进入一个分区,大于基准点的元素进入另一个分区

  1. 当分区完成时,基准点元素的位置就是其最终位置

  1. 在子分区内重复以上过程,直至子分区的元素个数小于等于1,这体现的是分而治之的思想(divide-and-conquer)

二、实现方式:单边循环快排(lomuto洛穆托分区方案)

  1. 选择最右元素作为基准点元素

  1. j指针负责找到比基准点小的元素,一旦找到则与i进行交换

  1. i指针维护小于基准点元素的边界,也是每次交换的目标索引

  1. 最后基准点与i交换,i即为分区位置

代码如下:

import java.util.Arrays;

public class QuickSort1 {
    public static void main(String[] args) {
        int[] a ={5,3,7,2,9,8,1,4};
        quick(a,0,a.length-1);

    }

    //递归实现快排
    public static void quick(int[] a,int low,int high){
        if(low>=high){
            return;
        }
        int p = partition(a, low, high);//p 索引值
        quick(a,low,p-1);
        quick(a,p+1,high);
    }

    //分区方法
    public static int partition(int[] a, int low, int high) {
        // 返回值元素代表了基准点元素所在的正确索引,用它确定下一轮分区的边界
        int pv = a[high];
        int i = low;
        for (int j = low; j < high; j++) {
            if (a[j] < pv) {//如果找到比基准点元素小的元素
                swap(a, i, j);
                i++;
            }
        }
        swap(a, high, i);
        System.out.println(Arrays.toString(a));
        return i;
    }

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

结果及过程:

小优化点:分区方法内,如果找到比基准点元素小的元素,如果i不等于j时再交换,最后交换基准点时,如果i不等于最后一个元素位置时再交换

public static int partition(int[] a, int low, int high) {
        // 返回值元素代表了基准点元素所在的正确索引,用它确定下一轮分区的边界
        int pv = a[high];
        int i = low;
        for (int j = low; j < high; j++) {
            if (a[j] < pv) {//如果找到比基准点元素小的元素
                if(i!=j) {
                    swap(a, i, j);
                }
                i++;
            }
        }
        if(i!=high){
            swap(a, high, i);
        }
        System.out.println(Arrays.toString(a));
        return i;
    }

三、实现方式:双边循环快排(并不完全等价于hoare霍尔分区方案)

  1. 选择最左元素作为基准点元素

  1. j指针负责从右向左找比基准点小的元素,i指针负责从左向右找比基准点大的元素,一旦找到二者交换,直至i,j相交

  1. 最后基准点与i(此时i与j相等)交换,i即为分区位置

代码如下:

import java.util.Arrays;

public class QuickSort2 {
    public static void main(String[] args) {
        int[] a = {5, 3, 7, 2, 9, 8, 1, 4};
        quick(a, 0, a.length - 1);

    }

    //递归实现快排
    public static void quick(int[] a, int low, int high) {
        if(low>=high){
            return;
        }
        int p = partition(a, low, high);//p 索引值
        quick(a,low,p-1);
        quick(a,p+1,high);
    }

    //分区方法
    public static int partition(int[] a, int low, int high) {
        int pv = a[low];
        int i = low;
        int j = high;
        while (i < j) {
            //顺序:要先从右往左找小的,再从左往右找大的
            while (j > i && a[j] > pv) {
                j--;
            }
            while ((i < j && a[i] <= pv)) {
                i++;
            }
            swap(a,i,j);
        }
        swap(a,low,j);
        System.out.println(Arrays.toString(a));
        return j;
    }

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

结果如图:

四、快速排序-特点

  1. 平均时间复杂度是O(nlog2n),最坏时间复杂度O(n2)

  1. 数据量较大时,优势非常明显

  1. 属于不稳定排序,遇到相同值的元素可能交换其位置

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值