【C语言 || 数据结构】快速排序

news2024/11/22 5:35:13

文章目录

    • 前言
    • 快速排序
      • 1.快排的前后指针法
        • 1.1快排的前后指针法的代码实现
        • 1.2快排的前后指针法的注意事项
      • 2.快排的挖坑法
        • 2.1快排的挖坑法的代码实现
        • 2.2快排的挖坑法的注意事项
      • 3.快排的hoare法
        • 3.1快排的hoare法的代码实现
        • 3.2快排的hoare法的注意事项
      • 4快排的优化
        • 4.1快排的三数取中
        • 4.2快排的三数取中的实现方法
        • 4.3快排的小区间优化
        • 4.3快排的小区间优化的代码实现

前言

快速排序是一种基于分治策略的排序算法,通过选取一个基准元素将数组分为两部分,再递归地对这两部分进行排序,平均时间复杂度为O(n log n)。它在原地进行排序,且在实际应用中非常高效。

快速排序

快速排序有三种方式实现:hoare(托尼·霍尔)版本、前后指针、挖坑法

快速排序(QuickSort)的逻辑是基于分治策略,算法的基本步骤包括选择一个基准值(key),将数组分成两个子数组:一个小于基准值的子数组,另一个大于基准值的子数组,然后将key和最后的位置相互交换,最后递归地对这两个子数组进行快速排序。

1.快排的前后指针法

快速排序的前后指针方法个人认为是三种方法中较为简单,易于理解的一种了。
它通过两个指针(通常称为“前指针”和“后指针”)来有效地进行分区操作。

我们可以先看动图:

在这里插入图片描述

算法逻辑
1.先定义一个基准值(key)和两个指针cur(快)和prev(慢),让两个指针指向key的下一个下标.
2.让cur先走,如果cur++小于key的话,就让prev也++.
3.直到cur大于key,prev就停止下来,不在发生移动,直到cur再次移动到比key小的,然后prev再次++,然后和cur进行交换,
4.直到cur走出数组,直接让prev与key进行交换
5.最后进行递归,就可以了

1.1快排的前后指针法的代码实现
void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

// 前后指针法
void quicksort(int* a, int left,int right)
{
	if (left >= right)
		return;

	int key = left;	
	int prev = left;
	int cur = left + 1;
	while (cur <= right)
	{
		if (a[cur] < a[key])
		{
			prev++;
			Swap(&a[cur], &a[prev]);
		}
		cur++;
	}
	Swap(&a[key], &a[prev]);
	// [left,key-1] [key+1,right] 
	quicksort(a, left, key - 1);
	quicksort(a, key + 1, right);
}
1.2快排的前后指针法的注意事项

递归需要有跳出递归的条件,切记cur是小于等于right,不能超出数组的范围,否则就会导致越界风险。

2.快排的挖坑法

快速排序的挖坑法(也称为“填坑法”)是一种直观且有效的排序算法实现方式。

看动图:
在这里插入图片描述

算法逻辑
1.首先将最左边的数定义为坑位,并把这个坑位的值记录在key中,同时定义两个变量,begin和end,一个从最左边开始每一个从右边开始。
2.先从右边开始向左边开始找小于key的数据,然后和坑位进行交换,交换完成之后,把坑位转移到end当前所在的位置。
3.然后再让begin开始向右移动,找到比key大的值然后和坑位进行交换,再次将坑位移动到当前begin所在的位置。
4.直到最后begin和end相遇了,且坑位的值已经进行过交换,将key值放入坑位。
5.再进行递归就可以实现算法了

2.1快排的挖坑法的代码实现
// 挖坑法
void quicksort(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--;
		}

		if (begin < end && a[end] < key)	
		{
			a[keyi] = a[end];
			keyi = end;
		}

		while(begin < end && a[begin] <= key)
		{
			begin++;
		}

		if (begin < end && a[begin] > key)
		{
			a[keyi] = a[begin];
			keyi = begin;
		}
	}
	a[keyi] = key;
	// [left,keyi-1] [keyi+1,right]
	quicksort(a, left, keyi - 1);
	quicksort(a, keyi + 1 , right);
}
2.2快排的挖坑法的注意事项

