排序篇——递归实现快速排序(hoare版-挖坑法-前后指针版)

news2024/11/24 3:08:43

目录

前言

一、key?

二、思路及代码实现

1.hoare版

2.挖坑法

3.前后指针版本

总结


前言

        快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法。它会选出一个基准值(key),把它放到正确的位置(排序之后的位置)。


提示:以下是本篇文章正文内容,下面案例可供参考

以下所有排序均以升序为例 

一、key?

        快速排序中有一个基准值,一般在待排序元素的最左边或最右边,看个人喜好。此次以最左边为例做key。把key位置代表的元素放到正确的位置。这是排序的第一步。并且这个位置的将左边比5小,右边比5大。

二、思路及代码实现

1.hoare版

        看这个动图很容易就能看出这个思路,右边先走,找比key小的位置,找到比key小的位置停止,之后左边再走找比key大的位置,找到后两者交换,右边继续走重复上述操作。当两者相遇那么就找到key的位置了,将key的值放到key位置里。(左边找大,右边找小 降序降序)。这是这个排序的单趟排序。这样这个数组比6小的都在左边,比6大的都在右边.

  结论: 左边做key右边先走,右边做key左边先走。  相遇位置一定比key小

        下面就是排序的单趟,这里有一些坑,需要大家注意。

       如果是 1 2 3 4 5 右边找小的情况下right会一种减直到越界访问,所以要带上 left < right。如果是 5 1 2 3 4 左边找小的情况下left会一种加,直到越界访问,所以要带上left < right。同时左边找小的情况下如果写成a[left] == a[key]的那么left再还未走到下一个位置,就和right位置进行了交换,那么key的位置直接被打乱了。左边的位置一定是要走到下一个位置之后再找小。

void quick_sort(int* a, int left, int right)
{
	int key = left;
	while (left < right)
	{
		//右边找小
		while (left < right && a[right] >= a[key])
		{
			right--;
		}
		//左边找大
		while (left < right && a[left] <= a[key])
		{
			left++;
		}
		swap(&a[left], &a[right]);	//	一轮过后进行交换
	}
	swap(&a[key], &a[left]);	//这里,left和right相遇了,进行交换。代码写key和right交换也可以
}

         进行完成单趟之后,key的位置就确定了,后面我们以key分为界限分左右两个部分,根据这两个部分继续把key放在正确的位置,左右再分左右,用递归的方法进行找key,直到排序完成。

        实现递归要满足的两个条件:1.递归有一个界限,当满足这个界限停止递归,进行返回。2.每次向后递归都会接近这个界限。 

        看到单趟排序,和递归实现图,一个就有了一个思路了。那么代码怎么实现?返回递归的界限是什么?怎么递归?

        这个图是不是有点像二叉树。我们递归需要套两个自身的函数。一个左,一个右。先走左边再走右。有点前序遍历的味道。那么递归结束的条件是什么呢?当左右相等,那么只有一个数或者左边的下标大于右边的下标也代表这个数以及放在固定的位置上了,递归结束返回。

void quick_sort(int* a, int left, int right)
{
	if (left >= right)
	{
		return 0;
	}
	int begin = left, end = right;//	储存最左边和右边的值
	int key = left;
	while (left < right)
	{
		//右边找小
		while (left < right && a[right] > a[key])
		{
			right--;
		}
		//左边找大
		while (left < right && a[left] <= a[key])
		{
			left++;
		}
		swap(&a[left], &a[right]);	//	一轮过后进行交换
	}
	swap(&a[key], &a[left]);	//这里,left和right相遇了,进行交换。代码写key和right交换也可以
	key = left;	//left的值代表key的下标,但key现在依然为0所以将left现在的值交给key。
	quick_sort(a, begin, key - 1);//先走左边
	quick_sort(a, key + 1, end);//再走右边
}

        这里的排序还有一个点,key的正确位置越接近中间,越接近满二叉树,越接近满二叉树,左右两边的深度越均匀,越均匀,这个排序的时间越少。假如一串数越接近有序,那么当key 再靠左或靠右的位置上,那么它的深度越深。当这串数越多越明显。所有就有大佬想出了一种方法,我们随机一个位置,让这个位置的值与key的值相交换,那么就可以让排序的深度可能性的更好一些,有的大佬感觉随机选key这个不好,有概率。所以又有了一个叫三数取中。从最左边,最右边,中间去不是最小也不是最大的值。下面是一些优化。

