【算法】手撕快速排序

快速排序的思想

任取一个元素作为枢轴,然后想办法把这个区间划分为两部分,小于等于枢轴的放左边,大于等于枢轴的放右边

然后递归处理左右区间,直到空或只剩一个

具体动画演示详见 

数据结构合集 - 快速排序(算法过程, 效率分析, 稳定性分析)

 Lomuto 分区方案(单边扫描法)

public static void quickSort(int[] nums){subSort(nums, 0, nums.length-1);
}private static void subSort(int[] nums, int low, int high){if(low < high){int pos = partition(nums, low, high);subSort(nums, low, pos-1);subSort(nums, pos+1, high);}
}private static int partition(int[] nums, int low, int high){int pivot = nums[high];int i = low-1;for(int j=low; j<high; j++){if(nums[j] <= pivot){i++;int tmp = nums[i];nums[i] = nums[j];nums[j] = tmp;}}// 将pivot放到正确的位置int temp = nums[i + 1];nums[i + 1] = nums[high];nums[high] = temp;return i+1;
}

进一步优化上述代码

优化点:小数组时改用插入排序

当待排序数组较小的时候,快速排序的递归开销会比插入排序更大

public static void quickSort(int[] nums){subSort(nums, 0, nums.length-1);}private static void subSort(int[] nums, int low, int high) {if (low < high) {// 小数组改用插入排序(阈值可调整,通常 10~20)if (high - low < 10) {insertionSort(nums, low, high);return;}int pos = partition(nums, low, high);subSort(nums, low, pos - 1);subSort(nums, pos + 1, high);}}private static void insertionSort(int[] nums, int low, int high) {for (int i = low + 1; i <= high; i++) {int key = nums[i];int j = i - 1;while (j >= low && nums[j] > key) {nums[j + 1] = nums[j];j--;}nums[j + 1] = key;}}private static int partition(int[] nums, int low, int high){int pivot = nums[high];int i = low-1;for(int j=low; j<high; j++){if(nums[j] <= pivot){i++;int tmp = nums[i];nums[i] = nums[j];nums[j] = tmp;}}// 将pivot放到正确的位置int temp = nums[i + 1];nums[i + 1] = nums[high];nums[high] = temp;return i+1;}

优化点:三数取中法选择pivot避免最坏情况

如果pivot选择最左或最右,在已排序或接近排序的数组上会导致最坏的情况O(n^{2}

public static void quickSort(int[] nums){subSort(nums, 0, nums.length-1);}private static void subSort(int[] nums, int low, int high) {if (low < high) {// 小数组改用插入排序(阈值可调整,通常 10~20)if (high - low < 10) {insertionSort(nums, low, high);return;}int pos = partition(nums, low, high);subSort(nums, low, pos - 1);subSort(nums, pos + 1, high);}}private static void insertionSort(int[] nums, int low, int high) {for (int i = low + 1; i <= high; i++) {int key = nums[i];int j = i - 1;while (j >= low && nums[j] > key) {nums[j + 1] = nums[j];j--;}nums[j + 1] = key;}}private static int partition(int[] nums, int low, int high){// 三数取中法选择 pivotint mid = low + (high - low) / 2;if (nums[low] > nums[mid]) swap(nums, low, mid);if (nums[low] > nums[high]) swap(nums, low, high);if (nums[mid] > nums[high]) swap(nums, mid, high);// 将中位数放到 nums[high]swap(nums, mid, high);int pivot = nums[high];int i = low-1;for(int j=low; j<high; j++){if(nums[j] <= pivot){i++;swap(nums, i, j);}}// 将pivot放到正确的位置swap(nums, i + 1, high);return i+1;}private static void swap(int[] nums, int i, int j) {int tmp = nums[i];nums[i] = nums[j];nums[j] = tmp;}

优化点:尾递归优化避免栈溢出

public static void quickSort(int[] nums){subSort(nums, 0, nums.length-1);}private static void subSort(int[] nums, int low, int high) {// 小数组改用插入排序if (high - low < 10) {insertionSort(nums, low, high);return;}// 尾递归优化while (low < high) {int pos = partition(nums, low, high);if (pos - low < high - pos) {subSort(nums, low, pos - 1);low = pos + 1;} else {subSort(nums, pos + 1, high);high = pos - 1;}}}private static void insertionSort(int[] nums, int low, int high) {for (int i = low + 1; i <= high; i++) {int key = nums[i];int j = i - 1;while (j >= low && nums[j] > key) {nums[j + 1] = nums[j];j--;}nums[j + 1] = key;}}private static int partition(int[] nums, int low, int high){// 三数取中法选择 pivotint mid = low + (high - low) / 2;if (nums[low] > nums[mid]) swap(nums, low, mid);if (nums[low] > nums[high]) swap(nums, low, high);if (nums[mid] > nums[high]) swap(nums, mid, high);// 将中位数放到 nums[high]swap(nums, mid, high);int pivot = nums[high];int i = low-1;for(int j=low; j<high; j++){if(nums[j] <= pivot){i++;swap(nums, i, j);}}// 将pivot放到正确的位置swap(nums, i + 1, high);return i+1;}private static void swap(int[] nums, int i, int j) {int tmp = nums[i];nums[i] = nums[j];nums[j] = tmp;}

优化点:双轴快排

java的Arrays.sort对小数组用插入排序,对大数组用双轴快排(比单轴更快)

双轴快排的基本思路:

  1. 选取两个 pivot
    • pivot1 = nums[left](较小的 pivot)
    • pivot2 = nums[right](较大的 pivot)
    • 确保 pivot1 ≤ pivot2(否则交换)
  1. 分区
    • [left, i)< pivot1
    • [i, k)≥ pivot1 && ≤ pivot2
    • [k, j]:未处理区间
    • (j, right]> pivot2
  1. 递归处理三个子数组
    • [left, i-1](小于 pivot1 的部分)
    • [i, j](介于 pivot1pivot2 之间的部分)
    • [j+1, right](大于 pivot2 的部分)
public static void quickSort(int[] nums){dualPivotQuickSort(nums, 0, nums.length-1);}private static void dualPivotQuickSort(int[] nums, int left, int right) {if (left >= right) return;// 确保 pivot1 ≤ pivot2if (nums[left] > nums[right]) {swap(nums, left, right);}int pivot1 = nums[left];int pivot2 = nums[right];int i = left + 1;  // [left+1, i) 存储 < pivot1 的元素int k = left + 1;  // [i, k) 存储 ≥ pivot1 && ≤ pivot2 的元素int j = right - 1; // (j, right-1] 存储 > pivot2 的元素while (k <= j) {if (nums[k] < pivot1) {swap(nums, i, k);i++;k++;} else if (nums[k] <= pivot2) {k++;} else {swap(nums, k, j);j--;}}// 将 pivot1 和 pivot2 放到正确位置swap(nums, left, i - 1);swap(nums, right, j + 1);// 递归处理三个子数组dualPivotQuickSort(nums, left, i - 2);   // < pivot1dualPivotQuickSort(nums, i, j);          // pivot1 ≤ x ≤ pivot2dualPivotQuickSort(nums, j + 2, right);  // > pivot2}private static void swap(int[] nums, int i, int j) {int tmp = nums[i];nums[i] = nums[j];nums[j] = tmp;}

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

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

相关文章

《八大排序算法》

相关概念 排序&#xff1a;使一串记录&#xff0c;按照其中某个或某些关键字的大小&#xff0c;递增或递减的排列起来。稳定性&#xff1a;它描述了在排序过程中&#xff0c;相等元素的相对顺序是否保持不变。假设在待排序的序列中&#xff0c;有两个元素a和b&#xff0c;它们…

深度学习篇---paddleocr正则化提取

文章目录 前言一、代码总述&介绍1.1导入必要的库1.1.1cv21.1.2re1.1.3paddleocr 1.2初始化PaddleOCR1.3打开摄像头1.4使用 PaddleOCR 进行识别1.5定义正则表达式模式1.6打印提取结果1.7异常处理 二、正则表达式2.1简介2.2常用正则表达式模式及原理2.2.1. 快递单号模式2.2.2…

JavaScript DOM与元素操作

目录 DOM 树、DOM 对象、元素操作 一、DOM 树与 DOM 对象 二、获取 DOM 元素 1. 基础方法 2. 现代方法&#xff08;ES6&#xff09; 三、修改元素内容 四、修改元素常见属性 1. 标准属性 2. 通用方法 五、通过 style 修改样式 六、通过类名修改样式 1. className 属…

单元测试的编写

Python 单元测试示例 在 Python 中&#xff0c;通常使用 unittest 模块来编写单元测试。以下是一个简单的示例&#xff1a; 示例代码&#xff1a;calculator.py # calculator.py def add(a, b):return a bdef subtract(a, b):return a - b 单元测试代码&#xff1a;test_c…

大模型学习:从零到一实现一个BERT微调

目录 一、准备阶段 1.导入模块 2.指定使用的是GPU还是CPU 3.加载数据集 二、对数据添加词元和分词 1.根据BERT的预训练&#xff0c;我们要将一个句子的句头添加[CLS]句尾添加[SEP] 2.激活BERT词元分析器 3.填充句子为固定长度 代码解释&#xff1a; 三、数据处理 1.…

10组时尚复古美学自然冷色调肖像电影照片调色Lightroom预设 De La Mer – Nautical Lightroom Presets

De La Mer 预设系列包含 10 种真实的调色预设&#xff0c;适用于肖像、时尚和美术。为您的肖像摄影带来电影美学和个性&#xff01; De La Mer 预设非常适合专业人士和业余爱好者&#xff0c;可在桌面或移动设备上使用&#xff0c;为您的摄影项目提供轻松的工作流程。这套包括…

SDL多窗口多线程渲染技术解析

SDL多窗口多线程渲染技术解析 技术原理 SDL多线程模型与窗口管理 SDL通过SDL_Thread结构体实现跨平台线程管理。在多窗口场景中,每个窗口需关联独立的渲染器,且建议遵循以下原则: 窗口与渲染器绑定:每个窗口创建时生成专属渲染器(SDL_CreateRenderer),避免跨线程操作…

QT 跨平台发布指南

一、Windows 平台发布 1. 使用 windeployqt 工具 windeployqt --release --no-compiler-runtime your_app.exe 2. 需要包含的文件 应用程序 .exe 文件 Qt5Core.dll, Qt5Gui.dll, Qt5Widgets.dll 等 Qt 库 platforms/qwindows.dll 插件 styles/qwindowsvistastyle.dll (如果使…

L2-037 包装机 (分数25)(详解)

题目链接——L2-037 包装机 问题分析 这个题目就是模拟了物品在传送带和筐之间的传送过程。传送带用队列模拟&#xff0c;筐用栈模拟。 输入 3 4 4 GPLT PATA OMSA 3 2 3 0 1 2 0 2 2 0 -1输出 根据上述操作&#xff0c;输出的物品顺序是&#xff1a; MATA样例分析 初始…

机器学习的一百个概念(4)下采样

前言 本文隶属于专栏《机器学习的一百个概念》&#xff0c;该专栏为笔者原创&#xff0c;引用请注明来源&#xff0c;不足和错误之处请在评论区帮忙指出&#xff0c;谢谢&#xff01; 本专栏目录结构和参考文献请见[《机器学习的一百个概念》 ima 知识库 知识库广场搜索&…

qt6下配置qopengl

qt部件选择 Qt 6&#xff1a;需要手动选择 Qt Shader Tools 和 Qt 5 Compatibility Module&#xff08;如果需要兼容旧代码&#xff09; cmake文件 cmake_minimum_required(VERSION 3.16) # Qt6 推荐最低 CMake 3.16 project(myself VERSION 0.1 LANGUAGES CXX)set(CMAKE_A…

数据安全系列4:密码技术的应用-接口调用的身份识别

传送门 数据安全系列1&#xff1a;开篇 数据安全系列2&#xff1a;单向散列函数概念 数据安全系列3&#xff1a;密码技术概述 什么是认证&#xff1f; 一谈到认证&#xff0c;多数人的反应可能就是"用户认证" 。就是应用系统如何识别用户的身份&#xff0c;直接…

STL之map和set

1. 关联式容器 vector、list、deque、 forward_list(C11)等&#xff0c;这些容器统称为序列式容器&#xff0c;因为其底层为线性序列的数据结构&#xff0c;里面存储的是元素本身。 关联式容器也是用来存储数据的&#xff0c;与序列式容器不同的是&#xff0c;其里面存储的是结…

Vue3 其它API Teleport 传送门

Vue3 其它API Teleport 传送门 在定义一个模态框时&#xff0c;父组件的filter属性会影响子组件的position属性&#xff0c;导致模态框定位错误使用Teleport解决这个问题把模态框代码传送到body标签下

C++练习

1.将File练习题&#xff0c;内部的FILE*描述符&#xff0c;改成int描述符 2。写一个类Fifo管道类。提高难度&#xff0c;什么都不提示。只要求&#xff1a;使用自己编写的Fifo类对象&#xff0c;实现2个终端之间互相聊天 file.cpp #include <iostream> #include <c…

《Python Web网站部署应知应会》No4:基于Flask的调用AI大模型的高性能博客网站的设计思路和实战(上)

基于Flask的调用AI大模型的高性能博客网站的设计思路和实战&#xff08;上&#xff09; 摘要 本文详细探讨了一个基于Flask框架的高性能博客系统的设计与实现&#xff0c;该系统集成了本地AI大模型生成内容的功能。我们重点关注如何在高并发、高负载状态下保持系统的高性能和…

实现一个简易版的前端监控 SDK

【简易版的前端监控系统】 1、Promise的错误如何监控&#xff1f;–promise不是所有都是接口请求 2、接口的报错如何监控&#xff1f;–全局监控sdk&#xff0c;不改动公共的请求方法、不改动业务代码&#xff1b;一般接口使用axios请求 3、资源的报错如何监控&#xff1f; 4、…

【操作系统】软中断vs硬中断

在操作系统中&#xff0c;中断&#xff08;Interrupt&#xff09; 是 CPU 响应外部事件的重要机制&#xff0c;分为 硬中断&#xff08;Hardware Interrupt&#xff09; 和 软中断&#xff08;Software Interrupt&#xff09;。它们的核心区别在于 触发方式 和 处理机制。 1. 硬…

力扣刷题-热题100题-第27题(c++、python)

21. 合并两个有序链表 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/merge-two-sorted-lists/description/?envTypestudy-plan-v2&envIdtop-100-liked 常规法 创建一个新链表&#xff0c;遍历list1与list2&#xff0c;将新链表指向list1与list2…

Python包下载路径 Chrome用户数据 修改到非C盘

查看 site-packages 是否能通过命令行完成&#xff1f; 可以&#xff0c;使用以下命令&#xff08;不需写脚本&#xff09;&#xff1a; python -m site输出包含&#xff1a; sys.path site-packages 路径&#xff08;全局和用户级&#xff09; 如果只想看安装路径&#…