开始比较的时候一定要从右边开始进行。
开始挖坑的时候切记,需要将key值进行保存,否则后期找不到最开始的key值。
再内部进行比较的时候,也一定要将begin和end进行比较,否则就会有报错。
数据进行交换的时候,坑位移动要发生改变,否则排序将失败。

3.快排的hoare法

快速排序的Hoare法是一种基于二叉树思想的交换排序方法,由托尼·霍尔在1962年提出。

动图:
在这里插入图片描述

算法逻辑
1任取待排序元素序列中的某元素作为基准值(key),通常选择第一个或最后一个元素。
2.初始化两个指针,begin指向序列的开始,end指向序列的末尾。
3.begin从左边开始找比key大的值,找到就停下来。
4.end从右边开始找比key小的值,找到就和begin进行交换。
5.直到begin和end相遇,将这个位置与key进行交换,然后再分治递归即可。

3.1快排的hoare法的代码实现
// hoare法
void quicksort(int* a, int left, int right)
{
	if (left >= right)
		return;

	int key = left;
	int begin = left;
	int end = right;
	while (begin < end)
	{
		while (begin < end && a[end] >= a[key])
		{
			end--;
		}

		while (begin < end && a[begin] <= a[key])
		{
			begin++;
		}
		
		Swap(&a[begin], &a[end]);
	}
	Swap(&a[begin], &a[key]);
	key = begin;
	// [left,keyi-1] [keyi+1,right]
	quicksort(a, left, key - 1);
	quicksort(a, key + 1, right);
}
3.2快排的hoare法的注意事项

开始比较的时候一定要从右边开始进行。
end找到比key小的值需要停下来,直到begin遇到比key大的值,才进行交换。

4快排的优化

快拍有两个优化,一个是三数取中,另一个是小区间优化

4.1快排的三数取中

其实key的取值是有方法的,取三个值中间的那个,取最左边的值、最右边的值和中间的那个值。避免最坏情况的O(N^2),避免有序和接近有序。

4.2快排的三数取中的实现方法
int GetMidi(int* a, int left, int right)
{
	int midi = (left + right) / 2;
	if (a[left] > a[right])
	{
		if (a[midi] < a[right])
		{
			return right;
		}
		else if(a[midi] > a[left])
		{
			return left;
		}
		else
		{
			return midi;
		}
	}
	else // r < l
	{
		if (a[midi] < a[left])
		{
			return left;
		}
		else if (a[midi] < a[left])
		{
			return midi;
		}
		else
		{
			return right;
		}
	}
}
4.3快排的小区间优化

再数据个数较小的时候,为了避免继续递归分割,转而采用其他更高效的排序算法,如插入排序,以减少递归调用的次数,提高排序效率。

4.3快排的小区间优化的代码实现
// 快速排序hoare版本
void PartSort(int* a, int left, int right)
{
	if (left >= right)
		return;

	if ((left + right) < 10)
	{
		InsertSort(a+left, (right - left + 1));
	}
	else
	{
		int midi = GetMidi(a, left, right);
		Swap(&a[left], &a[midi]);

		int key = left;
		int begin = left;
		int end = right;
		while (begin < end)
		{
			while (begin < end && a[end] >= a[key])
			{
				end--;
			}

			while (begin < end && a[begin] <= a[key])
			{
				begin++;
			}

			Swap(&a[begin], &a[end]);
		}

		Swap(&a[key], &a[begin]);
		key = begin;
		PartSort(a, left, key - 1);
		PartSort(a, key + 1, right);
	}
}