int get_(int* a, int left, int right)//三目取中判断
{
	int middle = (left + right) / 2;
	if (a[left] < a[middle])
	{
		if (a[middle] < a[right])
		{
			return middle;
		}
		else if (a[left] > a[right])
		{
			return left;
		}
		else
		{
			return right;
		}
	}
	else
	{
		if (a[middle] > a[right])
		{
			return middle;
		}
		else if (a[left] < a[right])
		{
			return left;
		}
		else
		{
			return right;
		}
	}
}
void quick_sort(int* a, int left, int right)
{
	if (left >= right)
	{
		return 0;
	}
	int begin = left, end = right;//	储存最左边和右边的值
	int key = left;
	//随机选key
	int randl = left + (rand() % (right - left));
	swap(&a[left], &a[randl]);
	//三目取中
	//int middle = get_(a, left, right);
	//swap(&a[left], &a[middle]);
	while (left < right)
	{
		//右边找小
		while (left < right && a[right] > a[key])
		{
			right--;
		}
		//左边找大
		while (left < right && a[left] <= a[key])
		{
			left++;
		}
		swap(&a[left], &a[right]);	//	一轮过后进行交换
	}
	swap(&a[key], &a[left]);	//这里,left和right相遇了,进行交换。代码写key和right交换也可以
	key = left;	//left的值代表key的下标,但key现在依然为0所以将left现在的值交给key。
	quick_sort(a, begin, key - 1);//先走左边
	quick_sort(a, key + 1, end);//再走右边
}

2.挖坑法

         挖坑法也很好懂。将key值先取出来,形成一个坑位,之后左边找大,右边找小,与上面的交换不同,这里是找到之后将当前位置的值放到坑位中,自己的位置就形成了一个新的坑位。当两者相遇那么把key放到相遇位置的坑(它们相遇一定在坑上相遇)。

        大方向和hoare是一样的,都是比key小的在左边,比key大的在右边。

         这里要主要key代表的是key的值,而hole才代表坑的位置。并且这里的key值也可以像hoare版的那些进行优化,这里就不作过多说明,hoare中有详细讲解。

//挖坑法
void gouge_pit(int* a, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	int begin = left, end = right;

	int hole = left;	//坑的位置值
	int key = a[left];	//保存key的值

	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;	//将key放到正确的位置
	gouge_pit(a, begin, hole - 1);
	gouge_pit(a, hole + 1, end);
}

3.前后指针版本

        放置两个指针,可以称之为下标。一前一后,这里我们让cur在前,prev在后。

        cur的作用是找小,比key值大,++cur向前走,走完再次比较,如果cur位置的值比key小那么,++prev之后,cur和prev位置的值进行交换 ,cur在向前走,依此类推,直到cur越界,此时prev位置的值就是key的正确位置。两者进行交换。单趟排序完成。

//前后指针排序
void ahead_after(int* a, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	//随机选key
	int randl = left + (rand() % (right - left));
	swap(&a[left], &a[randl]);

	int key = left;
	int prev = left;
	int cur = left + 1;


	while (cur <= right)
	{
		//r如果满足第一个条件,注意这是前置++prev,把prev加过的值与cur进行比较。
		// 如果我们在同一个位置那么为假跳过if,代码是从右向左走的,这样prev已经加过了,之后在比较。
		if (a[cur] < a[key] && ++prev != cur)
		{
			swap(&a[cur], &a[prev]);
		}
		cur++;
	}
	swap(&a[prev], &a[key]);
	ahead_after(a, left, prev - 1);
	ahead_after(a, prev + 1, right);
}

