当前位置: 首页 > news >正文

【数据结构】排序--快速排序

目录

一 概念

二 快速排序的实现

1. hoare版本

(1)代码实现

(2)单趟排序图解

(3) 递归实现图解

(4)细节控制

(5)时间复杂度

(6)三数取中优化

2 挖坑法

(1)代码实现

(2)单趟图解 

3 前后指针法 

(1) 代码实现 

(2) 单趟图解

​4 优化子区间 

5 非递归快速排序

​ 三 快速排序的特性总结


一 概念

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中 的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右 子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止

二 快速排序的实现

上述为快速排序递归实现的主框架,发现与二叉树前序遍历规则非常像

1. hoare版本

(1)代码实现

#include<stdio.h>
void Swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}int PartSort1(int* a, int left, int right)
{int keyi = left;while (left < right){//找小while (left < right && a[right] >= a[keyi]){right--;}//找大while (left < right && a[left] <= a[keyi]){left++;}Swap(&a[left], &a[right]);}Swap(&a[keyi], &a[left]);return left;
}void QuickSort(int* a, int begin, int end)
{if (begin >= end){return;}int key = PartSort1(a, begin, end);QuickSort(a, begin, key - 1);QuickSort(a, key + 1, end);
}
int main()
{int arr[] = {6,1,2,7,9,3,4,5,10,8};QuickSort(arr, 0, (sizeof(arr) / sizeof(int)) - 1);for (int i = 0; i < sizeof(arr) / sizeof(int); i++){printf("%d ", arr[i]);}
}

(2)单趟排序图解

我们看看单趟排序怎么排的

 

(3) 递归实现图解

再来看看递归怎么实现的

 

(4)细节控制

对细节控制上 我要做一下解释

 那这里相遇位置一定比a[keyi]小呢?  右边先走导致的

(5)时间复杂度

我们来算一下快速排序的时间复杂度(需要对二叉树的基本性质熟悉)

 

(6)三数取中优化

那针对有序 的情况 我们可以采取三数取中的方式解决

#include<stdio.h>
void Swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}int GetMidi(int* a, int left, int right)
{int mid = (left + right) / 2;if (a[left] < a[mid]){if (a[mid] < a[right]){return mid;}else if (a[left] > a[right]){return left;}else{return right;}}else// a[left] > a[mid]{if (a[left] < a[right]){return left;}else if (a[mid] > a[right]){return mid;}else{return right;}}
}int PartSort1(int* a, int left, int right)
{int midi = GetMidi(a, left, right);Swap(&a[midi], &a[left]);int keyi = left;while (left < right){//找小while (left < right && a[right] >= a[keyi]){right--;}//找大while (left < right && a[left] <= a[keyi]){left++;}Swap(&a[left], &a[right]);}Swap(&a[keyi], &a[left]);return left;
}void QuickSort(int* a, int begin, int end)
{if (begin >= end){return;}int key = PartSort1(a, begin, end);QuickSort(a, begin, key - 1);QuickSort(a, key + 1, end);
}
int main()
{int arr[] = {6,1,2,7,9,3,4,5,10,8};QuickSort(arr, 0, (sizeof(arr) / sizeof(int)) - 1);for (int i = 0; i < sizeof(arr) / sizeof(int); i++){printf("%d ", arr[i]);}
}

 2 挖坑法

  (1)代码实现

#include<stdio.h>
void Swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}int GetMidi(int* a, int left, int right)
{int mid = (left + right) / 2;if (a[left] < a[mid]){if (a[mid] < a[right]){return mid;}else if (a[left] > a[right]){return left;}else{return right;}}else// a[left] > a[mid]{if (a[left] < a[right]){return left;}else if (a[mid] > a[right]){return mid;}else{return right;}}
}int PartSort2(int* a, int left, int right)
{int midi = GetMidi(a, left, right);Swap(&a[left], &a[midi]);int key = a[left];//保存key值以后 左边形成第一个坑位int hole = left;while (left < right){//右边先走,找小,填到左边的坑,右边形成新的坑位if (left < right && a[right] >= key){right--;}a[hole] = a[right];hole = right;//左边再走,找大,填到右边的坑,左边形成新的坑位if (left < right && a[left] <= key){left++;}a[hole] = a[left];hole = left;}a[hole] = key;return hole;
}void QuickSort(int* a, int begin, int end)
{if (begin >= end){return;}int key = PartSort2(a, begin, end);QuickSort(a, begin, key - 1);QuickSort(a, key + 1, end);
}
int main()
{int arr[] = {6,1,2,7,9,3,4,5,10,8};QuickSort(arr, 0, (sizeof(arr) / sizeof(int)) - 1);for (int i = 0; i < sizeof(arr) / sizeof(int); i++){printf("%d ", arr[i]);}
}

(2)单趟图解 

