当前位置:首页 > 技术 > 正文内容

随机快速排序的原理与优化实现

访客 技术 2026年7月12日 1

算法核心思想

随机快速排序是一种基于分治策略的高效排序算法,其核心在于每次从待排序区间中随机选取一个基准值(pivot),通过一次划分操作将数组分为三部分:小于基准值、等于基准值、大于基准值。该过程递归执行,直至整个数组有序。

基础实现逻辑

  1. 在区间 [l, r] 内随机选择一个元素作为基准值 x
  2. x 为参照,将数组划分为两部分:所有 ≤ x 的元素置于左侧,所有 > x 的元素置于右侧。
  3. 将基准值 x 移动到左半部分的末尾位置,确保其已处于最终排序位置。
  4. 对左右两个子区间分别递归执行相同操作,直到区间长度 ≤ 1。

代码实现(基础版本)

public static void quickSort(int[] arr, int left, int right) {
    if (left >= right) return;

    // 随机选择基准值
    int pivotIndex = left + (int)(Math.random() * (right - left + 1));
    int pivot = arr[pivotIndex];

    // 划分并返回基准值最终位置
    int partitionIndex = partition(arr, left, right, pivot);

    // 递归处理左右子数组
    quickSort(arr, left, partitionIndex - 1);
    quickSort(arr, partitionIndex + 1, right);
}

private static int partition(int[] arr, int l, int r, int x) {
    int i = l;           // 当前遍历指针
    int boundary = l;    // 小于等于x区域的右边界

    for (; i <= r; i++) {
        if (arr[i] <= x) {
            swap(arr, i, boundary);
            boundary++;
        }
    }

    // 将基准值放入正确位置(位于≤x区域的末尾)
    swap(arr, boundary - 1, l + (int)(Math.random() * (r - l + 1)));

    return boundary - 1;
}

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

优化策略:三路划分(荷兰国旗问题)

当数组中存在大量重复元素时,传统二路划分效率下降。为此引入三路划分:

  • 左区:所有元素 < x
  • 中区:所有元素 == x
  • 右区:所有元素 > x

这样可一次性将多个相等元素归位,减少不必要的递归调用。

三路划分实现

private static int[] threeWayPartition(int[] arr, int l, int r, int x) {
    int lt = l;     // 左边界:小于x的区域右端
    int gt = r;     // 右边界:大于x的区域左端
    int i = l;      // 当前扫描指针

    while (i <= gt) {
        if (arr[i] < x) {
            swap(arr, lt++, i++);
        } else if (arr[i] > x) {
            swap(arr, i, gt--);
        } else {
            i++; // 等于x,无需移动,仅前进
        }
    }

    // 返回中区的左右边界
    return new int[]{lt, gt};
}

// 使用示例
public static void quickSortThreeWay(int[] arr, int l, int r) {
    if (l >= r) return;

    int pivotIndex = l + (int)(Math.random() * (r - l + 1));
    int pivot = arr[pivotIndex];

    int[] bounds = threeWayPartition(arr, l, r, pivot);
    int leftBound = bounds[0];
    int rightBound = bounds[1];

    quickSortThreeWay(arr, l, leftBound - 1);
    quickSortThreeWay(arr, rightBound + 1, r);
}

时间复杂度分析

  • 最坏情况:O(n²),当每次选中的基准值都是最大或最小元素(如已排序数组),但因随机化机制,此情况概率极低。
  • 最好情况:O(n log n),每次基准值都接近中位数,递归树深度为 log n。
  • 平均情况:期望时间复杂度为 O(n log n),得益于随机化策略,使分割趋于平衡。

空间复杂度

  • 递归栈空间:最坏情况下为 O(n),平均情况下为 O(log n)。
  • 无额外辅助数组,原地排序,空间效率高。

通过随机化选择基准和三路划分优化,随机快速排序在实际应用中表现出卓越的性能,尤其适合处理含有重复元素的数据集。

相关文章

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

linux screen 用法详情 (nohup 的替代方案)

一、screen 是什么?能干嘛?screen 是一个终端复用器,可以:在一个 SSH 会话中开多个“虚拟终端”SSH 断线后,程序仍然在后台运行随时重新连接到原来的会话特别适合:nohup 的替代方案跑脚本 / 爬虫 / 训练模型运维、远程开发二、安装 screen# CentOS / Rocky / Almayum install -y screen# Debian / Ubuntuapt i...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。