【数据结构--八大排序】之快速排序

news2024/10/7 14:32:57

在这里插入图片描述

💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤
📃个人主页 :阿然成长日记 👈点击可跳转
📆 个人专栏: 🔹数据结构与算法🔹C语言进阶
🚩 不能则学,不知则问,耻于问人,决无长进
🍭 🍯 🍎 🍏 🍊 🍋 🍒 🍇 🍉 🍓 🍑 🍈 🍌 🍐 🍍

文章目录

  • 一、快速排序的单趟排序
    • 方法一:霍尔法
      • 1.基本思路:
      • 2.原理图:
      • 3.动图:
      • 4.代码实现:
    • 方法二:挖坑法
      • 1.基本思路:
      • 2.原理图:
      • 3.动图:
      • 4.代码实现:
    • 方法三:前后指针法
      • 1.基本思路:
      • 2.动图
      • 3.代码实现:
  • 二、快速排序
    • 1.原理
    • 2.递归法:
  • 三、快速排序的优化
    • 1.优化方式:
    • 2.优化的使用方法:
  • 四、快速排序的完整实现(霍尔法):
  • 五、 时间复杂度

在这里插入图片描述
前言:

前面,我花费了大量时间学习排序算法,八大排序基本结束,本篇将开始快速排序的讲解。本篇文章适合刚开始学习快速排序的同学,总结的很全面,整理的很清楚,希望能帮到你,加油!

一、快速排序的单趟排序

快速排序的单趟排序:是以一个数作为基准值,实现将数组中比基准数小的数放在基准值的左侧,比基准值大的数放在基准值的右侧。

方法一:霍尔法

霍尔法的由来:霍尔是一个人的名字,他是最初发现快速排序的人,所以,它使用的单趟排序算法被称为霍尔法。

1.基本思路:

​用key标记基准值的下标(数组下标0的元素),使用两个指针leftright分别指向待排数组的最左侧和最右侧,right指针找比key基准值小的数,left指针找比key基准值大的数,找到后将两个数交换位置,同时实现大数右移和小数左移,直到left与right相遇则排序完成,最后将key基准值的下标返回,就完成了单趟排序。

2.原理图:

第一步:以第一个数作为基准值key
在这里插入图片描述

第二步:right从右边开始先找小于key的值,找到并停下来。
在这里插入图片描述

第三步:left从左边开始找大于key的值,找到并停下来。

在这里插入图片描述
第四步:交换两个值。Swap(&a[left], &a[right]);

在这里插入图片描述

第五步:重复第二步,找小于key的值,找到并停下来。
在这里插入图片描述
第六步:第三步:left从左边开始找大于key的值,找到并停下来。
此时leftright相遇,则退出循环,并交换key和left的值。

在这里插入图片描述

以上就是一次完整的快速排序的单趟排序。

3.动图:

在这里插入图片描述

4.代码实现:

//霍尔法

int Pritition1(int* a, int left, int right)
{
	//使用key保存基准值的下标
	int key = left;
	while (left < right)
	{
		//先从右边开始向左找小于a[key]的值下标。
		while (left < right && a[key] < a[right])
			right--;//没找到就一直向左寻找
		//再从左边开始向右找大于a[key]的值下标。
		while (left<right && a[key]>a[left])
			left++;//没找到就一直向右寻找
		//交换两个值
		Swap(&a[left], &a[right]);
	}
    //当left和right相遇时,将a[left]赋值给a[key]
	//该操作是为了下一轮的排序
	Swap(&a[left], &a[key]);
	//left相当于分界点坐标
	return left;
}

方法二:挖坑法

1.基本思路:

挖坑法是将key基准值用变量单独保存,然后将key的位置空出来形成一个,left和right指针分别从左右两端向中心遍历此时left先指向这个,从右边先开始,right找比key小的数,找到后将该数直接放进坑里,并将自己空出来的位置设置为left找比key大的数,找到后将该数放进坑里,并将现在空出来的位置设置为坑,一直遍历,直到left与right相遇,相遇位置一定为坑(left和right必定有一个指向坑),此时将key基准值放进坑内,并返回基准值下标完成单趟排序。

2.原理图:

第一步:使用变量key保存基准值。
在这里插入图片描述
第二步:right从右边开始先找小于key的值,找到就停下来,将该位置的值放入坑内。

在这里插入图片描述

第三步:left从左边开始找大于key的值,找到并停下来,将该位置的值放入坑内。
在这里插入图片描述

第四步:重复第二步,找小于key的值,找到并停下来。将该位置的值放入坑内。
在这里插入图片描述
第五步:left从左边开始找大于key的值,找到并停下来,将该位置的值放入坑内。

在这里插入图片描述
注意:此时没有找到就leftright相遇,此时leftright相遇,则退出循环,并交换key放入left。
在这里插入图片描述