总结

        1.排序左边做key右边先走,右边做key左边先走。  相遇位置一定比key小

        2.key的正确位置越接近中间,越接近满二叉树,越接近满二叉树,左右两边的深度越均匀,越均匀,这个排序的时间越少。

        3.key的值可以优化,有两种方法。

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

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

相关文章

c语言学习,tolower ()函数分析

1&#xff1a;tolower() 函数说明&#xff1a; 检查参数c&#xff0c;为大写字母返回对应的小写字母 2&#xff1a;函数原型&#xff1a; int toascii(int c) 3&#xff1a;函数参数&#xff1a; 参数c&#xff0c;为检测整数 4&#xff1a;返回值&#xff1a; 返回转换的小…

【Python】生成二维迷宫的算法

前言 哈里最近因为一个小插曲打算写一个设计迷宫的算法。为了锻炼脑力&#xff0c;特地没有上网搜索而是自己摸索出一个迷宫设计算法。 概述 1、需求 哈里准备实现下图的迷宫。 2、分析 可以看到&#xff0c;图里凡是x和y坐标为单数时&#xff0c;总是白色。于是哈里得到下…

二分查找专题(总)

1、经典二分查找模板 int search(vector<int>& nums, int target) {int right nums.size() - 1;int left 0;while(left < right){int mid (left right)/2;if(nums[mid] > target){right mid-1;}else if(nums[mid] < target){left mid1;}else {return…

c语言-经典例题

C语言-经典例题 一、单项选择题 1、 -- A 2、 -- C y<5 --是关系运算符的优先级大于&& -- 是逻辑运算符 3、 -- B - D选项&#xff1a;c是float类型&#xff0c;所以c/2是1.5 4、 -- C 从后往前执行&#xff08;先算后面的&a…

【uniapp】vue3+vite配置tailwindcss

安装 npm install autoprefixer tailwindcss uni-helper/vite-plugin-uni-tailwind -Dautoprefixer &#xff1a;自动管理浏览器前缀的插件&#xff0c;可以解析css文件并且添加前缀到css内容里。uni-helper/vite-plugin-uni-tailwind: 将 Tailwind CSS 框架集成到使用 Vite 作…

linux系统编程:多任务编程(进程1)

1.进程 进程:(进行中的程序)--正在运行的程序 (动态的) ---内存 程序的一次执行过程&#xff01; 一个程序一个程序 可以 对应多个进程 程序 -- a.out (可执行程序) ---静态的 程序 加载 到内存 运行起来 成为了 进程。 进程是 程序运行的实体。 程序 数据代码 2.进…

背包九讲(求方案数,求具体方案数,有依赖背包)

文章目录 求方案数基本思路代码 背包问题求具体方案基本思路代码 有依赖背包基本思路代码 求方案数 问题描述&#xff1a; 给定n nn个物品&#xff0c;以及一个容量大小为m mm的背包&#xff0c;然后给出n nn个物品的体积及价值&#xff0c;求背包最大价值是多少&#xff0c;也…

递归排序 归并排序 快排

递归 求中点 midL(R-L)/2 midL((R-L)>>1)右移一位更快 子问题等量 mast er公式 T(N) a*T(N/b)O(N^d) T(N):母 T(N/b)&#xff1a;子 &#xff08;是否等量&#xff09; a&#xff1a;调用多少次 O(N^d)&#xff1a;除去子变量之外的时间复杂度 子问题等量 上面…

sqli-labs-master靶场通关

目录 一、sqli-labs第一关 1.判断是否存在sql注入 &#xff08;1&#xff09;提示输入数字值的ID作为参数&#xff0c;输入?id1 &#xff08;2&#xff09;通过数字值不同返回的内容也不同&#xff0c;所以我们输入的内容是带入到数据库里面查询了 &#xff08;3&#xff0…

信号量和管道

一、信号量 实现模拟售票问题&#xff1a; 1、信号量的机制&#xff1a;描述可使用资源的个数。 &#xff08;1&#xff09;P操作&#xff1a;表示使用这个资源&#xff0c;资源个数减一 逻辑&#xff1a;尝试获取资源——有资源可用直接使用 | 无资源可用等待 如果信号量的…

面向新人的 Java 面试问题(101-150)

101.什么是多态&#xff1f; 多态性被定义为能够采用多种形式的能力。它有两种类型&#xff0c;即编译时多态性或方法重载 - 在编译时调用的函数。例如&#xff0c;以“面积”类为例。根据参数数量&#xff0c;它可以计算正方形、三角形或圆形的面积。运行时多态性或方法覆盖 …

【详细】linux 打包QT程序

【详细】linux 打包QT程序 一. 安装linuxdeployqt1.1 下载linuxdeployqt源码并修改如下 二. 安装patchelf三. 打包appimage四. 打包成 Debian包4.1 control文件内容4.2 postinst文件内容4.3 postrm文件内容4.4 打包命令4.4 安装命令4.5 卸载命令 一. 安装linuxdeployqt 下载地…

【Redis】Redis 数据类型与结构—(二)

Redis 数据类型与结构 一、值的数据类型二、键值对数据结构三、集合数据操作效率 一、值的数据类型 Redis “快”取决于两方面&#xff0c;一方面&#xff0c;它是内存数据库&#xff0c;另一方面&#xff0c;则是高效的数据结构。 Redis 键值对中值的数据类型&#xff0c;也…

被极氪“背刺”新能源汽车车主,你可以说不

文 魏强 导语&#xff1a;谁也改变不了不断被“背刺”的命运。 8月13日晚&#xff0c;极氪召开发布会主推两款改款车——2025款极氪007升级了第二代金砖电池&#xff08;磷酸铁锂&#xff09;、标配了激光雷达和两颗Orin X芯片、全系降价2-3万元。2025款极氪001智驾芯片从Mob…

Artifactory集成LDAP示例

LDAP在企业软件身份认证中起到了非常关键的作用&#xff0c;给企业内用户带来了非常多的便利&#xff0c;JFrog 平台支持针对开箱即用的 LDAP 服务器对用户进行身份验证。下面我们一起看下Artifactory如何集成LDAP&#xff0c;本示例以OpenLDAP为例。 1.快速安装OpenLDAP doc…

SqlSugar详解-国产ORM框架

ORM (Object-Relational Mapping) 概念 ORM 是一种程序技术&#xff0c;用于将关系型数据库中的数据映射到对象上。 主要目的是简化数据库操作&#xff0c;使得开发人员可以像操作对象一样来操作数据库。 原理 数据表与类的映射&#xff1a;数据库中的表对应为类。 记录与对象…

灵办AI助手Chrome插件全面评测:PC Web端的智能办公利器

探索灵办AI助手在Mac OS上的高效表现&#xff0c;支持多款主流浏览器&#xff0c;助你轻松应对办公挑战 文章目录 探索灵办AI助手在Mac OS上的高效表现&#xff0c;支持多款主流浏览器&#xff0c;助你轻松应对办公挑战摘要引言开发环境介绍核心功能评测1. 网页翻译与双语对照 …

【祖孙询问】

问题 代码 #include <bits/stdc.h> using namespace std; const int N 4e410; vector<int> edge[N]; int p[N][20], d[N]; void dfs(int from, int u) {for(auto to : edge[u]){if(to from) continue;d[to] d[u] 1;p[to][0] u;dfs(u, to);}} void init() {fo…

mysql主从同步遇到的问题

1&#xff0c;主库data文件复制到从库&#xff0c;之后主库要同步的实例data一定不要在修改&#xff1b; 1.1&#xff0c;修改之后就要重新覆盖一遍 2&#xff0c;如果状态不对&#xff1a;一定要查看日志&#xff1b;比如slave_io_state是空时&#xff0c;需要查看日志 2.1&a…

使用百度文心智能体创建AI旅游助手

百度文心智能体平台为你开启。百度文心智能体平台&#xff0c;创建属于自己的智能体应用。百度文心智能体平台是百度旗下的智能AI平台&#xff0c;集成了先进的自然语言处理技术和人工智能技术&#xff0c;可以用来创建属于自己的智能体应用&#xff0c;访问官网链接&#xff1…