Java 快速排序算法详解及通用实现模板案例示范

news2024/10/17 6:19:10

1. 引言

在众多排序算法中,快速排序(QuickSort) 是一种非常经典且高效的算法。它采用“分治法”的策略,通过递归地将数组分割成更小的部分,从而快速完成排序操作。快速排序的平均时间复杂度为 O(n log n),在大多数情况下表现优异。

2. 快速排序的原理

快速排序的核心思想是选择一个“基准”元素,然后通过比较将数组划分为两部分:小于基准值的元素大于基准值的元素。接下来,递归地对左右两部分进行排序,最终得到一个有序数组。

快速排序的关键步骤包括:

  1. 选择基准元素(Pivot)。
  2. 划分数组:将小于基准值的元素放在左侧,大于基准值的元素放在右侧。
  3. 递归排序左右两部分。

3. 快速排序适用问题类型

快速排序主要用于解决以下问题:

  1. 对无序数组进行排序:适用于需要高效排序的场景,如对大量数据的排序。
  2. 查找排序后的中位数或其他特定位置的元素:例如在数据集中寻找第 k 小的元素。
  3. 需要原地排序的场景:快速排序是原地排序算法,无需额外的内存空间。

4. 快速排序的通用实现模板

4.1 标准快速排序模板

下面是快速排序的标准实现:

public class QuickSort {
    /**
     * 快速排序的主方法
     * @param arr 待排序的数组
     * @param left 数组的左边界
     * @param right 数组的右边界
     */
    public static void quickSort(int[] arr, int left, int right) {
        if (left < right) {
            // 获取基准元素的正确位置
            int pivotIndex = partition(arr, left, right);
            // 递归排序左边部分
            quickSort(arr, left, pivotIndex - 1);
            // 递归排序右边部分
            quickSort(arr, pivotIndex + 1, right);
        }
    }

    /**
     * 划分数组,将小于基准值的元素放在左边,大于基准值的放在右边
     * @param arr 待划分的数组
     * @param left 左边界
     * @param right 右边界
     * @return 基准元素的最终位置
     */
    private static int partition(int[] arr, int left, int right) {
        int pivot = arr[right];  // 选择最右边的元素作为基准
        int i = left - 1;  // i是小于pivot的元素的边界

        for (int j = left; j < right; j++) {
            if (arr[j] < pivot) {
                i++;
                swap(arr, i, j);  // 交换小于pivot的元素到左边
            }
        }

        swap(arr, i + 1, right);  // 将pivot放到正确的位置
        return i + 1;
    }

    /**
     * 交换数组中的两个元素
     * @param arr 数组
     * @param i 元素索引 i
     * @param j 元素索引 j
     */
    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    public static void main(String[] args) {
        int[] array = {9, 7, 5, 11, 12, 2, 14, 3, 10, 6};
        quickSort(array, 0, array.length - 1);
        System.out.println("排序后的数组:" + java.util.Arrays.toString(array));
    }
}

解释

  • quickSort 是主函数,负责递归地对数组的左右部分进行排序。
  • partition 函数通过选择基准元素将数组划分为两部分。
  • swap 用于交换两个元素的位置。
4.2 快速排序的时间复杂度

快速排序的时间复杂度在平均情况下为 O(n log n),这是因为每次划分时,数组被大致平分为两部分,每个元素在每层递归中最多比较一次。递归的深度为 log n,因此总的比较次数为 n log n

在最坏情况下(当数组已经有序,且每次选择的基准是最小或最大的元素),时间复杂度会退化为 O(n²)。为了解决这个问题,可以随机选择基准元素,或者使用三数取中法(选择左、中、右三个元素的中值作为基准)来减少最坏情况发生的概率。

5. 快速排序的时序图

在这里插入图片描述

6. 案例分析:快速排序在电商系统中的应用

在电商系统中,快速排序的应用场景非常广泛,特别是在处理大规模数据时。例如:

  1. 商品价格排序:快速排序可以用来对商品价格进行排序,以便用户可以按照价格升序或降序查看商品。
  2. 订单记录排序:通过对订单的时间戳进行排序,系统可以快速找到最近的订单。
  3. 用户评分排序:对于基于用户评分的推荐系统,快速排序可以对用户评分进行排序,以便根据评分推荐商品。
案例 1:商品价格排序

假设电商系统需要对某一类别的商品价格进行排序,以便用户可以根据价格从低到高查看商品列表。以下是通过快速排序对商品价格排序的代码示例:

public class ProductPriceSort {
    public static void quickSortPrices(double[] prices, int left, int right) {
        if (left < right) {
            int pivotIndex = partition(prices, left, right);
            quickSortPrices(prices, left, pivotIndex - 1);
            quickSortPrices(prices, pivotIndex + 1, right);
        }
    }