3.动图:

在这里插入图片描述

4.代码实现:

//挖坑法
int  Pritition2(int* a, int left,int right)
{
	//使用key保存基准值
	int key = a[left];
	//定义hole是坑;初始坑的位置下标是left
	int hole = left;
	while (left < right)
	{
		//从右向左找小于a[key]的值
		while (left < right && a[right] >= key)
			right--;//没找到就一直向左寻找
		a[left] = a[right];//将找到的值放入坑中
		hole = right;//并且将找到的位置置为新的坑
		while (left < right && a[left] <= key)
			left++;//没找到就一直向右寻找
		a[right] = a[left];//将找到的值放入坑中
		hole = left;//并且将找到的位置置为新的坑
	}
	a[hole] = key;//将基准值交换到hole位置
	//此时hole的位置就是分界点
	return hole;
}

方法三:前后指针法

1.基本思路:

(1) key保存数组第一个元素作为基准值,定义前指针prev指向第一个数,后指针cur指向前指针的后一个位置。

(2) cur挨个遍历数组中的数据,如果cur寻找比key基准值小的数,则prev后移一个位置,并且交换cur和prev所对应的元素值,cur和prev位置不变。

(3) 依次类推直到cur完全遍历完数组,停止。

prev之前的值一定小于key基准值,而prev与cur之间的一定大于基准值
最后将prev处与key位置的元素交换,将基准值下标返回(此时基准值下标已经交换到prev位置)。则完成单趟排序

2.动图

在这里插入图片描述

3.代码实现:

//前后指针法
int PartSort3(int* arr, int left, int right)
{
	int key = left;
	int prev = left;
	int cur = left + 1;

	while (cur <= right)
	{
		//arr[cur]小于基准值就交换
		//这里做了优化,使用前置++prev
		//如果prev+1等于cur则不用交换
		if (arr[cur] <= arr[key] && ++prev != cur)	
		{
			Swap(&arr[cur], &arr[prev]);
		}
		cur++;
	}
	//交换prev处元素到key位置
	Swap(&arr[key], &arr[prev]);
	//返回prev,相当于分界点
	return prev;
}

二、快速排序

1.原理

快速排序从整体上来看,是以一个选定的数为基准,将数组分为两个子序列,左子序列放比基准数小的,右子序列放比基准数大的数,然后再将子序列以以上方式同样分割,直到数组有序。
快速排序使用递归的方式调用单趟排序,每次调用后都以基准值为界,将数组分为2个子序列,继续排序。一直分到只有一个元素停止。

2.递归法:

void QuickSort(int* a, int left,int right)
{
	if (left >= right)
	{
		return;
	}
	
	int privot = Pritition1(a, left,right );
	QuickSort(a, left, privot - 1);
	QuickSort(a, privot + 1, right);
}

三、快速排序的优化

1.优化方式:

采取三数取中法leftright、和中间下标的值中选取一个折中值,基准值不可能为最大值或最小值,可以避免出现最差情况,从而提高快排的时间复杂度。

int GetMidIndex(int* arr, int left, int right)
{
    int mid = (left + right) / 2;
    
	if (arr[left] < arr[right])
	{
		if (arr[mid] < arr[left])
		{
			return left;
		}
		else if (arr[mid] <arr[right])
		{
			return mid;
		}
		else
		{
			return right;
		}
	}
	else
	{
		if (arr[mid] < arr[right])
		{
			return right;
		}
		else if (arr[mid] < arr[left])
		{
			return mid;
		}
		else
		{
			return left;
		}
	}
}

2.优化的使用方法:

在我们选择好基准值后,为了保证原来的单趟排序保持原有状态,我们选好的基准数与数组中第一个数交换位置,然后使用第一个数作为基准值排序
使用方法:

int PartSort(int* arr, int left, int right)
{
    //获取基准值,并与left交换位置
    int key = GetMidIndex(arr, left, right);
    //交换key和left对应的值,但是key指向不变
    Swap(&arr[key], &arr[left]);
    //将key指向数组开始位置
    key = left;
    

    //单趟排序算法
    ...
}

四、快速排序的完整实现(霍尔法):

