排序算法——快速排序

devtools/2025/2/4 4:27:56/

代码仓库: 1037827920/AlgorithmZoo

快速排序

算法步骤

  1. 选择基准元素,从数组中选择一个元素作为基准,通常选择方式有:
    • 第一个元素
    • 最后一个元素
    • 中间元素
    • 随机选择
  2. 分区操作,将数组元素根据基准分为两部分,一部分小于基准,另一部分大于基准
  3. 递归排序,对基准左边和右边的子数组都进行快速排序,每次递归都会进行分区操作
  4. 终止条件:当子数组的大小为1或0时,也就是左指针索引大于等于右指针索引时,递归终止

时间复杂度

平均情况: O(N logN)

假设每次分区操作都能把数组划分成大约两半,这样递归的深度大概就是logN(以2为底,每次除以2,直到除到1,就是递归深度,如log8=3,如果每次对半分,8个元素递归深度为3),所以时间复杂度为O(logN)

每一层递归都需要对数组进行分区操作,这个通过for循环可以看出时间复杂度为O(N)

因此平均情况的时间复杂度为O(NlogN)

最坏情况: O(N^2)

每次选择的基准元素总是数据中的最大或最小元素,如果是这样,分区操作会把数组分成一个空数组和一个包含所有其余元素的数组,递归深度会达到N,每次递归减少一个元素。分区操作复杂度也为O(N),所以最坏情况的时间复杂度为O(N^2)

稳定性

当两个相等的元素在排序后保持原来的相对顺序,说明这个排序算法是稳定的。

快速排序不是稳定的排序算法

举个具体的例子,对数组[5, 2, 5]进行分区操作,选择第二个5作为基准,对数组进行遍历,第一个5保持不变,2会跟第一个5交换位置([2, 5, 5]),然后要把基准放到正确的位置(这时候小于基准的元素的索引是0,+1后是基准要放置的索引),所以第一个5会跟第二个5进行交换,这时候两个相等的元素已经不是原来的相对顺序了,所以快速排序不是稳定的

C++

#include <iostream>
#include <vector>
#include <cmath>
#include <concepts>using std::vector;
using std::swap;
using std::cout;
using std::endl;
using std::totally_ordered;// 快速排序的分区函数
template<totally_ordered T>
int partition(vector<T>& arr, size_t left, size_t right) {// 选择最右边的元素作为基准T pivot = arr[right];// 小于基准的元素的索引size_t i = left;// 遍历数组// 将小于基准的元素放到左边// 将大于基准的元素放到右边for (size_t j = left; j < right; ++j) {if (arr[j] < pivot) {swap(arr[i], arr[j]);++i;}}// 将基准元素放到正确的位置swap(arr[i], arr[right]);// 返回基准元素的索引return i;
}template<totally_ordered T>
void quick_sort(vector<T>& arr, size_t left, size_t right) {if (left < right) {// 分区操作size_t pivot_index = partition<T>(arr, left, right);// 对两边进行递归quick_sort(arr, left, pivot_index - 1);quick_sort(arr, pivot_index + 1, right);}
}int main() {vector<int> arr = { 10, 7, 8, 9, 1, 5 };quick_sort<int>(arr, 0, arr.size() - 1);for (const auto& num : arr) {cout << num << " ";}cout << endl;return 0;
}

C

#include <stdio.h>
#include <stdlib.h>void swap(int* a, int* b) {int tmp = *b;*b = *a;*a = tmp;
}int partition(int* arr, int left, int right) {// 确定基准元素int pivot = arr[right];// 小于基准的元素的索引int i = left;for (int j = left; j < right; ++j) {if (arr[j] < pivot) {swap(&arr[i], &arr[j]);i++;}}// 将基准元素放到正确的位置swap(&arr[i], &arr[right]);// 返回基准元素的索引return i;
}void quick_sort(int* arr, int left, int right) {if (left < right) {int pivot_index = partition(arr, left, right);quick_sort(arr, left, pivot_index - 1);quick_sort(arr, pivot_index + 1, right);}
}int main() {int* arr = (int*)malloc(6 * sizeof(int));if (arr == NULL) {fprintf(stderr, "malloc failed\n");return 1;}arr[0] = 10;arr[1] = 7;arr[2] = 8;arr[3] = 9;arr[4] = 1;arr[5] = 5;quick_sort(arr, 0, 5);for (int i = 0; i < 6; ++i) {printf("%d ", arr[i]);}printf("\n");free(arr);return 0;
}

Rust

