快速排序算法改进:随机快排-荷兰国旗划分详解

news2025/6/10 17:08:16

随机快速排序-荷兰国旗划分算法详解

    • 一、基础知识回顾
      • 1.1 快速排序简介
      • 1.2 荷兰国旗问题
    • 二、随机快排 - 荷兰国旗划分原理
      • 2.1 随机化枢轴选择
      • 2.2 荷兰国旗划分过程
      • 2.3 结合随机快排与荷兰国旗划分
    • 三、代码实现
      • 3.1 Python实现
      • 3.2 Java实现
      • 3.3 C++实现
    • 四、性能分析
      • 4.1 时间复杂度
      • 4.2 空间复杂度
    • 五、实际应用场景
      • 5.1 数据排序与预处理
      • 5.2 并行计算与分布式系统

快速排序是经典的平均时间复杂度 O ( n ) O(n) O(n)的排序算法,而随机快排结合荷兰国旗划分(Dutch National Flag Partitioning)的优化策略,进一步提升了性能和稳定性。本文我将深入剖析随机快排 - 荷兰国旗划分的原理、实现细节、性能分析以及实际应用场景,带你全面掌握这一强大的排序技术。

一、基础知识回顾

1.1 快速排序简介

快速排序是一种基于分治思想的排序算法。基本思路是:从数组中选择一个元素作为枢轴(pivot),将数组分为两部分,使得左边部分的元素都小于等于枢轴,右边部分的元素都大于枢轴,然后递归地对左右两部分进行排序,最终实现整个数组的有序排列。快速排序在平均情况下的时间复杂度为 O ( n l o g n ) O(n log n) O(nlogn),但在最坏情况下(如数组已经有序且每次选择第一个或最后一个元素作为枢轴),时间复杂度会退化为 O ( n 2 ) O(n^2) O(n2)

1.2 荷兰国旗问题

荷兰国旗由红、白、蓝三种颜色的条纹组成,荷兰国旗问题是指:给定一个由红色、白色和蓝色三种颜色的小球组成的数组,要求将数组中的元素按照红、白、蓝的顺序排列,且尽可能地提高排序效率。在算法实现中,用0表示红色,1表示白色,2表示蓝色。荷兰国旗划分算法通过一次遍历,将数组划分为小于枢轴、等于枢轴和大于枢轴的三个区域,这种划分方式为随机快排的优化提供了思路。

二、随机快排 - 荷兰国旗划分原理

2.1 随机化枢轴选择

为了避免快速排序在最坏情况下时间复杂度退化,随机快排采用随机选择枢轴的策略,在每次进行划分操作前,从待排序数组中随机选择一个元素作为枢轴。这样做使得无论输入数组的初始顺序如何,都能在大概率下保证划分的均衡性,算法的平均时间复杂度从而稳定在 O ( n l o g n ) O(n log n) O(nlogn)

2.2 荷兰国旗划分过程

荷兰国旗划分的核心在于将数组划分为三个区间:

  1. 小于枢轴区间:位于数组的左侧,包含所有小于枢轴的元素。
  2. 等于枢轴区间:位于小于枢轴区间和大于枢轴区间之间,包含所有等于枢轴的元素。
  3. 大于枢轴区间:位于数组的右侧,包含所有大于枢轴的元素。

在划分过程中,使用三个指针:

  • left指针:指向小于枢轴区间的右边界,初始位置为数组起始位置 - 1。
  • current指针:用于遍历数组,从数组起始位置开始。
  • right指针:指向大于枢轴区间的左边界,初始位置为数组末尾位置。

划分过程如下:

  1. current 指针小于等于 right 指针时,进行循环:
    • arr[current] 小于枢轴,left 指针右移一位,交换 arr[left]arr[current],然后 current 指针右移一位。
    • arr[current] 等于枢轴,current 指针直接右移一位。
    • arr[current] 大于枢轴,交换 arr[current]arr[right]right 指针左移一位。
  2. 循环结束后,数组被成功划分为三个区间,小于枢轴的元素在左侧,等于枢轴的元素在中间,大于枢轴的元素在右侧。
    荷兰国旗-随机快排

2.3 结合随机快排与荷兰国旗划分

在随机快排中引入荷兰国旗划分后,排序过程如下:

  1. 随机选择一个枢轴元素。
  2. 使用荷兰国旗划分算法将数组划分为三个区间:小于枢轴区间、等于枢轴区间和大于枢轴区间。
  3. 递归地对小于枢轴区间和大于枢轴区间进行随机快排。
  4. 由于等于枢轴区间的元素已经有序,无需再次排序,最终得到整个有序数组。