//三数取中
int GetMidIndex(int* arr, int left, int right)
{
	int mid = (left + right) / 2;

	if (arr[left] < arr[right])
	{
		if (arr[mid] < arr[left])
		{
			return left;
		}
		else if (arr[mid] < arr[right])
		{
			return mid;
		}
		else
		{
			return right;
		}
	}
	else
	{
		if (arr[mid] < arr[right])
		{
			return right;
		}
		else if (arr[mid] < arr[left])
		{
			return mid;
		}
		else
		{
			return left;
		}
	}
}
//单趟排序
int Pritition1(int* a, int left, int right)
{
    //使用三数取中
    int key = GetMidIndex(arr, left, right);
    Swap(&arr[key], &arr[left]);
    key = left;
	while (left < right)
	{
		//先从右边开始向左找小于a[key]的值下标。
		while (left < right && a[key] < a[right])
			right--;//没找到就一直向左寻找
		//再从左边开始向右找大于a[key]的值下标。
		while (left<right && a[key]>a[left])
			left++;//没找到就一直向右寻找
		//交换两个值
		Swap(&a[left], &a[right]);
	}
    //当left和right相遇时,将a[left]赋值给a[key]
	//该操作是为了下一轮的排序
	Swap(&a[left], &a[key]);
	//left相当于分界点坐标
	return left;
}
//采用递归法
void QuickSort(int* a, int left,int right)
{
	if (left >= right)
	{
		return;
	}
	
	int privot = Pritition1(a, left,right );
	QuickSort(a, left, privot - 1);
	QuickSort(a, privot + 1, right);
}

五、 时间复杂度

在这里插入图片描述

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

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

相关文章

MySQL——使用mysqldump备份与恢复数据

目录 1.mysqldump简介 2.mysqldump备份数据 2.1 备份所有数据库 2.2 备份一个/多个数据库 2.3 备份指定库中的指定表 3.mysqldump恢复数据 3.1 恢复数据库 3.2 恢复数据表 1.mysqldump简介 mysqldump命令可以将数据库中指定或所有的库、表导出为SQL脚本。表的结构和表中…

图像压缩:Transformer-based Image Compression with Variable Image Quality Objectives

论文作者&#xff1a;Chia-Hao Kao,Yi-Hsin Chen,Cheng Chien,Wei-Chen Chiu,Wen-Hsiao Peng 作者单位&#xff1a;National Yang Ming Chiao Tung University 论文链接&#xff1a;http://arxiv.org/abs/2309.12717v1 内容简介&#xff1a; 1&#xff09;方向&#xff1a;…

代码随想录 Day11 二叉树 LeetCode T144,145,94 前中后序遍历 (递归解法)

题解及更详细解答来自于:代码随想录 (programmercarl.com) 前言: 递归三要素 确定递归函数的参数和返回值&#xff1a; 确定哪些参数是递归的过程中需要处理的&#xff0c;那么就在递归函数里加上这个参数&#xff0c; 并且还要明确每次递归的返回值是什么进而确定递归函数的返…

深度学习实战基础案例——卷积神经网络(CNN)基于MobileNetV3的肺炎识别|第3例

文章目录 前言一、数据集介绍二、前期工作三、数据集读取四、构建CA注意力模块五、构建模型六、开始训练 前言 Google公司继MobileNetV2之后&#xff0c;在2019年发表了它的改进版本MobileNetV3。而MobileNetV3共有两个版本&#xff0c;分别是MobileNetV3-Large和MobileNetV2-…

不容易解的题10.5

31.下一个排列 31. 下一个排列 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/next-permutation/?envTypelist&envIdZCa7r67M会做就不算难题&#xff0c;如果没做过不知道思路&#xff0c;这道题将会变得很难。 这道题相当于模拟cpp的next_permu…

Windows系统上使用CLion远程开发Linux程序

CLion远程开发Linux程序 情景说明Ubuntu配置CLion配置同步 情景说明 在Windows系统上使用CLion开发Linux程序&#xff0c;安装CLion集成化开发环境时会自动安装cmake、mingw&#xff0c;代码提示功能也比较友好。 但是在socket开发时&#xff0c;包含sys/socket.h头文件时&am…

浅谈CDN内容分发与全局负载均衡

CDN简介 CDN的全称是Content Delivery Network&#xff0c;即内容分发网络。CDN是构建在现有网络基础之上的智能虚拟网络&#xff0c;依靠部署在各地的边缘服务器&#xff0c;通过中心平台的负载均衡、内容分发、调度等功能模块&#xff0c;使用户就近获取所需内容&#xff0c…

软件设计师_数据结构与算法_学习笔记

文章目录 6.1 数组与矩阵6.1.1 数组6.1.2 稀疏矩阵 6.2 线性表6.2.1 数据结构的定义6.2.2 顺序表与链表6.2.2.1 定义6.2.2.2 链表的操作 6.2.3 顺序存储和链式存储的对比6.2.4 队列、循环队列、栈6.2.4.2 循环队列队空与队满条件6.2.4.3 出入后不可能出现的序列练习 6.2.5 串 6…

C语言学习系列->联合体and枚举

文章目录 前言联合体概述联合体的特点联合体大小的计算优点练习 枚举概述优点使用 前言 在上一篇文章中&#xff0c;小编将结构体的学习笔记整理出来了。现在&#xff0c;小编将枚举和联合体笔记分享给大家。枚举和联合体与结构体一样&#xff0c;都是自定义类型&#xff0c;在…