use std::vec::Vec;// 快速排序的分区函数
fn partition<T: PartialOrd + Copy>(arr: &mut Vec<T>, left: usize, right: usize) -> usize {// 选择最右边的元素作为基准let pivot = arr[right as usize];// 小于基准的元素的索引let mut i = left;// 遍历数组// 将小于基准的元素放到左边// 将大于基准的元素放到右边for j in left..right {if arr[j] < pivot {arr.swap(i, j);i += 1;}}// 将基准元素放到正确的位置arr.swap(i, right);// 返回基准元素的索引i
}// 快排函数
fn quick_sort<T: PartialOrd + Copy>(arr: &mut Vec<T>, left: usize, right: usize) {if left < right {// 分区操作let pivot_index = partition(arr, left, right);// 对两边执行递归quick_sort(arr, left, pivot_index - 1);quick_sort(arr, pivot_index + 1, right);}
}fn main() {let mut arr = vec![10, 7, 8, 9, 1, 5];let len = arr.len();quick_sort(&mut arr, 0, len - 1);arr.iter().for_each(|x| print!("{x} "));println!();
}

Python

# 快速排序的分区函数
def partition(arr, left, right):# 选择最右边的元素作为基准pivot = arr[right]# 小于基准的元素的索引i = left# 遍历数组# 将小于基准的元素放到左边# 将大于基准的元素放到右边for j in range(left, right):if arr[j] < pivot:arr[i], arr[j] = arr[j], arr[i]i += 1# 将基准元素放到正确的位置arr[i], arr[right] = arr[right], arr[i]# 返回基准元素的索引return i# 递归函数
def quick_sort(arr, left, right):if left < right:# 分区操作pivot_index = partition(arr, left, right)# 对两边进行递归quick_sort(arr, left, pivot_index - 1)quick_sort(arr, pivot_index + 1, right)def main():arr = [10, 7, 8, 9, 1, 5]quick_sort(arr, 0, len(arr) - 1)for num in arr:print(num, end=' ')print()if __name__ == '__main__':main()

http://www.ppmy.cn/devtools/155903.html

相关文章

Hive存储系统全面测试报告

引言 在大数据时代&#xff0c;数据存储和处理技术的重要性日益凸显。Apache Hive作为一个基于Hadoop的数据仓库工具&#xff0c;因其能够提供类SQL查询功能&#xff08;HiveQL&#xff09;而广受欢迎。Hive的设计初衷是为了简化大数据集的查询和管理&#xff0c;它允许用户通…

AMD架构简单读书笔记1

目录 前言 关于AMD架构 AMD64 Features 概述 寄存器 指令集 媒体指令 浮点指令 前言 笔者打算简单的记录一下自己读AMD手册架构的书。笔者先前还记录了自己RISC-V手册的阅读笔记&#xff0c;RISC-V读书笔记-CSDN博客。感兴趣的朋友可以简单的翻阅一二。 AMD的所有技术…

记录 | 基于MaxKB的仿小红书旅游文章AI制作(含图文、视频)

目录 前言一、创建应用Step1 表单Step2 AI对话生成旅游攻略提炼场景Step3 图片生成Step4 视频生成Step5 指定回复二、检验效果三、整体结构视图更新时间前言 参考文章: 自己的感想 想复现文章的内容你需要先学习下我之前的三篇文章中的记录。 1、记录 | Docker的windows版安装…

【故障诊断】量子粒子群优化极限学习机实现乳腺癌诊断,(QPSO-ELM)数据分类

1.简介 本文采用量子粒子群优化极限学习机实现乳腺癌诊断&#xff0c;极限学习机&#xff08;ELM&#xff09;用来训练单隐藏层前馈神经网络&#xff08;SLFN&#xff09;与传统的SLFN训练算法不同&#xff0c;极限学习机随机选取输入层权重和隐藏层偏置&#xff0c;输出层权重…

机器学习优化算法:从梯度下降到Adam及其变种

机器学习优化算法&#xff1a;从梯度下降到Adam及其变种 引言 最近deepseek的爆火已然说明&#xff0c;在机器学习领域&#xff0c;优化算法是模型训练的核心驱动力。无论是简单的线性回归还是复杂的深度神经网络&#xff0c;优化算法的选择直接影响模型的收敛速度、泛化性能…

基于PostgreSQL的自然语义解析电子病历编程实践与探索(上)

一、引言 1.1研究目标与内容 本研究旨在构建一个基于 PostgreSQL 的自然语义解析电子病历编程体系,实现从电子病历文本中提取结构化信息,并将其存储于 PostgreSQL 数据库中,以支持高效的查询和分析。具体研究内容包括: 电子病历的预处理与自然语言处理:对电子病历文本进…

【Conda 和 虚拟环境详细指南】

Conda 和 虚拟环境的详细指南 什么是 Conda&#xff1f; Conda 是一个开源的包管理和环境管理系统&#xff0c;支持多种编程语言&#xff08;如Python、R等&#xff09;&#xff0c;最初由Continuum Analytics开发。 主要功能&#xff1a; 包管理&#xff1a;安装、更新、删…

【Linux】线程互斥与同步

&#x1f525; 个人主页&#xff1a;大耳朵土土垚 &#x1f525; 所属专栏&#xff1a;Linux系统编程 这里将会不定期更新有关Linux的内容&#xff0c;欢迎大家点赞&#xff0c;收藏&#xff0c;评论&#x1f973;&#x1f973;&#x1f389;&#x1f389;&#x1f389; 文章目…