通过这种方式,随机快排 - 荷兰国旗划分不仅避免了传统快排的最坏情况,还能在一次划分中处理所有等于枢轴的元素,减少了递归调用的次数,进一步提高了排序效率。

三、代码实现

3.1 Python实现

import random


def dutch_national_flag_partition(arr, pivot_index):
    pivot = arr[pivot_index]
    left, current, right = 0, 0, len(arr) - 1
    while current <= right:
        if arr[current] < pivot:
            arr[left], arr[current] = arr[current], arr[left]
            left += 1
            current += 1
        elif arr[current] == pivot:
            current += 1
        else:
            arr[current], arr[right] = arr[right], arr[current]
            right -= 1
    return left, right


def randomized_quicksort(arr):
    if len(arr) <= 1:
        return arr
    pivot_index = random.randint(0, len(arr) - 1)
    left, right = dutch_national_flag_partition(arr, pivot_index)
    left_part = randomized_quicksort(arr[:left])
    right_part = randomized_quicksort(arr[right + 1:])
    equal_part = arr[left:right + 1]
    return left_part + equal_part + right_part


# 测试
arr = [2, 0, 2, 1, 1, 0]
print(randomized_quicksort(arr))

3.2 Java实现

import java.util.Arrays;
import java.util.Random;

public class RandomizedQuicksort {
    public static int[] dutchNationalFlagPartition(int[] arr, int pivotIndex) {
        int pivot = arr[pivotIndex];
        int left = 0, current = 0, right = arr.length - 1;
        while (current <= right) {
            if (arr[current] < pivot) {
                swap(arr, left, current);
                left++;
                current++;
            } else if (arr[current] == pivot) {
                current++;
            } else {
                swap(arr, current, right);
                right--;
            }
        }
        return new int[]{left, right};
    }

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

    public static int[] randomizedQuicksort(int[] arr) {
        if (arr.length <= 1) {
            return arr;
        }
        Random random = new Random();
        int pivotIndex = random.nextInt(arr.length);
        int[] partitionIndices = dutchNationalFlagPartition(arr, pivotIndex);
        int left = partitionIndices[0];
        int right = partitionIndices[1];
        int[] leftPart = randomizedQuicksort(Arrays.copyOfRange(arr, 0, left));
        int[] rightPart = randomizedQuicksort(Arrays.copyOfRange(arr, right + 1, arr.length));
        int[] equalPart = Arrays.copyOfRange(arr, left, right + 1);
        int[] result = new int[leftPart.length + equalPart.length + rightPart.length];
        System.arraycopy(leftPart, 0, result, 0, leftPart.length);
        System.arraycopy(equalPart, 0, result, leftPart.length, equalPart.length);
        System.arraycopy(rightPart, 0, result, leftPart.length + equalPart.length, rightPart.length);
        return result;
    }

    public static void main(String[] args) {
        int[] arr = {2, 0, 2, 1, 1, 0};
        System.out.println(Arrays.toString(randomizedQuicksort(arr)));
    }
}

3.3 C++实现

#include <iostream>
#include <vector>
#include <ctime>
#include <cstdlib>
using namespace std;

pair<int, int> dutch_national_flag_partition(vector<int>& arr, int pivot_index) {
    int pivot = arr[pivot_index];
    int left = 0, current = 0, right = arr.size() - 1;
    while (current <= right) {
        if (arr[current] < pivot) {
            swap(arr[left], arr[current]);
            left++;
            current++;
        }
        else if (arr[current] == pivot) {
            current++;
        }
        else {
            swap(arr[current], arr[right]);
            right--;
        }
    }
    return make_pair(left, right);
}

vector<int> randomized_quicksort(vector<int> arr) {
    if (arr.size() <= 1) {
        return arr;
    }
    srand(time(nullptr));
    int pivot_index = rand() % arr.size();
    pair<int, int> partition_indices = dutch_national_flag_partition(arr, pivot_index);
    int left = partition_indices.first;
    int right = partition_indices.second;
    vector<int> left_part = randomized_quicksort(vector<int>(arr.begin(), arr.begin() + left));
    vector<int> right_part = randomized_quicksort(vector<int>(arr.begin() + right + 1, arr.end()));
    vector<int> equal_part(arr.begin() + left, arr.begin() + right + 1);
    vector<int> result;
    result.reserve(left_part.size() + equal_part.size() + right_part.size());
    result.insert(result.end(), left_part.begin(), left_part.end());
    result.insert(result.end(), equal_part.begin(), equal_part.end());
    result.insert(result.end(), right_part.begin(), right_part.end());
    return result;
}