// 快速排序挖坑法
void PartSort2(int* a, int left, int right)
{
	if (left >= right)
		return;

	if ((left + right) < 10)
	{
		InsertSort(a + left, (right - left + 1));
	}
	else
	{
		int midi = GetMidi(a, left, right);
		Swap(&a[left], &a[midi]);

		int key = left; // 坑位
		int keyi = a[left];
		int begin = left, end = right;
		while (begin < end)
		{
			while (a[end] >= keyi && begin < end)
			{
				end--;
			}
			if (a[end] < keyi)
			{
				a[key] = a[end];
				key = end;
			}
			while (a[begin] <= keyi && begin < end)
			{
				begin++;
			}
			if (a[begin] > keyi)
			{
				a[key] = a[begin];
				key = begin;
			}
		}
		a[key] = keyi;
		// [left,key-1] key [key+1,right]
		PartSort2(a, left, key - 1);
		PartSort2(a, key + 1, right);
	}
}

// 快速排序前后指针法
void PartSort3(int* a, int left, int right)
{
	if (left >= right)
		return;


	if ((left + right) < 10)
	{
		InsertSort(a + left, (right - left + 1));
	}
	else
	{
		int midi = GetMidi(a, left, right);
		Swap(&a[left], &a[midi]);

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

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

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

相关文章

恢复机制-数据库系统中的故障(事务故障、系统故障、介质故障)、一致性错误、窃取但不强制的缓冲区管理策略

一、引言 数据库管理系统DBMS的事务处理技术实现的一个主要功能部分就是恢复机制&#xff0c;恢复机制完成的功能就是对发生故障后系统中事务的更新结果进行数据恢复&#xff0c;保证事务的原子性和持久性&#xff0c;从而进一步保证数据库的一致性。 数据库系统与其他计算机系…

办公开源利器:ONLYOFFICE

目录 0、引子&#xff1a;一、ONLYOFFICE协作空间1.可集成至Web应用程序2.多种协作方式3.快捷的AI助手4.公共房间&#xff1a;连接第三方存储空间5.集成6.开发人员工具7.用插件拓展功能 二、新增功能1.功能全面的PDF编辑2.PDF 表单3.文本文档编辑器4.电子表格编辑器 三、结语 0…

EasyExcel数据导入

前言&#xff1a; 我先讲一种网上信息的获取方式把&#xff0c;虽然我感觉和后面的EasyExcel没有什么关系&#xff0c;可能是因为这个项目这个操作很难实现&#xff0c;不过也可以在此记录一下&#xff0c;如果需要再拆出来也行。 看上了网页信息&#xff0c;怎么抓到&#x…

【操作系统】进程管理——进程的概念、组成和特征(个人笔记)

学习日期&#xff1a;2024.6.29 内容摘要&#xff1a;进程的基本概念和特征、状态和转换 进程的概念 程序与进程 程序&#xff1a;是静态的&#xff0c;是存放在磁盘里的可执行文件&#xff0c;就是一系列的指令集合 进程&#xff08;Process&#xff09;&#xff1a;是动态…

一文带你了解乐观锁和悲观锁的本质区别!

文章目录 悲观锁是什么&#xff1f;乐观锁是什么&#xff1f;如何实现乐观锁&#xff1f;什么是CAS应用局限性ABA问题是什么&#xff1f; 悲观锁是什么&#xff1f; 悲观锁它总是假设最坏的情况&#xff0c;它会认为共享资源在每次被访问的时候就会出现线程安全问题&#xff0…

primeflex overflow样式类相关的用法和案例

文档地址&#xff1a;https://primeflex.org/overflow 案例1 <script setup> import axios from "axios"; import {ref} from "vue";const message ref("frontend variable") axios.get(http://127.0.0.1:8001/).then(function (respon…

库存管理系统基于spingboot vue的前后端分离仓库库存管理系统java项目java课程设计java毕业设计

文章目录 库存管理系统一、项目演示二、项目介绍三、部分功能截图四、部分代码展示五、底部获取项目源码&#xff08;9.9&#xffe5;带走&#xff09; 库存管理系统 一、项目演示 库存管理系统 二、项目介绍 基于spingboot和vue前后端分离的库存管理系统 功能模块&#xff…

如何利用python画出AHP-SWOT的战略四边形(四象限图)

在企业或产业发展的相关论文分析中&#xff0c;常用到AHP-SWOT法进行定量分析&#xff0c;形成判断矩阵后&#xff0c;如何构造整洁的战略四边形是分析的最后一个环节&#xff0c;本文现将相关代码发布如下&#xff1a; import mpl_toolkits.axisartist as axisartist import …

chrome.storage.local.set 未生效

之前chrome.storage.local.set 和 get 一直不起作用 使用以下代码运行成功。 chrome.storage.local.set({ pageState: "main" }).then(() > {console.log("Value is set");});chrome.storage.local.get(["pageState"]).then((result) > …

java之命令执行审计思路

1 漏洞原理 因用户输入未过滤或净化不完全&#xff0c;导致Web应用程序接收用户输入&#xff0c;拼接到要执行的系统命令中执行。一旦攻击者可以在目标服务器中执行任意系统命令&#xff0c;就意味着服务器已被非法控制。 2 审计中常用函数 一旦攻击者可以在目标服务器中执行…

2024 Parallels Desktop for Mac 功能介绍

Parallels Desktop的简介 Parallels Desktop是一款由Parallels公司开发的桌面虚拟化软件&#xff0c;它允许用户在Mac上运行Windows和其他操作系统。通过强大的技术支持&#xff0c;用户无需重新启动电脑即可在Mac上运行Windows应用程序&#xff0c;实现了真正的无缝切换。 二…

基于LangChain+LLM的本地知识库问答:从企业单文档问答到批量文档问答

前言 过去半年&#xff0c;随着ChatGPT的火爆&#xff0c;直接带火了整个LLM这个方向&#xff0c;然LLM毕竟更多是基于过去的经验数据预训练而来&#xff0c;没法获取最新的知识&#xff0c;以及各企业私有的知识 为了获取最新的知识&#xff0c;ChatGPT plus版集成了bing搜索…

11_电子设计教程基础篇(磁性元件)

文章目录 前言一、电感1、原理2、种类1、制作工艺2、用途 3、参数1、测试条件2、电感量L3、品质因素Q4、直流电阻&#xff08;DCR&#xff09;5、额定电流6、谐振频率SRF&#xff08;Self Resonant Frequency&#xff09;7、磁芯损耗 4、应用与选型 二、共模电感1、原理2、参数…

第一周:李宏毅机器学习笔记

第一周学习周报 摘要一、机器学习基础理论1. 什么是机器学习&#xff1f;2. 机器学习“寻找”的函数有哪些类型&#xff1f;3. 机器学习中机器如何“寻找”函数&#xff1f;三步走3.1 第一步&#xff1a;设定函数的未知量&#xff08;Function with Unknown Parameters&#xf…

大多数博客首页都在使用的文字打字机出现效果

打字机效果展示 原理步骤 初步框架 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</…

vue使用axios获取信息的案例

List组件&#xff08;用来展示搜索的信息&#xff09; <template><div class"row"><!-- 列表数据 --><div class"card" v-for"user in info.users" :key"user.login" v-show"info.users.length">&l…

在 Windows 下使用 Linux 命令的多种方法

在 Windows 操作系统上使用 Linux 命令行工具&#xff0c;对于许多开发者和系统管理员来说是一个常见的需求。特别是对于那些习惯于 Linux 命令行的用户来说&#xff0c;Windows 自带的 CMD 和 PowerShell 可能并不满足他们的需求。虽然 Windows Subsystem for Linux (WSL) 是一…

【JavaEE】多线程代码案例(1)

&#x1f38f;&#x1f38f;&#x1f38f;个人主页&#x1f38f;&#x1f38f;&#x1f38f; &#x1f38f;&#x1f38f;&#x1f38f;JavaEE专栏&#x1f38f;&#x1f38f;&#x1f38f; &#x1f38f;&#x1f38f;&#x1f38f;上一篇文章&#xff1a;多线程&#xff08;2…

力扣 单词规律

所用数据结构 哈希表 核心方法 判断字符串pattern 和字符串s 是否存在一对一的映射关系&#xff0c;按照题意&#xff0c;双向连接的对应规律。 思路以及实现步骤 1.字符串s带有空格&#xff0c;因此需要转换成字符数组进行更方便的操作&#xff0c;将字符串s拆分成单词列表…