    private static int partition(double[] prices, int left, int right) {
        double pivot = prices[right];
        int i = left - 1;

        for (int j = left; j < right; j++) {
            if (prices[j] < pivot) {
                i++;
                swap(prices, i, j);
            }
        }

        swap(prices, i + 1, right);
        return i + 1;
    }

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

    public static void main(String[] args) {
        double[] productPrices = {299.99, 499.99, 199.99, 899.99, 799.99, 100.00};
        quickSortPrices(productPrices, 0, productPrices.length - 1);
        System.out.println("排序后的商品价格:" + java.util.Arrays.toString(productPrices));
    }
}
案例 2:订单记录排序

电商系统通常会按照时间对订单进行排序,以便用户查看历史订单记录。通过对订单时间戳进行排序,用户可以方便地查看最近的订单。

public class OrderSort {
    public static void quickSortOrders(long[] orderTimes, int left, int right) {
        if (left < right) {
            int pivotIndex = partition(orderTimes, left, right);
            quickSortOrders(orderTimes, left, pivotIndex - 1);
            quickSortOrders(orderTimes, pivotIndex + 1, right);
        }
    }

    private static int partition(long[] orderTimes, int left, int right) {
        long pivot = orderTimes[right];
        int i = left - 1;

        for (int j = left; j < right; j++) {
            if (orderTimes[j] < pivot) {
                i++;
                swap(orderTimes, i, j);
            }
        }

        swap(orderTimes, i + 1, right);
        return i + 1;
    }

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

    public static void main(String[] args) {
        long[] orderTimes = {1623010200L, 1623011100L, 1623010500L, 1623010800L};
        quickSortOrders(orderTimes, 0, orderTimes.length - 1);
        System.out.println("排序后的订单时间:" + java.util.Arrays.toString(orderTimes));
    }
}

7. 快速排序的常见问题

  1. 最坏情况性能退化:当输入数组已经有序,或者每次选择的基准值是最小或最大值时,快速排序的时间复杂度会退化为 O(n²)。为了解决这个问题,可以使用三数取中法或随机选择基准值来避免这种情况。
  2. 递归过深导致栈溢出:对于非常大的数组,递归过深可能导致栈溢出。可以通过优化递归条件,或者采用尾递归优化来避免这个问题。
  3. 不稳定性:快速排序是一种不稳定排序,即相同值的元素在排序后相对顺序可能会发生变化。如果对稳定性有要求,可以考虑其他排序算法,如归并排序。

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

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

相关文章

webpack 学习入门

webpack 1. 简介1.1 webpack 是什么1.2 webpack 五个核心概念1.2.1 入口 - Entry1.2.2 出口 - Output1.2.3 Loader1.2.4 插件 - Plugins1.2.6 模式 - Mode 2. webpack 初体验2.1 初始化配置2.1.1. 准备2.1.2. 写代码2.1.3 编译打包应用 3. webpack 开发环境的基本配置3.1 打包样…

《深度学习》OpenCV EigenFaces算法 人脸识别

目录 一、EigenFaces算法 1、什么是EigenFaces算法 2、原理 3、实现步骤 1&#xff09;数据预处理 2&#xff09;特征提取 3&#xff09;构建模型 4&#xff09;识别 4、优缺点 1&#xff09;优点 2&#xff09;缺点 二、案例实现 1、完整代码 运行结果&#xff…

大学新生编程入门指南:如何选择编程语言与制定学习计划

大学新生编程入门指南&#xff1a;如何选择编程语言与制定学习计划 编程已成为当代大学生的必备技能&#xff0c;尤其是在信息技术高速发展的今天&#xff0c;编程能力不仅能帮助你在课堂学习中脱颖而出&#xff0c;更能为未来职业生涯打下坚实的基础。然而&#xff0c;面对如…

The 48 bit pointer

在 Intel CPU 和 Arm CPU 中&#xff0c;用户空间的指针地址默认都只使用低 48 位&#xff0c;高16 位总是 0。 写一小段代码验证下&#xff1a; #include <stdio.h> #include <memory.h> #include <stdlib.h>void o(long long ptr) {printf("%016p: &…

如何通过CDN优化网站服务器访问速度?

CDN&#xff0c;即内容分发网络&#xff08;Content Delivery Network&#xff09;&#xff0c;在现代互联网中起着重要作用。它可以显著提升网站服务器的访问速度。以下是CDN在加速网站访问方面的主要优势及其工作原理。 1. 全球分布的服务器节点 CDN通过在全球范围内布设多个…

mysql的重置

今天用Navicat16去连接mysql突然就连不上了。一直报错 连接本地mysql时出现2003-Can‘t connect to MySql server on ‘localhost‘(10061)错误。 以为是Navicat过期了。正好Navicat推出了Lite 17免费版本&#xff0c;心想正好可以尝尝鲜&#xff0c;而且还支持连接Redis&#…

C++:Boost的安装和使用