3 前后指针法 

(1) 代码实现 

int PartSort3(int* a, int left, int right)
{int keyi = left;int prev = left;int cur = prev + 1;while (cur <= right){if (a[cur] < a[keyi] && ++prev != cur){Swap(&a[cur], &a[prev]);}cur++;}Swap(&a[keyi], &a[prev]);return prev;
}
void QuickSort(int* a, int begin, int end)
{if (begin >= end){return;}int key = PartSort3(a, begin, end);QuickSort(a, begin, key - 1);QuickSort(a, key + 1, end);
}
int main()
{int arr[] = {6,1,2,7,9,3,4,5,10,8};QuickSort(arr, 0, (sizeof(arr) / sizeof(int)) - 1);for (int i = 0; i < sizeof(arr) / sizeof(int); i++){printf("%d ", arr[i]);}
}

(2) 单趟图解

4 优化子区间 

递归到小的子区间时, 不在递归分割排序,可以考虑使用插入排序

因为区间比较小的时候节点数开的很多  特别是最后一层 节点数占了整个数大致百分之五十

 

int PartSort(int* a, int left, int right)
{int midi = GetMidi(a, left, right);Swap(&a[left], &a[midi]);int keyi = left;int prev = left;int cur = prev + 1;while (cur <= right){if (a[cur] < a[keyi] && ++prev != cur){Swap(&a[cur], &a[prev]);}cur++;}Swap(&a[prev], &a[keyi]);return prev;
}void QuickSort(int* a, int begin, int end)
{if (begin >= end){return;}if ((end - begin + 1) > 10){int keyi = PartSort(a, begin, end);QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);}else{//插入排序InsertSort(a + begin, end - begin + 1);}
}

5 非递归快速排序

需要有对栈的基础  不会的可以看前面的博客

#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
#include<assert.h>
typedef struct STList
{int* a;int size;int capacity;
}ST;void STInit(ST* ps)
{assert(ps);ps->a = NULL;ps->size = ps->capacity = 0;
}void STDestroy(ST* ps)
{assert(ps);free(ps->a);ps->a = NULL;ps->size = ps->capacity = 0;
}
void STPush(ST* ps, int x)
{assert(ps);if (ps->size == ps->capacity){int newcapacity = (ps->capacity == 0 ? 4 : ps->capacity * 2);int* tmp = (int*)realloc(ps->a, sizeof(int) * newcapacity);if (tmp == NULL){perror("realloc fail");exit(-1);}ps->a = tmp;ps->capacity = newcapacity;}ps->a[ps->size] = x;ps->size++;
}void STPop(ST* ps)
{assert(ps);assert(ps->size > 0);ps->size--;
}bool STEmpty(ST* ps)
{assert(ps);return ps->size == 0;
}int STTop(ST* ps)
{assert(ps);assert(ps->size > 0);return ps->a[ps->size - 1];
}
void Swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}int GetMidi(int* a, int left, int right)
{int mid = (left + right) / 2;if (a[left] < a[mid]){if (a[mid] < a[right]){return mid;}else if (a[left] > a[right]){return left;}else{return right;}}else// a[left] > a[mid]{if (a[left] < a[right]){return left;}else if (a[mid] > a[right]){return mid;}else{return right;}}
}int PartSort(int* a, int left, int right)
{int midi = GetMidi(a, left, right);Swap(&a[left], &a[midi]);int keyi = left;int prev = left;int cur = prev + 1;while (cur <= right){if (a[cur] < a[keyi] && ++prev != cur){Swap(&a[cur], &a[prev]);}cur++;}Swap(&a[prev], &a[keyi]);return prev;
}void QuickSortNonR(int* a, int begin, int end)
{ST st;//创建一个栈 STInit(&st);//初始化STPush(&st, end);STPush(&st, begin);while (!STEmpty(&st)){int left = STTop(&st);STPop(&st);int right = STTop(&st);STPop(&st);int keyi = PartSort(a, left, right);// [lefy,keyi-1] keyi [keyi+1, right]if (keyi + 1 < right){STPush(&st, right);STPush(&st, keyi + 1);}if (left < keyi - 1){STPush(&st, keyi - 1);STPush(&st, left);}}STDestroy(&st);
}int main()
{int arr[] = {6,1,2,7,9,3,4,5,10,8};QuickSortNonR(arr, 0, (sizeof(arr) / sizeof(int)) - 1);for (int i = 0; i < sizeof(arr) / sizeof(int); i++){printf("%d ", arr[i]);}
}

 三 快速排序的特性总结

1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序

2. 时间复杂度:O(N*logN)

3. 空间复杂度:O(logN)

4. 稳定性:不稳定

本节难度还是不低, 但是我觉得大家根据图解和代码慢慢吃透还是不难的,这节我画的图解很多, 对重点和难点进行了很细致的划分和讲解, 希望大家可以窥探到快速排序的妙处

