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

【排序篇】实现快速排序的三种方法

🌈个人主页:Yui_
🌈Linux专栏:Linux
🌈C语言笔记专栏:C语言笔记
🌈数据结构专栏:数据结构

文章目录

  • 1 交换排序
    • 1.1 冒泡排序
    • 1.2 快速排序
      • 1.2.1 hoare版本
      • 1.2.2 挖坑法
      • 1.2.3 前后指针版本

1 交换排序

基本思想:所谓交换,就是根据序列中的两个记录键位的比较结果来交换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

1.1 冒泡排序

冒泡排序

冒泡排序的特点就是,从前到后两两比较找到最大的数放在数组的末尾。因为已经找到数组最大元素并放置在末尾,也就是说最大元素已经放置在最终的位置,我们接下来就是把末尾提前来来一次找到数组中次大的元素,以此类推将数组彻底排序。

#include <stdio.h>
void swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}
void bubblesort(int* a, int n)
{for (int i = 0; i < n - 1; ++i){int flag = 1;for (int j = 0; j < n - i - 1; ++j){if (a[j] > a[j + 1]){swap(&a[j], &a[j + 1]);flag = 0;}}if (flag)break;}
}
int main()
{int arr[10] = { 0,9,8,7,6,5,4,3,2,1 };//slectsort(arr, 10);bubblesort(arr, 10);for (int i = 0; i < 10; ++i){printf("%d ", arr[i]);}return 0;
}
//打印结果
/*
0 1 2 3 4 5 6 7 8 9
*/

冒泡排序总结:

  1. 冒泡排序是一种非常容易理解的排序
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1)
  4. 稳定性:稳定

1.2 快速排序

快速排序是Hoare在1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序的元素序列中的某元素作为基准,按照该排序码将待排序序集分割成两子序列,左子序列中所有元素均小于其基准值,右子序列中的所有元素均大于基准值,然后左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
下面会给出快速排序递归实现的主框架,发现于二叉树前序遍历的逻辑非常像,大家在写递归框架时可以想想二叉树前序遍历的过程快速写成来。后续只需要分析如何对区间中的数据进行划分就可以了。

//假设按照升序对arr数组中的[left,right]区间中的元素进行排序
void quicksort(int* a, int left,int right)
{if (left >= right)return;//按照基准值对arr数组的[left,right]区间中的元素进行划分int mid = PartSort1(a, left, right);//划分成功后以mid为边界形成左右两部分[left,mid-1][mid+1,right]//递归排[left,mid-1]quicksort(a, left, mid - 1);//递归排[mid+1,right]quicksort(a, mid + 1, right);
}

将区间按照基准值划分为左右部分的常见方式:

1.2.1 hoare版本

hoare版本

通过动图我们可以发现,hoare版本的做法就是先确立一个基准值key的坐标,然后定义两个指针一左一右,左指针找大于a[key]的,右指针找小于a[key]的,找到后交换左右的数据,一直进行到左右指针相遇,相遇后就把a[key]和左右指针相遇的位置的数据交换。如此一来,就做到了把a[key]放到数组的最终位置,因为此时a[key]的左边都是小于它的数,右边都是大于它的数,不就是a[key]的最终位置嘛。
提问:为什么最终左右指针相遇时的数据一定小于a[key]?
回答:这就关系到左右指针谁先走的问题。当我排升序的时候一定要让右指针先走,右指针是找小嘛。在它们相遇前会存在两种情况:

  1. 左指针去与右指针相遇,因为右指针是先走的,同时右指针又是找小于a[key]的值,所以如果是左指针去与右指针相遇,此时的右指针移动指向了一个小于a[key]的值。
  2. 右指针去与左指针相遇,因为右指针先走,左指针就会有两种情况:还没走,指向a[key],走过了,走过了然后于右指针交换数据,导致指向小于a[key]的数据。当右指针与左指针相遇时的数据还是小于a[key]的数据
    如此一来就说明了最终左右指针相遇时的数据一定小于a[key]
//hoare版本
int PartSort1(int* a, int left, int right)
{int key = left;while (left < right){while (right > left && a[right] >= a[key])right -= 1;while (right > left && a[left] <= a[key])left += 1;swap(&a[left], &a[right]);}swap(&a[left], &a[key]);return left;
}

优化
这种情况有个致命的缺陷,当数组接近有序时效率就会退化为O(N^2)。如图所示:
缺陷

为了解决这个问题,我们在选择基准值的时候可以不选择数组的第一个数字,而是选择数组中大小适中的元素。我们把这个方法叫做三数取中法。
修改后的版本

//三数取中
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 right;elsereturn left;}}else//left<=mid{if (a[mid] < a[right])return mid;else//mid>right{if (a[left] > a[right])//mid>left>rightreturn left;elsereturn right;}}
}//hoare版本
int PartSort1(int* a, int left, int right)
{int mid = GetMidi(a, left, right);swap(&a[mid], &a[left]);int key = left;while (left < right){while (right > left && a[right] >= a[key])right -= 1;while (right > left && a[left] <= a[key])left += 1;swap(&a[left], &a[right]);}swap(&a[left], &a[key]);return left;
}

