常见排序算法之快速排序

       快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法。

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

void QuickSort(int array[], int left, int right)
{
     if(right - left <= 1)
         return; 
     int div = partion(array, left, right);
     QuickSort(array, left, div); 
     QuickSort(array, div+1, right);
}

       上述为快速排序递归实现的主框架,发现与二叉树前序遍历规则非常像,我们在写递归框架时可想想二叉树前序遍历规则即可快速写出来,后序只需分析如何按照基准值来对区间中数据进行划分的方式即可。

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

        1.hoare版本

        2.挖坑法

        3.前后指针版本

下面将介绍三种方法,在此之前我们现需写一个三数取中代码:

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 left;
		}
		else
		{
			return right;
		}
	}
	else 
	{
		if (a[mid] > a[right])
		{
			return mid;
		}
		else if (a[left] < a[right]) 
		{
			return left;
		}
		else
		{
			return right;
		}
	}
}

一、hoare版本

具体步骤

  1. 先选取数组左边的第一个数作为key,定义一个左下标left指向最左边的数,定义一个右下标right指向最右边的数。
  2. 然后让right先往左遍历,找到第一个比key大的值,让left后向右遍历,找到第一个比key小的值。
  3. 这时,左边的值都比key小,右边的值都比key大,交换left和right指向的值。
  4. 这样一趟排序就结束了,key就在了它应该在的位置上。重复以上步骤,直到整个序列有序。

核心代码

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

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
		return;

	int keyi = PartSort1(a, begin, end);
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}

void PrintArray(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

void TestQuickSort()
{
	int a[] = { 6,1,2,7,9,3,4,5,10,8 };
	QuickSort(a, 0, sizeof(a) / sizeof(int) - 1);
	PrintArray(a, sizeof(a) / sizeof(int));
}

int main()
{
    TestQuickSort();
	return 0;
}

实验结果


二、挖坑法

具体步骤

       首先设置两个指针,一个指向数组的起始位置,一个指向数组的末尾位置。从末尾位置开始,向前遍历,找到第一个小于基准元素的元素,并将其填入起始位置的坑中。然后从起始位置开始,向后遍历,找到第一个大于基准元素的元素,并将其填入上一步所挖的坑中。重复上述步骤,直到起始位置和末尾位置相遇。此时,将基准元素填入最后一个坑中,这样就完成了一次分区操作。最后对分区后的左右两个子数组,分别递归地进行上述步骤。

核心代码

int PartSort2(int* a, int left, int right)
{
	int midi = GetMidi(a, left, right);
	Swap(&a[left], &a[midi]);
	int key = a[left];
	int hole = left;
	while (left < right)
	{
		while (left < right && a[right] >= key)
		{
			--right;
		}
		a[hole] = a[right];
		hole = right;
		while (left < right && a[left] <= key)
		{
			++left;
		}
		a[hole] = a[left];
		hole = left;
	}
	a[hole] = key;
	return hole;
}

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
		return;

	int keyi = PartSort2(a, begin, end);
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}

void PrintArray(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

void TestQuickSort()
{
	int a[] = { 6,1,2,7,9,3,4,5,10,8 };
	QuickSort(a, 0, sizeof(a) / sizeof(int) - 1);
	PrintArray(a, sizeof(a) / sizeof(int));
}

int main()
{
    TestQuickSort();
	return 0;
}

实验结果


三、前后指针版本

具体步骤

  1. 选择枢轴元素:在数组中选择一个元素作为枢轴,一般选择第一个元素或最后一个元素。

  2. 初始化左右指针:左指针指向数组的第一个元素,右指针指向数组的最后一个元素。

  3. 划分过程:从前往后遍历数组,当找到一个比枢轴大的元素时,将左指针向右移动一位;从后往前遍历数组,当找到一个比枢轴小的元素时,将右指针向左移动一位。当左指针大于等于右指针时,说明已经找到正确的位置,将枢轴与左指针所在位置的元素交换。

  4. 递归处理:将枢轴左边的部分作为新的子数组,递归调用快速排序函数;将枢轴右边的部分作为新的子数组,递归调用快速排序函数。

核心代码 

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

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
		return;

	int keyi = PartSort3(a, begin, end);
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}