继续加油!

相关文章:

【数据结构】排序--快速排序

目录 一 概念 二 快速排序的实现 1. hoare版本 (1)代码实现 (2)单趟排序图解 (3) 递归实现图解 (4)细节控制 (5)时间复杂度 (6)三数取中优化 2 挖坑法 (1)代码实现 (2)单趟图解 3 前后指针法 (1) 代码实现 (2) 单趟图解 ​4 优化子区间 5 非递归快速排序 …...

【试题040】多个逻辑或例题2

1.题目&#xff1a;设int n0;&#xff0c;执行表达式n ||(n-1) ||(n0)||(n1)||(n2)后n的值是 &#xff1f; 2.代码解析&#xff1a; 逻辑或 || 运算符是一个短路运算符&#xff0c;它从左到右依次计算表达式&#xff0c;如果遇到一个为真&#xff08;非零&#xff09;的值&am…...

自然语言处理---Self Attention自注意力机制

Self-attention介绍 Self-attention是一种特殊的attention&#xff0c;是应用在transformer中最重要的结构之一。attention机制&#xff0c;它能够帮助找到子序列和全局的attention的关系&#xff0c;也就是找到权重值wi。Self-attention相对于attention的变化&#xff0c;其实…...

推荐收藏系列!2万字图解Hadoop

今天我用图解的方式讲解pandas的用法&#xff0c;内容较长建议收藏&#xff0c;梳理不易&#xff0c;点赞支持。 学习 Python 编程&#xff0c;给我的经验就是&#xff1a;技术要学会分享、交流&#xff0c;不建议闭门造车。一个人可能走的很快、但一堆人可以走的更远。如果你…...

Python高级篇(08):生成器

一、生成器定义和作用 定义&#xff1a;Python中&#xff0c;一边循环一边计算的机制&#xff0c;生成器对象也是迭代器对象&#xff0c;支持for循环、next()方法…等。作用&#xff1a;循环的过程中不断推算出后续的元素&#xff0c;这样就不必创建完整的list&#xff0c;从而…...

力扣100114. 元素和最小的山形三元组 II(中等)

题目描述&#xff1a; 给你一个下标从 0 开始的整数数组 nums 。 如果下标三元组 (i, j, k) 满足下述全部条件&#xff0c;则认为它是一个 山形三元组 &#xff1a; i < j < knums[i] < nums[j] 且 nums[k] < nums[j] 请你找出 nums 中 元素和最小 的山形三元组…...

LuatOS-SOC接口文档(air780E)--lcdseg - 段式lcd