1.2.2 挖坑法

挖坑法

解释:先利用key保存基准值,初始让left为坑位,然后开始左右指针的遍历,让右指针先走去找小于key的值,找到后将值填入坑位,然后更新坑位为right,同理左指针是找大,找到大于key的值后,将值填入坑位,然后再更新坑位位left,直到相遇。循环结束后将基准值填入左右指针相遇位置。

//挖坑法
int PartSort2(int* a, int left, int right)
{int mid = GetMidi(a, left, right);swap(&a[mid], &a[left]);int hole = left;int key = a[left];while (left < right){while (left < right && a[right] >= key)right -= 1;a[hole] = a[right];hole = right;while (left < right && a[left] <= key)left += 1;a[hole] = a[left];hole = left;}a[left] = key;return left;
}

1.2.3 前后指针版本

前后指针法

创建两个指针,一前一后,正常情况我们一定cur指针去遍历数组每当我们指向的数据小于a[key]时,就让prev+=1,然后交换a[prev]和a[cur]的值。

//前后指针法
int PartSort3(int* a, int left, int right)
{int mid = GetMidi(a, left, right);swap(&a[left], &a[mid]);int key = left;int prev = left;int cur = prev + 1;while (cur<=right){if (a[cur] < a[key]){prev += 1;swap(&a[prev], &a[cur]);}cur += 1;}swap(&a[prev], &a[key]);return prev;
}

相关文章:

【排序篇】实现快速排序的三种方法

&#x1f308;个人主页&#xff1a;Yui_ &#x1f308;Linux专栏&#xff1a;Linux &#x1f308;C语言笔记专栏&#xff1a;C语言笔记 &#x1f308;数据结构专栏&#xff1a;数据结构 文章目录 1 交换排序1.1 冒泡排序1.2 快速排序1.2.1 hoare版本1.2.2 挖坑法1.2.3 前后指针…...

Java 标识符(详解)

文章目录 一、简介二、命名规则三、命名规范 一、简介 在 Java 中&#xff0c;用于给变量、类、方法等命名的符号组合&#xff0c;我们称之为Java标识符&#xff0c;它就像是给这些编程元素贴上的独特标签&#xff0c;以便在程序中能够准确地引用和操作它们。 二、命名规则 标…...

2024年,有哪些优质的计算机书籍推荐?

在2024年&#xff0c;计算机领域的新书层出不穷&#xff0c;涵盖了从基础理论到前沿技术的多个方面。以下是今年出版的几本备受关注的计算机新书。 1. AI与机器学习类 1、深度学习详解 1.李宏毅老师亲笔推荐&#xff0c;杨小康、周明、叶杰平、邱锡鹏鼎力推荐! 2.数百万次播…...

Python基础知识点--总结

1. 注释 注释用于提高代码的可读性&#xff0c;在代码中添加说明文字&#xff0c;使代码更容易理解。 单行注释&#xff1a;使用 # 符号开头&#xff0c;注释内容在符号之后的行内。多行注释&#xff1a;使用三引号&#xff08; 或 """&#xff09;包裹注释内…...

高效记录与笔记整理的策略:工具选择、结构设计与复习方法

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…...

Request重复读的问题

换了新工作都有时间写文章&#xff0c;每天也是加班到很晚&#xff0c;也不是工作内容多&#xff0c;主要是还是效率低&#xff0c;要考虑多干的很心累。 一、关于request重复读的问题&#xff0c;从源码的角度来分析 为什么他不能重复读 跳转 再看源码前可能需要一些基础的…...

Linux学习第60天:Linux驱动开发的一些总结

今天是Linux驱动开发的最后一个章节&#xff0c;题目中标明是60天完成的&#xff0c;其实在实际学习及笔记的整理中不止是60天。中间有过断更&#xff0c;有时断更的时间还是挺长的。这是在整个Linux驱动开发学习中最不满意的地方。 题目为Linux学习&#xff0c;其实这个题目有…...

OPP || 继承和抽象类 || 访问控制

OPP面向对象程序设计 数据抽象&#xff1a;类的接口声明和定义实现分离继承&#xff1a;类构成的&#xff08;树型&#xff09;层次关系动态绑定&#xff1a;忽略相似类型区别&#xff0c;用统一的方式使用 基类派生类&#xff1a; 继承&#xff1a;类名 冒号 访问说明符 …...

蓝牙音视频远程控制协议(AVRCP) command跟response介绍

零.声明 本专栏文章我们会以连载的方式持续更新&#xff0c;本专栏计划更新内容如下&#xff1a; 第一篇:蓝牙综合介绍 &#xff0c;主要介绍蓝牙的一些概念&#xff0c;产生背景&#xff0c;发展轨迹&#xff0c;市面蓝牙介绍&#xff0c;以及蓝牙开发板介绍。 第二篇:Trans…...

MySQL的InnoDB存储引擎中的Buffer Pool机制