竞赛 机器视觉的试卷批改系统 - opencv python 视觉识别

文章目录 0 简介1 项目背景2 项目目的3 系统设计3.1 目标对象3.2 系统架构3.3 软件设计方案 4 图像预处理4.1 灰度二值化4.2 形态学处理4.3 算式提取4.4 倾斜校正4.5 字符分割 5 字符识别5.1 支持向量机原理5.2 基于SVM的字符识别5.3 SVM算法实现 6 算法测试7 系统实现8 最后 0…

MySQL之逻辑备份与恢复

逻辑备份简介&#xff1a; 备份的是建表、建库、插入等操作所执行SQL语句&#xff0c;适用于中小型数据库&#xff0c;效率相对较低。 本质&#xff1a;导出的是SQL语句文件 优点&#xff1a;不论是什么存储引擎&#xff0c;都可以用mysqldump备成SQL语句 缺点&#xff1a;速度…

输入一个大写字母,程序根据输入字符在字母表的顺序位置n,输出一个高度为n的金字塔图形

python字母金字塔根据输入的字母输出一个字母金字塔输入一个大写字母&#xff0c;程序根据输入字符在字母表的顺序位置n,输出一个高度为n的金字塔图形&#xff0c;比如输入E时&#xff0c;此时 字母金字塔 # A # ABA # ABCBA # ABCDCBA # ABCDEDCBA 看到…

php单独使用think-rom数据库 | thinkphp手动关闭数据库连接

背景&#xff08;think-orm2.0.61&#xff09; 由于需要长时间运行一个php脚本&#xff0c;而运行过程并不是需要一直与数据库交互&#xff0c;但thinkphp主要是为web站点开发的框架&#xff0c;而站点一般都是数据获取完则进程结束&#xff0c;所以thinkphp没提供手动关闭数据…

Trie树(字典树)C++详解

字典树的定义 字典树是一个用来快速查找和存储字符串集合的数据结构。 字典树的形状 假设我们字典树里有以下5个单词&#xff1a; akio&#xff0c;akno&#xff0c;cspj&#xff0c;csps&#xff0c;trie 那么字典树长这样&#xff1a; trie 的结构非常好懂&#xff0c;我们…

软考高级之系统架构师之设计模式

概述 设计模式是一种通用的设计方法&#xff0c;实际开发中可能不止23种。为方便理解和应用&#xff0c;一般分为3类&#xff1a; 创建型&#xff0c;通过采用抽象类所定义的接口&#xff0c;封装系统中对象如何创建、组合等信息。工厂方法模式、抽象工厂模式、单例模式、建造…

堆的初步认识

在学习本节文章前要先了解&#xff1a;大顶堆与小顶堆&#xff1a; &#xff08;优先级队列_加瓦不加班的博客-CSDN博客&#xff09; 堆实现 计算机科学中&#xff0c;堆是一种基于树的数据结构&#xff0c;通常用完全二叉树实现。 什么叫完全二叉树&#xff1f; 答&#x…

8.Vue_Element

1 Ajax 1.1 Ajax介绍 1.1.1 Ajax概述 我们前端页面中的数据&#xff0c;如下图所示的表格中的学生信息&#xff0c;应该来自于后台&#xff0c;那么我们的后台和前端是互不影响的2个程序&#xff0c;那么我们前端应该如何从后台获取数据呢&#xff1f;因为是2个程序&#xf…

JavaEE-文件IO操作

构造方法 一般方法&#xff0c;有很多&#xff0c;我们以下只是列举几个经常使用的 注意在上述的操作过程中&#xff0c;无论是绝对路径下的这个文件还是相对路径下的这个文件&#xff0c;都是不存在的 Reader 使用 --> 文本文件 FileReader类所涉及到的一些方法 Fil…

Covert Communication 与选择波束(毫米波,大规模MIMO,可重构全息表面)

Covert Communication for Spatially Sparse mmWave Massive MIMO Channels 2023 TOC abstract 隐蔽通信&#xff0c;也称为低检测概率通信&#xff0c;旨在为合法用户提供可靠的通信&#xff0c;并防止任何其他用户检测到合法通信的发生。出于下一代通信系统安全链路的强烈…

C#学习系列相关之多线程(一)----常用多线程方法总结

一、多线程的用途 在介绍多线程的方法之前首先应当知道什么是多线程&#xff0c; 在一个进程内部可以执行多个任务&#xff0c;而这每一个任务我们就可以看成是一个线程。是程序使用CPU的基本单位。进程是拥有资源的基本单位&#xff0c; 线程是CPU调度的基本单位。多线程的作用…