int main() {
    vector<int> arr = {2, 0, 2, 1, 1, 0};
    vector<int> sorted_arr = randomized_quicksort(arr);
    for (int num : sorted_arr) {
        cout << num << " ";
    }
    cout << endl;
    return 0;
}

四、性能分析

4.1 时间复杂度

随机快排 - 荷兰国旗划分在平均情况下的时间复杂度为 O ( n l o g n ) O(n log n) O(nlogn)。由于随机选择枢轴,使得每次划分都能大概率地将数组分成两个大致相等的子数组,递归深度为 O ( l o g n ) O(log n) O(logn),每次划分操作的时间复杂度为 O ( n ) O(n) O(n),因此整体时间复杂度为 O ( n l o g n ) O(n log n) O(nlogn)。在最坏情况下,虽然随机化枢轴选择降低了出现的概率,但仍有可能每次都选择到数组中的最大值或最小值,导致划分不均衡,此时时间复杂度退化为 O ( n 2 ) O(n^2) O(n2)。不过,这种极端情况发生的概率极小。

4.2 空间复杂度

随机快排 - 荷兰国旗划分的空间复杂度主要取决于递归调用栈的深度。在平均情况下,递归深度为 O ( l o g n ) O(log n) O(logn),因此空间复杂度为 O ( l o g n ) O(log n) O(logn);在最坏情况下,递归深度为 O ( n ) O(n) O(n),空间复杂度为 O ( n ) O(n) O(n)。此外,在划分过程中,虽然没有使用额外的与数组长度成正比的空间,但存在一些临时变量(如指针和交换过程中的临时存储),这些变量占用的空间为常数级,对整体空间复杂度影响较小。

五、实际应用场景

5.1 数据排序与预处理

在数据处理和分析领域,随机快排 - 荷兰国旗划分常用于对大规模数据进行排序。例如,在数据库查询结果的排序、日志文件中数据的整理等场景中,该算法能够高效地将数据按指定顺序排列,为后续的数据分析和处理提供基础。其随机化和优化的划分策略,使得在处理各种分布的数据时都能保持较好的性能。

5.2 并行计算与分布式系统

在并行计算和分布式系统中,随机快排 - 荷兰国旗划分可以用于数据的并行排序。将大规模数据划分为多个子数组,每个子数组在不同的计算节点上进行随机快排 - 荷兰国旗划分,最后合并结果。这种方式能够充分利用并行计算资源,提高排序效率,适用于处理海量数据的场景,如大数据分析平台中的数据排序任务。

That’s all, thanks for reading!
觉得有用就点个赞、收进收藏夹吧!关注我,获取更多干货~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2406859.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

篇章二 论坛系统——系统设计

目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…

32单片机——基本定时器

STM32F103有众多的定时器&#xff0c;其中包括2个基本定时器&#xff08;TIM6和TIM7&#xff09;、4个通用定时器&#xff08;TIM2~TIM5&#xff09;、2个高级控制定时器&#xff08;TIM1和TIM8&#xff09;&#xff0c;这些定时器彼此完全独立&#xff0c;不共享任何资源 1、定…

热门Chrome扩展程序存在明文传输风险,用户隐私安全受威胁

赛门铁克威胁猎手团队最新报告披露&#xff0c;数款拥有数百万活跃用户的Chrome扩展程序正在通过未加密的HTTP连接静默泄露用户敏感数据&#xff0c;严重威胁用户隐私安全。 知名扩展程序存在明文传输风险 尽管宣称提供安全浏览、数据分析或便捷界面等功能&#xff0c;但SEMR…

Matlab实现任意伪彩色图像可视化显示

Matlab实现任意伪彩色图像可视化显示 1、灰度原始图像2、RGB彩色原始图像 在科研研究中&#xff0c;如何展示好看的实验结果图像非常重要&#xff01;&#xff01;&#xff01; 1、灰度原始图像 灰度图像每个像素点只有一个数值&#xff0c;代表该点的​​亮度&#xff08;或…

图解JavaScript原型:原型链及其分析 | JavaScript图解

​​ 忽略该图的细节&#xff08;如内存地址值没有用二进制&#xff09; 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么&#xff1a;保存在堆中一块区域&#xff0c;同时在栈中有一块区域保存其在堆中的地址&#xff08;也就是我们通常说的该变量指向谁&…

《信号与系统》第 6 章 信号与系统的时域和频域特性

目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …

一些实用的chrome扩展0x01

简介 浏览器扩展程序有助于自动化任务、查找隐藏的漏洞、隐藏自身痕迹。以下列出了一些必备扩展程序&#xff0c;无论是测试应用程序、搜寻漏洞还是收集情报&#xff0c;它们都能提升工作流程。 FoxyProxy 代理管理工具&#xff0c;此扩展简化了使用代理&#xff08;如 Burp…