目录 Buffer Pool 简介 定义 为什么需要Buffer Pool 图解重点知识 Buffer Pool 的组成 数据页&#xff08;Data Pages&#xff09; 索引页&#xff08;Index Pages&#xff09; 插入缓冲页&#xff08;Insert Buffer Pages&#xff09; undo页&#xff08;Undo Pages&a…...

5. MongoDB 文档插入、更新、删除、查询

1. 插入文档 文档的数据结构和JSON基本一样。所有存储在集合中的数据都是BSON格式。 BSON是一种类似JSON的二进制形式的存储格式&#xff0c;是Binary JSON的简称。常用的插入文档方法包括&#xff1a; db.collection.insertOne()&#xff1a;插入单个文档db.collection.inse…...

⌈ 传知代码 ⌋ DETR[端到端目标检测]

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…...

Oracle之触发器

简介 触发器在数据库里以独立的对象存储&#xff0c;他与存储过程不同的是&#xff0c;存储过程通过其他程序来启动运行或直接启动运行而触发器是由一个事件来启动运行&#xff0c;即触发器是当某个事件发生时自动式运行。并企&#xff0c;触发器不能接收参数。所以运行触发器…...

从零搭建微前端架构:解耦大型项目的终极方案

随着前端应用的复杂度不断提升,单体前端应用(Monolithic Frontend)的维护和扩展难度也日益增加。微前端(Micro-Frontend)作为一种新兴架构理念,旨在将大型前端项目拆分为多个独立、可独立部署的微应用,从而提升项目的可维护性和灵活性。这篇文章将带你从零开始搭建一个微…...

24/8/17算法笔记 MPC算法

MPC算法&#xff0c;在行动前推演一下 MPC&#xff08;Model Predictive Control&#xff0c;模型预测控制&#xff09;是一种先进的控制策略&#xff0c;它利用未来预测模型来优化当前的控制动作。MPC的核心思想是&#xff0c;在每一个控制步骤中&#xff0c;都基于当前系统状…...

GROUP_CONCAT 用法详解(Mysql)

GROUP_CONCAT GROUP_CONCAT 是 MySQL 中的一个聚合函数&#xff0c;用于将分组后的多行数据连接成一个单一的字符串。 通常用于将某个列的多个值合并到一个字符串中&#xff0c;以便更方便地显示或处理数据。 GROUP_CONCAT([DISTINCT] column_name[ORDER BY column_name [ASC…...

Golang httputil 包深度解析:HTTP请求与响应的操控艺术

标题&#xff1a;Golang httputil 包深度解析&#xff1a;HTTP请求与响应的操控艺术 引言 在Go语言的丰富标准库中&#xff0c;net/http/httputil包是一个强大的工具集&#xff0c;它提供了操作HTTP请求和响应的高级功能。从创建自定义的HTTP代理到调试HTTP流量&#xff0c;h…...

SQLALchemy 分页

SQLALchemy 分页 1. 使用SQLAlchemy的`slice`和`offset`/`limit`SQLAlchemy 1.4及更新版本SQLAlchemy 1.3及更早版本使用第三方库注意事项在Web开发中,分页是处理大量数据时一个非常重要的功能。SQLAlchemy是一个流行的Python SQL工具包和对象关系映射(ORM)库,它允许开发者…...

快速上手体验MyPerf4J监控springboot应用(docker版快速开始-本地版)

使用MyPerf4J监控springboot应用 快速启动influxdb时序数据库日志收集器telegrafgrafana可视化界面安装最终效果 项目地址 项目简介: 一个针对高并发、低延迟应用设计的高性能 Java 性能监控和统计工具。 价值 快速定位性能瓶颈快速定位故障原因 快速启动 监控本地应用 idea配…...

C语言 之 strlen、strcpy、strcat、strcmp字符串函数的使用和模拟实现

文章目录 strlen的使用和模拟实现函数的原型strlen模拟实现&#xff1a;方法1方法2方法3 strcpy的使用和模拟实现函数的原型strcpy的模拟实现&#xff1a; strcat的使用和模拟实现函数的原型strcat的模拟实现&#xff1a; strcmp的使用和模拟实现函数的原型strcmp的模拟实现 本…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

linux arm系统烧录

1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 &#xff08;忘了有没有这步了 估计有&#xff09; 刷机程序 和 镜像 就不提供了。要刷的时…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

oracle与MySQL数据库之间数据同步的技术要点

Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异&#xff0c;它们的数据同步要求既要保持数据的准确性和一致性&#xff0c;又要处理好性能问题。以下是一些主要的技术要点&#xff1a; 数据结构差异 数据类型差异&#xff…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

Springboot社区养老保险系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;社区养老保险系统小程序被用户普遍使用&#xff0c;为方…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】&#xff0c;分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...

JavaScript 数据类型详解

JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型&#xff08;Primitive&#xff09; 和 对象类型&#xff08;Object&#xff09; 两大类&#xff0c;共 8 种&#xff08;ES11&#xff09;&#xff1a; 一、原始类型&#xff08;7种&#xff09; 1. undefined 定…...

Golang——6、指针和结构体

指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...