void PrintArray(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

void TestQuickSort()
{
	int a[] = { 6,1,2,7,9,3,4,5,10,8 };
	QuickSort(a, 0, sizeof(a) / sizeof(int) - 1);
	PrintArray(a, sizeof(a) / sizeof(int));
}

int main()
{
    TestQuickSort();
	return 0;
}

实验结果


四、非递归版本

       快速排序的非递归版本是递归版本的一种优化,主要是通过将递归过程转化为循环来实现。基本思路是将数组分为两部分,一部分比基准值小,另一部分比基准值大,然后对这两部分分别进行快速排序。这个过程可以通过循环来实现,而不是通过递归调用函数自身。

具体步骤

       首先从数组中挑选一个元素作为基准值,然后将所有小于基准值的元素移动到基准值的左边,将所有大于基准值的元素移动到基准值的右边。接下来,对基准值左右两边的子数组分别进行相同的操作,直到数组完全有序。

核心代码

void QuickSortNonR(int* a, int left, int right)
{
Stack st;
StackInit(&st);
StackPush(&st, left);
StackPush(&st, right);
while (StackEmpty(&st) != 0)
{
 right = StackTop(&st);
 StackPop(&st);
 left = StackTop(&st);
 StackPop(&st);
 
 if(right - left <= 1)
 continue;
 int div = PartSort1(a, left, right);
 // 以基准值为分割点,形成左右两部分:[left, div) 和 [div+1, right)
 StackPush(&st, div+1);
 StackPush(&st, right);
 
 StackPush(&st, left);
 StackPush(&st, div);
}
 
 StackDestroy(&s);
}

       感兴趣的同学可以自行练习。


五、快速排序特性总结

        1.快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序

        2.时间复杂度:O(N*logN)

        3.空间复杂度:O(logN)

        4.稳定性:不稳定


结语:快速排序的相关分享到这里就结束了,希望对大家的学习会有帮助,如果大家有什么问题或者不同的见解,欢迎大家的留言~~~

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

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

相关文章

【Pytest】跳过执行之@pytest.mark.skip()详解

一、skip介绍及运用 在我们自动化测试过程中&#xff0c;经常会遇到功能阻塞、功能未实现、环境等一系列外部因素问题导致的一些用例执行不了&#xff0c;这时我们就可以用到跳过skip用例&#xff0c;如果我们注释掉或删除掉&#xff0c;后面还要进行恢复操作。 1、skip跳过成…

nodejs express multer 保存文件名为中文时乱码,问题解决 originalname

nodejs express multer 保存文件名为中文时乱码&#xff0c;问题解决 originalname 一、问题描述 用 express 写了个后台&#xff0c;在接收文件并保存的时候 multer 接收到的文件名为乱码。 二、解决 找了下解决方法&#xff0c;在 github 的 multer issue 中找到了答案 参…

【MySQL进阶之路丨第十七篇(完结)】一文带你精通MySQL运算符

引言 在上一篇中我们介绍了MySQL函数&#xff1b;在开发中&#xff0c;对MySQL运算符的运用是十分重要的。这一篇我们使用命令行方式来帮助读者掌握MySQL中运算符的操作。 上一篇链接&#xff1a;【MySQL进阶之路丨第十六篇】一文带你精通MySQL函数 MySQL运算符 MySQL中的运…

node插件MongoDB(四)—— 库mongoose 的个性话读取(字段筛选、数据排序、数据截取)(四)

文章目录 一、字段筛选二、数据排序三、数据截取1. skip 跳过2. limit 限定![在这里插入图片描述](https://img-blog.csdnimg.cn/c7067b1984ee4c6686f8bbe07cae9176.png) 一、字段筛选 字段筛选&#xff1a;只读取指定的数据&#xff0c;比如集合&#xff08;表&#xff09;中有…

JMeter 相关的面试题

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;加入1000人软件测试技术学习交流群&#x1f4e2;资源分享&#xff1a;进了字节跳动之后&#xff0c;才…

【已验证】php配置连接sql server中文乱码(解决方法)更改utf-8格式

解决数据库中的中文数据在页面显示乱码的问题 在连接的$connectionInfo中设置"CharacterSet" > "UTF-8"&#xff0c;指定编码方式即可 $connectionInfo array("UID">$uid, "PWD">$pwd, "Database">$database…

提升采购订单管理效率的五个最佳实践

企业每年都要订购成千上万的商品和服务。公司运营和发展所需的一切都来自庞大的供应商网络&#xff0c;而沟通这些需求的主要方式是通过采购订单。 由于所有订单都会流经系统&#xff0c;而且每个月都会发生数千元不等的供应支出&#xff0c;因此掌握采购订单流程成为重中之重…

[autojs]逍遥模拟器和vscode对接

第一步&#xff1a;启动autojs服务 第二步&#xff1a;去cmd查看ip地址&#xff0c;输入ipconfig 第三步&#xff1a;打开逍遥模拟器中的sutojs-左上角- 连接电脑&#xff0c;然后输入WLAN或者其他ip也行&#xff0c;根据自己电脑实际情况确认 此时vscode显示连接成功。我们写…

selenium 自动化测试 1-如何搭建自动化测试环境,搭建环境过程应该注意的问题

最近也有很多人私下问我&#xff0c;selenium学习难吗&#xff0c;基础入门的学习内容很多是3以前的版本资料&#xff0c;对于有基础的人来说&#xff0c;3到4的差别虽然有&#xff0c;但是不足以影响自己&#xff0c;但是对于没有学过的人来说&#xff0c;通过资料再到自己写的…

Pinia 是什么?Redux、Vuex、Pinia 的区别?

结论先行&#xff1a; Pinia 是 Vue 官方团队开发的一个全新状态管理库。核心都是解决组件间的通信和数据的共享问题。 它和 Vuex 的区别呢&#xff0c;一个是虽然它和 Vuex 类似&#xff0c;但 Pinia 使用起来更加简单和直观。因为 Pinia 基于 Vue3 的组合式 API 风格&…

【Unity】零基础实现塔防游戏中敌人沿固定路径移动的功能

目录 场景搭建 烘焙(Bake) 敌人动作控制 脚本实现 我们知道&#xff0c;在一些塔防小游戏中&#xff0c;敌人往往会沿着给定的一条路径移动&#xff0c;我们在条路的路边会布置防御设施&#xff0c;攻击消灭敌人&#xff0c;阻止敌人到达终点。 场景搭建 我们首先新建一个…

【数据结构与算法】DFA算法-关键词匹配-java案例实现

该算法往往是用于匹配一些敏感词、绝对词等&#xff0c;从一篇文章中快速找到其中包含的关键词。 实现思路&#xff1a; 先读取所有关键词并存入set集合中。再将set中的关键词存入HashMap中&#xff0c;是以每个关键词字顺序存储&#xff0c;key为一个字、value为一个HashMap。…

Laplacian Redecomposition for Multimodal Medical Image Fusion

LRD方法 GDIE means ‘gradient-domain image enhancement’&#xff0c;DGR means ‘decision graph redecomposition’ MLD means ‘maximum local difference’ LEM means ‘local energy maximum’&#xff0c;OD means ‘overlapping domain’&#xff0c;LP means ‘L…

Awesome-Selfhosted:互联网常见服务开源平替 | 开源日报 No.68

awesome-selfhosted/awesome-selfhosted Stars: 137.7k License: NOASSERTION Awesome-Selfhosted 是一个列出了可以在自己的服务器上托管的免费软件网络服务和 Web 应用程序列表。 以下是该项目的主要功能&#xff1a; 提供各种类型 (如分析、备份、博客平台等) 的开源软件…

Vue创建浅层响应式数据

shallowReactive&#xff1a;只处理对象第一层数据的响应式&#xff08;浅响应式&#xff09;。 shallowRef&#xff1a;只处理基本数据类型的响应式&#xff0c;不处理对象类型的响应式。 shallowReactive 适用于&#xff1a;如果有一个对象类型的数据&#xff0c;结构比较深…

SQL表、字段、查询参数获取

SQL工具类表、字段、查询参数提取 1. 执行效果2. 使用2.1 引入依赖2.2 相关实体2.3 工具类 1. 执行效果 2. 使用 2.1 引入依赖 <!-- sql 解析处理--><dependency><groupId>com.github.jsqlparser</groupId><artifactId>jsqlparser</artifact…

PLSQL工具 数据库连接名的设置

在help >>surpost info 能看到 这东西好难用啊。。不直接显示url,非要搞个名称。。

2023年A股借壳上市研究报告

第一章 借壳上市概况 1.1 定义 借壳上市作为一种独特的资本市场操作手法&#xff0c;历来是企业拓展融资渠道和实现市场战略目标的重要途径。具体来说&#xff0c;借壳上市可分为狭义与广义两种模式。在狭义的定义下&#xff0c;借壳上市是指一家已上市的公司的控股母公司&am…

React【异步逻辑createAsyncThunk(一)、createAsyncThunk(二)、性能优化、createSelector】(十二)

文章目录 异步逻辑 createAsyncThunk&#xff08;一&#xff09; createAsyncThunk&#xff08;二&#xff09; 性能优化 createSelector 异步逻辑 //Product.js const onAdd () > {const name nameRef.current.value// 触发添加商品的事件dispatch(addProduct({name…

vue3介绍

介绍 3完全兼容2的语法 vue3&#xff1a;体积更小&#xff0c;性能会更高。底层做了很多优化 2倍左右 vue3vitets 渐进式框架 vue3和vue2 的区别 新语法&#xff0c;性能上提升很多 思想是一致的&#xff1a;动态绑定&#xff1a;状态data&计算属性&#xff0c;监听某些状态…