AxureRP-Pro-Beta-Setup_114413.exe (6.0.0.2887)

Name&#xff1a;3ddown Serial&#xff1a;FiCGEezgdGoYILo8U/2MFyCWj0jZoJc/sziRRj2/ENvtEq7w1RH97k5MWctqVHA 注册用户名&#xff1a;Axure 序列号&#xff1a;8t3Yk/zu4cX601/seX6wBZgYRVj/lkC2PICCdO4sFKCCLx8mcCnccoylVb40lP

02.运算符

目录 什么是运算符 算术运算符 1.基本四则运算符 2.增量运算符 3.自增/自减运算符 关系运算符 逻辑运算符 &&&#xff1a;逻辑与 ||&#xff1a;逻辑或 &#xff01;&#xff1a;逻辑非 短路求值 位运算符 按位与&&#xff1a; 按位或 | 按位取反~ …

uni-app学习笔记三十五--扩展组件的安装和使用

由于内置组件不能满足日常开发需要&#xff0c;uniapp官方也提供了众多的扩展组件供我们使用。由于不是内置组件&#xff0c;需要安装才能使用。 一、安装扩展插件 安装方法&#xff1a; 1.访问uniapp官方文档组件部分&#xff1a;组件使用的入门教程 | uni-app官网 点击左侧…

6.9-QT模拟计算器

源码: 头文件: widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QMouseEvent>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);…

java高级——高阶函数、如何定义一个函数式接口类似stream流的filter

java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用&#xff08;Math::max&#xff09; 2 函数接口…

数据结构第5章:树和二叉树完全指南(自整理详细图文笔记)

名人说&#xff1a;莫道桑榆晚&#xff0c;为霞尚满天。——刘禹锡&#xff08;刘梦得&#xff0c;诗豪&#xff09; 原创笔记&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 上一篇&#xff1a;《数据结构第4章 数组和广义表》…

Windows电脑能装鸿蒙吗_Windows电脑体验鸿蒙电脑操作系统教程

鸿蒙电脑版操作系统来了&#xff0c;很多小伙伴想体验鸿蒙电脑版操作系统&#xff0c;可惜&#xff0c;鸿蒙系统并不支持你正在使用的传统的电脑来安装。不过可以通过可以使用华为官方提供的虚拟机&#xff0c;来体验大家心心念念的鸿蒙系统啦&#xff01;注意&#xff1a;虚拟…

基于江科大stm32屏幕驱动,实现OLED多级菜单(动画效果),结构体链表实现(独创源码)

引言 在嵌入式系统中&#xff0c;用户界面的设计往往直接影响到用户体验。本文将以STM32微控制器和OLED显示屏为例&#xff0c;介绍如何实现一个多级菜单系统。该系统支持用户通过按键导航菜单&#xff0c;执行相应操作&#xff0c;并提供平滑的滚动动画效果。 本文设计了一个…

yaml读取写入常见错误 (‘cannot represent an object‘, 117)

错误一&#xff1a;yaml.representer.RepresenterError: (‘cannot represent an object’, 117) 出现这个问题一直没找到原因&#xff0c;后面把yaml.safe_dump直接替换成yaml.dump&#xff0c;确实能保存&#xff0c;但出现乱码&#xff1a; 放弃yaml.dump&#xff0c;又切…

WebRTC调研

WebRTC是什么&#xff0c;为什么&#xff0c;如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…

针对药品仓库的效期管理问题,如何利用WMS系统“破局”

案例&#xff1a; 某医药分销企业&#xff0c;主要经营各类药品的批发与零售。由于药品的特殊性&#xff0c;效期管理至关重要&#xff0c;但该企业一直面临效期问题的困扰。在未使用WMS系统之前&#xff0c;其药品入库、存储、出库等环节的效期管理主要依赖人工记录与检查。库…

从零开始了解数据采集(二十八)——制造业数字孪生

近年来&#xff0c;我国的工业领域正经历一场前所未有的数字化变革&#xff0c;从“双碳目标”到工业互联网平台的推广&#xff0c;国家政策和市场需求共同推动了制造业的升级。在这场变革中&#xff0c;数字孪生技术成为备受关注的关键工具&#xff0c;它不仅让企业“看见”设…

AD学习(3)

1 PCB封装元素组成及简单的PCB封装创建 封装的组成部分&#xff1a; &#xff08;1&#xff09;PCB焊盘&#xff1a;表层的铜 &#xff0c;top层的铜 &#xff08;2&#xff09;管脚序号&#xff1a;用来关联原理图中的管脚的序号&#xff0c;原理图的序号需要和PCB封装一一…