常量 常量 类型 解释 lcdseg.BIAS_STATIC number 没偏置电压(bias) lcdseg.BIAS_ONEHALF number 1/2偏置电压(bias) lcdseg.BIAS_ONETHIRD number 1/3偏置电压(bias) lcdseg.BIAS_ONEFOURTH number 1/4偏置电压(bias) lcdseg.DUTY_STATIC number 100%占空比(d…...

实现图像处理和分析的关键技术

在计算机视觉中&#xff0c;我们可以利用摄像头捕捉到的图像来进行各种分析和处理。以下是一些常见的计算机视觉任务&#xff1a; 对象检测&#xff1a;识别图像中的特定对象并标注其位置。人脸识别&#xff1a;识别和验证人脸身份。姿态估计&#xff1a;估计人体的姿态和动作…...

【C++学习笔记】内联函数

1. 概念 以inline修饰的函数叫做内联函数&#xff0c;编译时C编译器会在调用内联函数的地方展开&#xff0c;没有函数调 用建立栈帧的开销&#xff0c;内联函数提升程序运行的效率。 如果在上述函数前增加inline关键字将其改成内联函数&#xff0c;在编译期间编译器会用函数…...

macOS Sonoma 14.1RC(23B73)发布

黑果魏叔10 月 18 日消息&#xff0c;苹果今日向 Mac 电脑用户推送了 macOS 14.1 RC更新&#xff08;内部版本号&#xff1a;23B73&#xff09;&#xff0c;本次更新距离上次发布隔了 7 天。 macOS Sonoma 14.1RC&#xff08;23B73&#xff09;的更新内容主要包括以下方面&…...

数据结构数组 Array 手写实现,扩容原理

数组数据结构 数组&#xff08;Array&#xff09;是一种线性表数据结构。它用一组连续的内存空间&#xff0c;来存储一组具有相同类型数据的集合。 数组的特点&#xff1a; 数组是相同数据类型的元素集合&#xff08;int 不能存放 double&#xff09;数组中各元素的存储是有先…...

工作中几个问题的思考

对于需要并行多公司并行处理的任务&#xff0c;方案是什么&#xff1f; 多线程、并行流、并发库&#xff08;ExecutorService、Futrue、Callable&#xff09;&#xff0c;分布式计算&#xff08;1&#xff09;按照公司ID分片 &#xff08;2&#xff09;按照业务类型分片 处理…...

Jmeter的性能测试

性能测试的概念 定义&#xff1a;软件的性能是软件的一种非功能特性&#xff0c;它关注的不是软件是否能够完成特定的功能&#xff0c;而是在完成该功能时展示出来的及时性。 由定义可知性能关注的是软件的非功能特性&#xff0c;所以一般来说性能测试介入的时机是在功能测试…...

IntelliJ IDEA 2020.2.1白票安装使用方法

先安装好idear Plugins 内手动添加第三方插件仓库地址&#xff1a;https://plugins.zhile.io 搜索&#xff1a;IDE Eval Reset插件进行安装 输入https://plugins.zhile.io 手动安装离线插件方法 安装包可以去笔者的CSDN资源库下载 安装mybaties插件...

【UCAS自然语言处理作业一】利用BeautifulSoup爬取中英文数据,计算熵,验证齐夫定律

文章目录 前言中文数据爬取爬取界面爬取代码 数据清洗数据分析实验结果 英文数据爬取爬取界面动态爬取 数据清洗数据分析实验结果 结论 前言 本文分别针对中文&#xff0c;英文语料进行爬虫&#xff0c;并在两种语言上计算其对应的熵&#xff0c;验证齐夫定律github: ShiyuNee…...

微信小程序之个人中心授权登录

&#x1f3ac; 艳艳耶✌️&#xff1a;个人主页 &#x1f525; 个人专栏 &#xff1a;《Spring与Mybatis集成整合》《Vue.js使用》 ⛺️ 越努力 &#xff0c;越幸运。 1.了解微信授权登录 微信登录官网&#xff1a; 小程序登录https://developers.weixin.qq.com/miniprogram/d…...

Elasticsearch的聚集统计,可以进行各种统计分析

说明&#xff1a; Elasticsearch不仅是一个大数据搜索引擎&#xff0c;也是一个大数据分析引擎。它的聚集(aggregation)统计的REST端点可用于实现与统计分析有关的功能。Elasticsearch提供的聚集分为三大类。 度量聚集(Metric aggregation)&#xff1a;度量聚集可以用于计算搜…...

Webpack 理解 input output 概念

一、介绍 如果还没用过 Webpack 请先阅读 Webpack & 基础入门 再回头看本文。 Webpack 的核心只做两件事&#xff0c;输入管理&#xff08;Input Management&#xff09;和输出管理&#xff08;Output Management&#xff09;&#xff0c;什么花里胡哨的插件和配置都离不…...

【字符函数】

✨博客主页&#xff1a;小钱编程成长记 &#x1f388;博客专栏&#xff1a;进阶C语言 &#x1f388;相关博文&#xff1a;字符串函数&#xff08;一&#xff09;、字符串函数&#xff08;二&#xff09; 字符函数 字符函数1.字符分类函数1.1 iscntrl - 判断是否是控制字符1.2 i…...

git创建与合并分支

文章目录 创建与合并分支分支管理的概念实际操作 解决冲突分支管理策略Bug分支Feature分支多人协作 创建与合并分支 分支管理的概念 分支在实际中有什么用呢&#xff1f;假设你准备开发一个新功能&#xff0c;但是需要两周才能完成&#xff0c;第一周你写了50%的代码&#xf…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...

短视频矩阵系统文案创作功能开发实践,定制化开发

在短视频行业迅猛发展的当下&#xff0c;企业和个人创作者为了扩大影响力、提升传播效果&#xff0c;纷纷采用短视频矩阵运营策略&#xff0c;同时管理多个平台、多个账号的内容发布。然而&#xff0c;频繁的文案创作需求让运营者疲于应对&#xff0c;如何高效产出高质量文案成…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

20个超级好用的 CSS 动画库

分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码&#xff0c;而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库&#xff0c;可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画&#xff0c;可以包含在你的网页或应用项目中。 3.An…...

使用LangGraph和LangSmith构建多智能体人工智能系统

现在&#xff0c;通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战&#xff0c;比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...

MySQL 部分重点知识篇

一、数据库对象 1. 主键 定义 &#xff1a;主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 &#xff1a;确保数据的完整性&#xff0c;便于数据的查询和管理。 示例 &#xff1a;在学生信息表中&#xff0c;学号可以作为主键&#xff…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)

题目 做法 启动靶机&#xff0c;点进去 点进去 查看URL&#xff0c;有 ?fileflag.php说明存在文件包含&#xff0c;原理是php://filter 协议 当它与包含函数结合时&#xff0c;php://filter流会被当作php文件执行。 用php://filter加编码&#xff0c;能让PHP把文件内容…...