算法——初级排序算法之快速排序

本文详细介绍了快速排序算法,包括其原理、特点(如原地排序和时间复杂度)、代码实现以及针对小数组和性能优化的策略,如使用插入排序和三取样切分。

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

介绍

快速排序可以说是应用最广泛的排序算法了,主要是因为实现简单、适用于各种不同的输入数据且在一般应用中比其他排序算法都要快得多。

快速排序是一种基于分治思想的排序算法。它将一个数组分成两个子数组,将两部分独立地排序。

原理

  • 设置两个变量 low、high,排序开始时:low=0,high=size-1。

  • 整个数组找基准正确位置,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面

特点

优点:原地排序(只需要一个很小的辅助栈),且将长度为N的数组排序所需的时间和NlgN成正比

缺点:非常脆弱,实践中需要非常小心才能避免低劣的性能

代码

package algorithm.sort;

import javax.crypto.interfaces.PBEKey;
import java.util.Arrays;
import java.util.Random;
import java.util.stream.Stream;

/**
 * 描述:快速排序
 * Created by zjw on 2021/7/4 15:38
 */
public class QuickSort {

    public static void main(String[] args) {
        Integer[] arr = Stream.generate(() -> new Random().nextInt(12))
                .limit(10).toArray(Integer[]::new);

        System.out.println("排序前 " + Arrays.toString(arr));
        sort(arr);
        System.out.println("排序后 " + Arrays.toString(arr));
    }

    public static void sort(Comparable[] arr) {
        sort(arr, 0, arr.length - 1);
    }

    /**
     * 该方法使得数组满足下面三个条件
     * - 对于某个j, a[j]已经排定
     * - a[lo]到a[j-1]中的所有元素都不大于a[j]
     * - a[j+1]到a[hi]中的所有元素都不小于a[j]
     *
     * @param a
     * @param lo
     * @param hi
     */
    public static void sort(Comparable[] a, int lo, int hi) {
        if (hi <= lo) return;
        // if (hi <= lo + 10) { insertionSort(a, lo, hi);return; }
        int j = partition(a, lo, hi);// 切分
        sort(a, lo, j - 1); // 排序左边
        sort(a, j + 1, hi); // 排序右边
        // Quick3waySort(a, lo, hi);
    }

    /**
     * 切分
     *
     * @param a
     * @param lo
     * @param hi
     * @return
     */
    private static int partition(Comparable[] a, int lo, int hi) {
        // 将数组切分为a[lo..i-1],a[i],a[i+1..hi]
        int i = lo, j = hi + 1; // 左右指针
        Comparable v = a[lo]; // 切分元素
        while (true) {
            //检查左右是否结束并交换元素
            while (less(a[++i], v)) if (i == hi) break;
            while (less(v, a[--j])) if (j == lo) break;
            if (i >= j) break;
            exchange(a, i, j);
        }
        exchange(a, lo, j);// 将v = a[j]放入正确的位置
        return j;//a[lo..j-1]<=a[j]<=a[j+1..hi]达成
    }

    private static boolean less(Comparable v, Comparable w) {
        return v.compareTo(w) < 0;
    }

    private static void exchange(Comparable[] a, int i, int j) {
        Comparable t = a[i];
        a[i] = a[j];
        a[j] = t;
    }

    /**
     * @param a
     * @param lo
     * @param hi
     */
    public static void insertionSort(Comparable[] a, int lo, int hi) {
        int N = a.length;
        for (int i = 0; i < N; i++) {
            // 将 a[i] 插入到 a[i-1]、a[i-2]、a[i-3]...之中
            for (int j = i; j > 0 && less(a[j], a[j - 1]); j--) {
                exchange(a, j, j - 1);
            }
        }
    }

    /**
     * 三取样切分
     * @param a
     * @param lo
     * @param hi
     */
    public static void Quick3waySort(Comparable[] a, int lo, int hi) {
        if (hi <= lo) return;
        int lt = lo, i = lo + 1, gt = hi;
        Comparable v = a[lo];
        while (i <= gt) {
            int cmp = a[i].compareTo(v);
            if (cmp < 0) exchange(a, lt++, i++);
            else if (cmp > 0) exchange(a, i, gt--);
            else i++;
        }
        // //a[lo..lt-1] < v=a[lt..gt] < a[gt+1..hi]成立
        sort(a, lo, lt - 1);
        sort(a, gt + 1, hi);
    }
}

改进

  • 小数组使用插入排序
    • 对于小数组,快速排序比插入排序慢;
    • 因为递归,快速排序的sort()方法在小数组中也会调用自己;
    • sort()中的语句if(hi <= lo) return;替换成if(hi <= lo + M){Insertion.sort(a, lo, hi);return;}小数组使用插入排序
    • 转换参数M的最佳值是和系统相关的,但是通常5~15之间的任意值在大多数情况下才能令人满意
  • 三取样切分
    • 使用小数组的一部分元素的中位数来切分数组,代价是需要计算中位数

Donate

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值