数据结构之快速排序算法(快排)【图文详解】

P. S.:以下代码均在VS2019环境下测试,不代表所有编译器均可通过。
P. S.:测试代码均未展示头文件stdio.h的声明,使用时请自行添加。

  

在这里插入图片描述

                                           博主主页:LiUEEEEE
                                              C语言专栏
                                            数据结构专栏
                                         力扣牛客经典题目专栏

目录

  • 1、快排的基本思想
  • 2、快排的四种实现方法
  • 3、Hoare快速排序
  • 4、挖坑法快速排序
  • 5、前后指针法快速排序
  • 6、非递归法快速排序
  • 7、快速排序的优化
    • 7.1 三数取中
    • 7.2 少量数据另谋他法
  • 8、快速排序的特性总结
  • 9、结语

1、快排的基本思想


  快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为

  任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。




2、快排的四种实现方法


  快速排序有多种实现方法,其具体体现如下:
  • Hoare快速排序
  • 挖坑法快速排序
  • 前后指针法快速排序
  • 非递归法快速排序

下文将对上述四种实现方法依次进行讲解。




3、Hoare快速排序


  Hoare快速排序的示意图如下所示:

在这里插入图片描述
  其主要思想为:使用循环遍历数组

  • 将所给数组的第一个值的下标给予key变量和begin变量(下文中统一为keyi,方便理解其为下标地址而非数组成员值),将数组最后一个值的下标给予end变量。
  • 开始执行是令end向数组左侧遍历,寻找数值比数组下标为keyi的值小的数,若未寻找到,则继续向左侧遍历,若寻找到,则停止遍历,转而为begin向数组右侧遍历。
  • begin向数组右侧遍历,寻找数值比下标为keyi的值大的数,若寻找到,则交换数组中下标为end和begin的值。
  • 若在begin与end遍历过程中两变量出现相等的情况,则退出循环,将数组中下标为keyi和end的值交换。
  • 记录下交换位置,令其为新的分割点,利用递归的思想,将数组根据新分割点分割的左右两次子数组进行递归,直到出现新数组中L >= R。
  • 递归结束。


      其递归思想的图示如下,示例:成员为4,2,6,5,7,1,3,8,10,9的数组。
    在这里插入图片描述  当记录交换位置为3,即会有新的子数组【0,2】【4,9】,使用新数组进行递归。

在这里插入图片描述
  递归方法如上图所示(除3外其余递归交换位置为虚构,需实际计算得到)。

  • Hoare快速排序代码如下
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

int PartSort1(int* a, int left, int right)
{
	if (left >= right)
		return;
	int begin = left;
	int end = right;
	int keyi = left;
	while (begin < end)
	{
		while(begin < end && a[end] >= a[keyi])
		{
			end--;
		}
		while (begin < end && a[begin] <= a[keyi])
		{
			begin++;
		}
		Swap(&a[end], &a[begin]);
	}
	Swap(&a[end], &a[keyi]);
	keyi = begin;
	PartSort1(a, left, keyi - 1);
	PartSort1(a, keyi + 1, right);
}




4、挖坑法快速排序


  挖坑法快速排序示意图如下所示:

在这里插入图片描述
  其主要思想为:使用循环遍历数组

  • 将数组中keyi所在下标的值暂存起来,其位置看作坑位,先令end从右向左遍历寻找比暂存值小的值,寻找到后,将end所指向的值放入坑中,而后end所指向的位置变成新坑。
  • 再令begin从右向左寻找比暂存值大的值,寻找到后,将begin所指向的值放入坑中,而后begin所指向位置变为新坑。
  • 在循环过程中若begin与end相遇,则将暂存之放入此时的坑中。
  • 记录下最后一个坑的位置视作分界点,从分界点处分出两个新的子数组,进行下一轮递归,直至所分出的新数组L >= R。
  • 递归结束。
      其递归示意图与标题三中所展示一致,故不再做重复展示。

  • 挖坑法快速排序代码如下所示:
int PartSort2(int* a, int left, int right)
{
	if (left >= right)
		return;
	int key = a[left];
	int keyi = left;
	int begin = left;
	int end = right;
	while (begin < end)
	{
		while (begin < end && a[end] >= key)
		{
			end--;
		}
		a[keyi] = a[end];
		keyi = end;
		while (begin < end && a[begin] <= key)
		{
			begin++;
		}
		a[keyi] = a[begin];
		keyi = begin;
	}
	a[keyi] = key;
	PartSort2(a, left, end - 1);
	PartSort2(a, end + 1, right);
}




5、前后指针法快速排序


  前后指针法快速排序示意图如下所示:

在这里插入图片描述
  其主要思想为:

  • 定义指针prev指向数组开始位置,cur指向数组开始第二个位置,并使用keyi记录数组中用作比较的数。

  • 令cur向数组右端遍历,若出现小于keyi的数则停止遍历,转为prev进行遍历。

  • 令prev向数组右端遍历,若出现大于keyi的数,则令其与cur交换位置,而后继续进行cur的遍历,直至cur达到数组末尾的下一位(即越界)。

  • 当cur越界后将当前prev所代表的值与key所代表值进行交换,并将prev所处位置视为分界点,从分界点处分出两个新的子数组进行递归,直至所递归的数组L >= R。

  • 递归结束。
      其递归示意图与标题三中所展示一致,故不再做重复展示。

  • 前后指针法快速排序代码如下所示:

void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

int PartSort3(int* a, int left, int right)
{
	if (left >= right)
		return;
	int prev = left;
	int cur = prev + 1;
	int key = left;
	while (cur <= right)
	{
		if (a[cur] < a[key] && a[++prev] != a[key])
			Swap(&a[cur], &a[prev]);
		cur++;
	}
	Swap(&a[key], &a[prev]);
	PartSort3(a, left, prev - 1);
	PartSort3(a, prev + 1, right);
}




6、非递归法快速排序


  非递归的方法实现快速排序,其原理是使用栈来模拟代码在系统中的栈中的递归过程,也可称之为伪递归。
  有关栈的相关内容可在下方查看:

                                栈(使用顺序表构建)


  其主要思想为:
  • 使用栈来模拟系统中栈的功能,将数组的右左下标依次传入栈中,每一次取其栈顶位置数据分别作为单词快排的目标数组左下标和右下表(每一次取值后需删除栈顶元素)。

  • 在单次快排完成后判断其在分段点所分两个新的子数组左右下标是否合理(是否存在左下标大于或等于右下标的行为),若不存在则将右子数组右左下标依次推入栈中,而后将左子数组右左下标依次推入栈中,进行下一轮循环。

  • 其循环判断条件为栈中是否为空。

  • 有关其入栈顺序可不做过多研究,但要保证的前提是所取出数值需与操作数组的左右下标相对应,以避免不必要麻烦。

  • 非递归法快速排序代码如下所示:

void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void QuickSortNonR(int* a, int left, int right)
{
	ST s;
	STInit(&s);
	STPush(&s, right);
	STPush(&s, left);
	while (!STEmpty(&s))
	{
		int begin = STTop(&s);
		STPop(&s);
		int end = STTop(&s);
		STPop(&s);


		int prev = begin;
		int cur = prev + 1;
		int key = begin;
		while (cur <= end)
		{
			if (a[cur] < a[key] && a[++prev] != a[key])
				Swap(&a[cur], &a[prev]);
			cur++;
		}
		Swap(&a[key], &a[prev]);
		if (prev + 1 < end)
		{
			STPush(&s, end);
			STPush(&s, prev + 1);
		}
		if (begin < prev - 1)
		{
			STPush(&s, prev - 1);
			STPush(&s, begin);
		}
	}

	STDestroy(&s);
}




7、快速排序的优化


   此优化可适用于任意方法的可快速排序。
  在日常生活中,我们所使用排序功能的目标对象可能不会想日常练习中那样少,或许是一个非常庞大的数量,且其所提供的数据可能在原本基础上就有部分有序化,例如所给的大量数据中第一位为最小值或最大值,而这样的数据我们从一开始就进行快速排序的话,会让end从末尾一直遍历到数据首部或begin从数据首部一直遍历到末尾,拥有时间复杂度上的浪费,所以我们提出了一个 三数取中的方法来进行优化,且针对于大量数据,当所处理数据的子数据数量过少时,我们还是用快速排序的方法有点大材小用了,故针对于此我们也提出了 少量数据另谋他法的方法应对。

7.1 三数取中


  针对于所给目标数组第一位即为最小值或最大值而导致的时间复杂度浪费,我们可以取其数组首位,中位,末位三个数进行比较,选取其中处于中间位置的数,令其与数据首位进行交换。
  • 其代码如下所示:
int GetMid(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[right] > a[left])
			return left;
		else
			return right;
	}
	else
	{
		if (a[mid] < a[right])
			return mid;
		else if (a[right] < a[left])
			return left;
		else
			return right;
	}
}

7.2 少量数据另谋他法


  对于根据分界点所得到的新子数组,若其数据量较小,我们还是用快速排序的化有些大材小用,故当数据量较小时,我们一般使用其他排序方法,如冒泡排序或插入排序等,进行快速的排序。
  • 其代码如下所示
void BubbleSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		for (int j = 0; j < n - i - 1; j++)
		{
			if (a[j] > a[j + 1])
				Swap(&a[j], &a[j + 1]);
		}
	}
}

if (right - left < 10)
	{
		BubbleSort(a, right - left + 1);
	}




  至此快速排序的优化结束,其完整代码如下所示(前后指针法快速排序):

void QuickSort(int* a, int left, int right)
{
	if (left >= right)
		return;
	int mid = GetMid(a, left, right);
	Swap(&a[left], &a[mid]);
	if (right - left < 10)
	{
		BubbleSort(a, right - left + 1);
	}
	else
	{
		int prev = left;
		int cur = prev + 1;
		int key = left;
		while (cur <= right)
		{
			if (a[cur] < a[key] && a[++prev] != a[key])
				Swap(&a[cur], &a[prev]);
			cur++;
		}
		Swap(&a[key], &a[prev]);
		PartSort3(a, left, prev - 1);
		PartSort3(a, prev + 1, right);
	}
}




8、快速排序的特性总结


   快速排序的特性总结:
  1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(logN)
  4. 稳定性:不稳定




9、结语


在这里插入图片描述

  十分感谢您观看我的原创文章。
  本文主要用于个人学习和知识分享,学习路漫漫,如有错误,感谢指正。
  如需引用,注明地址。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

超详解——深入详解Python基础语法——小白篇

目录 1 .语句和变量 变量赋值示例&#xff1a; 打印变量的值&#xff1a; 2. 语句折行 反斜杠折行示例&#xff1a; 使用括号自动折行&#xff1a; 3. 缩进规范 缩进示例&#xff1a; 4. 多重赋值&#xff08;链式赋值&#xff09; 多重赋值的应用&#xff1a; 5 .多…

【权威发布】2024年环境资源与可持续发展国际会议(ICERSD 2024)

2024年环境资源与可持续发展国际会议 2024 International Conference on Environmental Resources and Sustainable Development 会议简介 2024年环境资源与可持续发展国际会议是一场聚焦于环境保护、资源管理和可持续发展领域的国际盛会。本次会议旨在汇集全球顶尖的专家学者、…

探索教研在线平台的系统架构

教研在线平台作为一家致力于教育技术领域的企业&#xff0c;其系统架构扮演着至关重要的角色。本文将深入探讨教研在线平台的系统架构&#xff0c;从技术架构、数据架构和安全架构等方面进行分析&#xff0c;以期帮助读者更好地理解这一教育科技平台的运作模式。 技术架构是教研…

系统思考—啤酒游戏沙盘

10个智商120的‮组人‬成‮团的‬队&#xff0c;大‮的家‬集体智‮是商‬多少&#xff1f; 在‮期长‬辅‮各导‬种‮业企‬的‮程过‬中&#xff0c;我‮经们‬常‮察观‬到&#xff0c;虽‮每然‬个‮门部‬都‮力努‬解决‮己自‬的问题&#xff0c;但‮司公‬整体的‮收应…

C++学习插曲:“name“的初始化操作由“case“标签跳过

问题 "name"的初始化操作由"case"标签跳过 问题代码 case 3: // 3、删除联系人string name;cout << "请输入删除联系人姓名&#xff1a;" << endl;cin >> name;if (isExistPerson(&abs, name) -1){cout << "…

Deepin安装PostGresql

最近要把开发环境完全从Windows移到Deepin上&#xff0c;本次介绍在Deepin借助apt-get安装和配置数据库。同时可以用Dbever提供图形化管理工具。 安装PostGreSQL数据库和创建数据库 #安装postgresql zhanglianzhuzhanglianzhu-PC:/$ sudo apt-get install postgresql-16 正在…

带池化注意力 Strip Pooling | Rethinking Spatial Pooling for Scene Parsing

论文地址:https://arxiv.org/abs/2003.13328 代码地址:https://github.com/houqb/SPNet 空间池化已被证明在捕获像素级预测任务的长距离上下文信息方面非常有效,如场景解析。在本文中,我们超越了通常具有N N规则形状的常规空间池化,重新思考空间池化的构成,引入了一种…

【Docker】上海交通大学开源镜像站服务变更:Docker 用户需迅速行动

近日&#xff0c;上海交通大学开源镜像站宣布了一个重大变更&#xff0c;对国内Docker用户来说&#xff0c;这一消息无疑具有紧迫性。 镜像站服务的变更 上海交通大学开源镜像站一直是国内Docker用户的重要资源&#xff0c;它提供了快速下载DockerHub仓库镜像的服务。然而&a…

