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

news2024/9/23 5:23:00

🌈个人主页: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;
			else
				return left;
		}
	}
	else//left<=mid
	{
		if (a[mid] < a[right])
			return mid;
		else//mid>right
		{
			if (a[left] > a[right])//mid>left>right
				return left;
			else
				return 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;
}

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

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

相关文章

C++求职LinuxWebServer面试篇(项目介绍)

⭐️我叫忆_恒心,一名喜欢书写博客的在读研究生👨‍🎓。 如果觉得本文能帮到您,麻烦点个赞👍呗! 近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧,喜欢的小伙伴给个三连支持一下呗。👍⭐️❤️ Qt5.9专栏定期更新Qt的一些项目Demo 项目与…

【数学建模】趣味数模问题——棋子颜色问题

问题描述&#xff1a; 在任意拿出黑白两种颜色的棋子共 n 个&#xff0c;随机排成一个圆圈。然后根据以下规则放置棋子&#xff1a; 在两颗颜色相同的棋子中间放一颗黑色棋子在两颗颜色不同的棋子中间放一颗白色棋子 放完后撤掉原来所放的棋子&#xff0c;重复以上过程&…

仿某知识蝉在线学习副业项目知识付费系统小程序源码

一、仿知识蝉知识付费系统源码简介 知识付费系统&#xff0c;集合了图文、音频、视频、直播授课&#xff0c;专栏&#xff0c;品牌商城等主流功能和付费形式&#xff0c;并且包含多种多样的营销方案和互动模块&#xff0c;打造属于自己的知识付费系统。 安装步骤&#xff1a;…

聚星文社的工具使用0.2.9版本

聚星文社是一个在线写作社区&#xff0c;提供多种工具供用户使用。以下是聚星文社的工具使用说明&#xff1a; 聚星文社的工具https://docs.qq.com/doc/DRU1vcUZlanBKR2xy 写作工具&#xff1a;聚星文社提供了一个强大的在线写作工具&#xff0c;包括富文本编辑器、字数统计、拼…

计算机毕业设计选题推荐-民宿管理系统-酒店预定系统-Java/Python项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

windows下TortoiseSVN切换账号的方法

前言 在项目开始初期的时候大家会使用一个默认账号,后面会根据需要给每个人分配各自的个人账号,这个时候就需要重登陆新的svn账号,下面就是讲解下怎样在windows下修改登录TortoiseSVN的账号。 方法 1.首先在桌面右键&#xff0c;选择TortoiseSVN-settings 2.进入设置页面&a…

Android常见的界面布局

目录 ​前言 1.线性布局LinearLayout 2.相对布局RelativeLayout 3.表格布局TableLayout 4.网格布局GridLayout 实现一个计算器界面 改Button按钮颜色 5.帧布局FrameLayout 前言 在Android应用程序中&#xff0c;界面是由布局和控件组成的。控件是功能单元&#xff0c;负…

【C++】深入探索类和对象:初始化列表及其static成员与友元

C语法相关知识点可以通过点击以下链接进行学习一起加油&#xff01;命名空间缺省参数与函数重载C相关特性类和对象-上篇类和对象-中篇 本章将分享C中类和对象最后章节“深入探索类和对象&#xff1a;初始化列表及其static成员与友元”&#xff0c;希望通过这几篇关于类和对象文…

JavaScript 错误 - Throw 和 Try to Catch

try 语句使您能够测试代码块中的错误。 catch 语句允许您处理错误。 throw 语句允许您创建自定义错误。 finally 使您能够执行代码&#xff0c;在 try 和 catch 之后&#xff0c;无论结果如何。 实例 在本例中&#xff0c;我们通过 adddlert 编写警告代码来故意制造了一个…

实用库/函数之string的用法

目录 1.使用: 2.定义 3.访问(两种方式) (1)通过下标访问(像字符数组那样) (2)通过迭代器访问 4.常用函数 (1)operator+=:string的加法,将两个string直接拼接起来 (2)compare operator:两个string类型可以直接使用==、!=、<、<=、>、>=比较大…

<数据集>红绿灯识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;7884张 标注数量(xml文件个数)&#xff1a;7884 标注数量(txt文件个数)&#xff1a;7884 标注类别数&#xff1a;3 标注类别名称&#xff1a;[light_green, light_red, light_yellow] 序号类别名称图片数框数1lig…

数据结构与算法 - 分治

一、概述 分治思想 将大问题划分为两个到多个子问题子问题可以继续拆分成更小的子问题&#xff0c;直到能简单求解如有必要&#xff0c;将子问题的解进行合并&#xff0c;得到原始问题的解 1. 二分查找 public static int binarySearch(int[] a, int target) {return recursi…

遇到的几个iOS问题

1 unable to boot the simulator 跑模拟器的时候遇到这个报错&#xff0c; 解决方法 处理办法&#xff1a; 删除升级之前的模拟器缓存&#xff0c;重启模拟器。删除路径&#xff1a;~/Library/Developer/CoreSimulator/Cache 注意&#xff1a;后面可能还会复现这个报错&#x…

计算机毕业设计选题推荐-养老院管理系统-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

vue2项目支持@用户的文本域,使用at-textarea实现

vue2项目支持用户的文本域&#xff0c;使用at-textarea 第一步安装at-textarea npm install at-textarea第二步引入并注册 import AtTextarea from "at-textarea" components:{AtTextarea }第三步使用 <AtTextarea:AtList"AtList":AtConfig"{k…

CData Drivers for Cosmos DB Crack

CData Drivers for Cosmos DB Crack   Key Features of CData Drivers for Cosmos DB: Multi-API Support: Compatibility with various Cosmos DB APIs, such as SQL, MongoDB, Cassandra, Gremlin, and Table, allows for data model flexibility. SQL Query Execution: A…

Frida 的下载和安装

首先要安装好 python 环境 安装 frida 和 工具包 pip install frida frida-tools 查看版本&#xff1a; frida --version 16.4.8 然后到 github 上下载对应 server &#xff08; 和frida 的版本一致 16.4.8&#xff09; Releases frida/frida (github.com) 查看手机或…

计算机毕业设计选题推荐-高校实验室管理系统-Java/Python项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

CommunityToolkit.MVVM

前言 MVVM工具包&#xff0c;以前名为 Microsoft.Toolkit.Mvvm 由 Microsoft 维护和发布&#xff0c;是 .NET Foundation 的一部分。 支持&#xff1a;.NET Standard 2.0、 .NET Standard 2.1 和 .NET 6(UI Framework 不可知&#xff0c;基本使用没有问题/编译特性用不了) 注…

C语言-写一函数,实现两个字符串的比较。即自己写一个strcmp函数,函数原型为 int strcmp(char *pl,char *p2);

题目要求&#xff1a; 17.写一函数,实现两个字符串的比较。即自己写一个strcmp函数,函数原型为 int strcmp(char *pl,char *p2); 设p1指向字符串s1&#xff0c;p2指向字符串s2。要求当s1s2时,返回值为0;若s1≠s2,返回它们二者第1个不同字符的 ASCI1码差值(如"BOY"与…