1、Boost简介 Boost的本质就是一个开源C库&#xff0c;它包含多种功能强大的模块&#xff0c;如&#xff1a;字符串文本处理模块、容器、算法、多线程、智能指针、线程池等模块 2、Boost的下载和安装 &#xff08;1&#xff09;Boost下载 官网&#xff1a;http://www.boost…

【JAVA毕业设计】基于Vue和SpringBoot的医院电子病历管理系统

本文项目编号 T 008 &#xff0c;文末自助获取源码 \color{red}{T008&#xff0c;文末自助获取源码} T008&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 医…

股票分析软件设计

设计一个功能齐全的股票分析软件是一个复杂且有挑战性的项目&#xff0c;需要综合运用多种编程技术和金融知识。下面是一个总体设计思路和主要功能模块的概述&#xff1a; 主要功能模块&#xff1a; 1. 用户界面&#xff08;UI/UX&#xff09; - 显示K线图&#xff0c;并允许…

西门子变频器SINAMICS V20选型

SINAMICS V20共有五种外形尺寸可供选择&#xff0c;输出功率覆盖0.12kW-30kW&#xff1a; V20订货号 单相230V&#xff1a; 三相380V&#xff1a;

数据链中常见电磁干扰matlab仿真,对比噪声调频,线性调频,噪声,扫频,灵巧五种干扰模型

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 噪声调频干扰 4.2 线性调频干扰 4.3 噪声干扰 4.4 扫频干扰 4.5 灵巧干扰 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 2.算法运行软件版本 matlab2022a 3…

数据结构——单链表的基本操作

前言 介绍 &#x1f343;数据结构专区&#xff1a;数据结构 参考 该部分知识参考于《数据结构&#xff08;C语言版 第2版&#xff09;》29~36页 补充 后序代码中会遇见这个结构体 typedef struct LNode { ... }LNode,*LinkList; 对于这个代码&#xff0c;目的是定义线性表…

【云原生kubernetes系列--coredns篇】

1.corednsd的介绍 官网&#xff1a;https://coredns.io/ CoreDNS是一个灵活、可扩展的 DNS 服务器&#xff0c;可以充当 Kubernetes 集群 DNS。与 Kubernetes 一样&#xff0c;CoreDNS 项目由 CNCF coredns在K8S中的用途,主要是用作服务发现&#xff0c;也就是服务(应用)之间…

Python中 文件操作及与数据库的交互

在数据驱动的时代&#xff0c;Python不仅是一门强大的编程语言&#xff0c;更是与文件系统和数据库交互的重要工具。无论是读取配置文件、处理数据集&#xff0c;还是与数据库进行交互&#xff0c;Python都能轻松胜任。那么&#xff0c;如何高效地进行文件操作&#xff0c;并实…

uniapp 小程序0到1教程

先说明一下&#xff0c;uni-app的文档很乱 一、注册微信小程序 如果你还没有微信公众平台的账号&#xff0c;请先进入微信公众平台首页&#xff0c;点击 “立即注册” 按钮进行注册。注册的账号类型可以是订阅号、服务号、小程序以及企业微信&#xff0c;我们选择 “小程序”…

基本计算器 II

文章目录 题目解析解题小结 题目解析 给你一个字符串表达式 s &#xff0c;请你实现一个基本计算器来计算并返回它的值。 整数除法仅保留整数部分。 你可以假设给定的表达式总是有效的。所有中间结果将在 [-231, 231 - 1] 的范围内。 注意&#xff1a;不允许使用任何将字符…

lazyLoad

//1.通过React的lazy函数配合import()函数动态加载路由组件 > 路由组件代码会被分开打包 const Login lazy(()>import(/pages/Login)) //2.通过<Suspense>指定在加载得到路由打包文件前显示一个自定义loading界面 <Suspense fallback{<h1&…

Arduino配置ESP32环境

Arduino配置ESP32环境 引言一、IDE下载教程操作取巧方法 二、社区安装包三、官方手动安装 引言 最近入手了一款ESP32-C3的开发板&#xff0c;想继续沿用现有Arduino IDE&#xff0c;网上看了很多方法&#xff0c;大致分了三类&#xff1a;IDE下载、社区安装包、github手动配置…

法规标准-懂车帝智能化实测标准(2024版)

场景&#xff1a;AEB追尾静态假车 1.场地布置&#xff1a; ——测试选取封闭场地&#xff0c;试验路面应为水平、干燥&#xff0c;具有良好附着能力的混凝土沥青路面&#xff0c;附着系数在0.8以上 ——试验过程中&#xff0c;在试验道路两边3m以内或者静止目标车前方30m内不能…

简单掌握 Android Studio 模拟器

下载 Android Studio安装adb、配置 adb创建一个新的Activity项目创建模拟器 参考&#xff1a;mac系统下android studio创建手机模拟器adb命令使用&#xff0c;可在模拟器上安装app 打开终端 adb devices // 查询设备 adb install xx/xx/xx // 安装apk&#xff08;apk路径拖进…