Leetcode 刷题第三天|链表

链表理论 什么是链表 链表是一种通过指针串联在一起的线性结构&#xff0c;每个节点有两个部分组成&#xff1a; 数据域和指针域。最后一个节点的指针域指向null 链表的入口节点为链表的头结点也就是head。 链表的类型 单链表 如上图就是单链表 双链表 单链表的指针域只…

对比深度图聚类的硬样本感知网络

Hard Sample Aware Network for Contrastive Deep Graph Clustering 文章目录 Hard Sample Aware Network for Contrastive Deep Graph Clustering摘要引言方法实验结论启发点 摘要 本文提出了一种名为Hard Sample Aware Network (HSAN)的新方法&#xff0c;用于对比深度图聚类…

OpenStack学习笔记之三:用软件定义的理念做安全

第3章 用软件定义的理念做安全 1.不进则退&#xff0c;传统安全回到“石器时代” 1.1 企业业务和IT基础设施的变化 随着企业办公环境变得便利&#xff0c;以及对降低成本的天然需求&#xff0c;企业始终追求IT集成设施的性价比、灵活性、稳定性和开放性。而云计算、移动办公…

【多模态/CV】图像数据增强数据分析和处理

note 多模态大模型训练前&#xff0c;图片数据处理的常见操作&#xff1a;分辨率调整、网格畸变、水平翻转、分辨率调整、随机crop、换颜色、多张图片拼接、相似图片检测并去重等 一、分辨率调整 from PIL import Image def resize_image(original_image_path, save_image_p…

【STM32】uc/OS-III多任务程序

目录 一、背景介绍二、UCOS-III简单介绍&#xff08;一&#xff09;源码&#xff08;二&#xff09;功能 三、实验&#xff08;一&#xff09;基于STM32CubeMX建立工程1、创建项目2、配置项目 &#xff08;二&#xff09;实现 四、总结五、参考 一、背景介绍 学习嵌入式实时操…

使用cv2控制鼠标实现circle的拖拽

2.代码 import numpy as np import cv2x_center [100,200,300,400] y_center [200,200,200,200] radius 30def mouse_LButtonDown(event, x, y, flags, param):global tempif event cv2.EVENT_LBUTTONDOWN:print(f" Down Clicked at ({x}, {y})")for i in range…

贪心算法-加油站

一、题目描述 二、解题思路 1.运动过程分析 这里需要一个油箱剩余油量的变量resGas&#xff0c;初始化resGas0&#xff1b;还需要一个标记从什么位置当做初始位置的startIdx&#xff0c;初始化startIdx0。 我们从数组下标idx0处开始向后遍历&#xff0c;初始时startIdx0&#…

第一个小爬虫_爬取 股票数据

前言 爬取 雪球网的股票数据 [环境使用]&#xff1a;python 3.12 解释器pycharm 编辑器 【模块使用】&#xff1a;import requests -->数据请求模块 要安装 命令 pip install requestsimport csv -->将数据保存到CSV表格中import pandas -->也可以将数据保…

武汉理工大学嵌入式系统应用之临时抱佛脚复习

其实大学很多课程的期末冲刺复习非常简单&#xff0c;就是在大脑中构建一个redis数据库就行了&#xff0c;缓存下一大堆键值对&#xff0c;然后考试的时候输出&#xff0c;很没意思。 嵌入式系统的定义 以应用为中心&#xff0c;以计算机技术为基础&#xff0c;软件硬件可裁剪…

基于Python的北京天气数据可视化分析

项目用到库 import numpy as np import pandas as pd import datetime from pyecharts.charts import Line from pyecharts.charts import Boxplot from pyecharts.charts import Pie,Grid from pyecharts import options as opts from pyecharts.charts import Calendar 1.2…

【Vue】——组件的注册与引用

&#x1f4bb;博主现有专栏&#xff1a; C51单片机&#xff08;STC89C516&#xff09;&#xff0c;c语言&#xff0c;c&#xff0c;离散数学&#xff0c;算法设计与分析&#xff0c;数据结构&#xff0c;Python&#xff0c;Java基础&#xff0c;MySQL&#xff0c;linux&#xf…

大泽动力30KW静音汽油发电机

安全操作&#xff1a; 在使用前&#xff0c;确保发电机放置在通风良好、干燥、无易燃物品的地方。 避免在发电机运行时触摸其热表面或运转部件&#xff0c;以免烫伤或受伤。 遵循发电机的启动和停机程序&#xff0c;不要随意操作。 燃油管理&#xff1a; 使用高质量的汽油&…