【数据结构】快速排序(详解)

news2024/11/14 23:21:25

目录

快速排序

历史:

基本思想:

主框架:

下面解释实现单次排序的几种版本:

1.Hoare版本

2. 挖坑法

3. 前后指针法

快速排序的实现包括递归与非递归:

1. 递归实现:(即开头的基本框架)

2. 非递归:(迭代)

下面我们通过栈来演示非递归实现快速排序:


快速排序

历史:

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

基本思想:

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

主框架:

上述为快速排序递归实现的主框架,我们可以发现它和二叉树的前序遍历十分相似 

下面解释实现单次排序的几种版本:
1.Hoare版本

单趟动图演示:

步骤:

  1. 选择基准值:通常选择序列的第一个元素或最后一个元素作为基准值。

  2. 设置两个int变量记录数组下标:一个记录序列的开始的下标(left),另一个记录序列的末尾的下标(right)。

  3. 开始分区

    • 先让right++,直到找到一个比基准值大的元素。
    • 再让left--,直到找到一个比基准值小的元素。
    • 交换这两个元素的位置,并继续right++。
    • 重复上述步骤,直到left >= right。
    • 注意:right先加,保证下一步骤与基准值交换的值小于基准值
  4. 交换基准值与right位置的值:此时,基准值左边的所有元素都比基准值小,基准值右边的所有元素都比基准值大。

代码实现:

int PartSort1(int* a, int left, int right) {
	int key = left;
	while (left < right) {
		while(a[right] >= a[key]&&left < right) {
			right--;
		}
		while(a[left] <= a[key]&&left < right) {
			left++;
		}
		swap(&a[left], &a[right]);
	}
	swap(&a[key], &a[right]);
	return left;
}
2. 挖坑法

单趟动图演示:

步骤:

  1. 选择基准元素:在待排序的序列中,选择一个元素作为基准元素(key)。这个元素可以是序列的第一个元素、最后一个元素或者任意一个元素,甚至是随机选取的一个元素。

  2. 挖坑:将基准元素从原位置移除,形成一个“坑”(hole)。

  3. 填坑:从序列的一端开始(可以是左端或右端),将遇到的第一个比基准元素小(或大)的元素填入坑中,并形成一个新的坑。这里有两种情况:

    • 如果从右向左遍历,遇到比基准元素小的元素,则将其填入坑中,并继续从左向右遍历。这里代码实现选择该情况
    • 如果从左向右遍历,遇到比基准元素大的元素,则将其填入坑中,并继续从右向左遍历。
  4. 此时,基准元素左边的所有元素都小于基准元素,右边的所有元素都大于或等于基准元素。

代码实现:

int PartSort2(int* a, int left, int right) {
	int x = a[left];
	int key = left;
	while (left < right) {
		while (a[right] >= x && left < right) {
			right--;
		}
		a[key] = a[right];
		key = right;
		while (a[left] <= x && left < right) {
			left++;
		}
		a[key] = a[left];
		key = left;
	}
	a[key] = x;
	return key;
}
3. 前后指针法

单趟动图演示:

基本步骤:

  • 从待排序的数组中选择一个元素(key)作为基准。通常选择第一个或最后一个元素作为基准,但也可以随机选择。
  • 设置两个下标,一个记录序列的开始的下标(pre),即pre=left,另一个记录pre下一个元素的下标的下标(cur),即cur=pre+1;
  • 当(cur<=right)时,循环执行:cur找比key小的元素,如果a[cur]<=a[key],交换a[cur]和a[pre],否则pre++,每次循环cur++

代码实现:

int PartSort3(int* a, int left, int right) {
	int key = left;
	int pre = left;
	int cur = pre + 1;
	while (cur <= right) {
		if (a[cur] <= a[key] && ++pre != cur) {
			swap(&a[cur], &a[pre]);
		}
		cur++;
	}
	swap(&a[key], &a[pre]);
	return pre;
}
快速排序的实现包括递归与非递归:
1. 递归实现:(即开头的基本框架)
void QuickSort(int* a, int left, int right) {
	if (left >= right) {
		return;
	}
	//int key = PartSort1(a, left, right);
	//int key = PartSort2(a, left, right);
	int key = PartSort3(a, left, right);
	QuickSort(a, left, key - 1);
	QuickSort(a, key + 1, right);
}
2. 非递归:(迭代)

在非递归版本中,我们使用栈(或队列)来保存待排序的子数组的起始和结束索引,而不是直接递归调用;栈相当于前序遍历,而队列相当于层序遍历

下面我们通过栈来演示非递归实现快速排序:

思路与步骤:

  1. 循环处理栈:当栈不为空时,执行以下步骤:

  • 弹出栈顶的两个元素,它们分别表示当前子数组的起始和结束索引。
  • 如果起始索引等于结束索引(即子数组只有一个元素),那么直接跳过,不需要排序。
  • 调用PartSort3函数对当前子数组进行划分,并返回划分后的基准索引key
  • 弹出之前压入的起始和结束索引(因为已经处理过这个子数组了)。
  • 根据key的位置,决定下一步的操作:
    • 如果key等于起始索引left,说明基准左边的元素都已经排好序,只需要对基准右边的元素进行排序,所以将key + 1right压入栈中。
    • 如果key等于结束索引right,说明基准右边的元素都已经排好序,只需要对基准左边的元素进行排序,所以将leftkey - 1压入栈中。
    • 如果key既不等于left也不等于right,说明基准左右两边都有需要排序的元素,所以将leftkey-1key+1right的子数组分别压入栈中
  • 2.完成:当栈为空时,表示所有子数组都已排序完成,此时整个数组也已排序完成。

代码实现:

typedef int StackNode;
typedef struct Stack {
	StackNode* a;
	int real;
	int capacity;
}Stack;
void InitStack(Stack* p) {
	assert(p);
	p->a = NULL;
	p->capacity = p->real = 0;
}
//扩容
void expansion(Stack*p) {
	int newcapacity = p->capacity == 0 ? 4 : 2 * p->capacity;
	StackNode* tmp = (StackNode*)realloc(p->a, sizeof(StackNode) * newcapacity);
	if (tmp == NULL) {
		perror("expansion::realloc::NULL");
		exit(0);
	}
	p->a = tmp;
	p->capacity = newcapacity;
}
//入栈
void Push(Stack*p,int x) {
	assert(p);
	if (p->capacity == p->real) {
		expansion(p);
	}
	p->a[p->real++] = x;
}
//出栈
void Pop(Stack* p) {
	assert(p);
	p->real--;
}
void StackDestroy(Stack* p) {
	assert(p);
	free(p->a);
	p->a = NULL;
	p->capacity = p->real = 0;
}
//快排代码实现
void QuickSortNonR(int* a, int left, int right) {
	if (left >= right) {
		return;
	}
	Stack stack;
	InitStack(&stack);
	Push(&stack, left);
	Push(&stack, right);
	while (stack.real) {
		left = stack.a[stack.real - 2];
		right = stack.a[stack.real - 1];
		if (left == right) {
			Pop(&stack);
			Pop(&stack);
			continue;
		}
		int key = PartSort3(a, left, right);
		Pop(&stack);
		Pop(&stack);
		if (key == left) {
			Push(&stack, key + 1);
			Push(&stack, right);
			continue;
		}
		if (key == right) {
			Push(&stack, left);
			Push(&stack, key - 1);
			continue;
		}
		Push(&stack, key + 1);
		Push(&stack, right);
		Push(&stack, left);
		Push(&stack, key - 1);
	}
	StackDestroy(&stack);
}

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

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

相关文章

工程机械比例阀电流采集方案——IPEhub2与IPEmotion APP

自从国家实施一带一路和新基建计划以来&#xff0c;工程机械的需求量呈现出快速增长的趋势。而关于工程机械&#xff0c;其比例阀的控制问题不容忽视。比例阀是一种新型的液压控制装置——在普通压力阀、流量阀和方向阀上&#xff0c;用比例电磁铁替代原有的控制部分&#xff0…

KEIL5鼠标右键查找定义或声明选项变灰色不可选

原因&#xff1a;我直接点的KEIL图标打开了昨天的工程 解决办法&#xff1a;关掉工程&#xff0c;重新从文件夹的路径打开 其他原因导致试试以下方法&#xff1a; 1.快捷键F12导航到目标位置 2.路径不能含有中文&#xff0c;改好后&#xff0c;shiftAltf12&#xff0c;更新搜索…

疑惑点:动作监听时this的含义:可以理解为接口的多态

全部代码&#xff1a; package test;import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Random;public class test3 extends JFrame implements ActionListener {JButton jb1 new JButton("你点我啊&am…

纽曼硬盘隐藏文件丢失怎么恢复?介绍几种有效的方法

纽曼硬盘作为存储设备中的佼佼者&#xff0c;以其高性能和稳定性受到了广大用户的青睐。然而&#xff0c;在使用过程中&#xff0c;有时我们可能会遇到一些意想不到的问题&#xff0c;比如隐藏文件的丢失。这对于依赖这些文件进行工作或生活的人来说无疑是一个巨大的困扰。那么…

香橙派AI Pro测评--ROS及激光SLAM

文章目录 前言一、外形与质感二、软件测评1. 系统界面2. ROS安装3. ROS节点测试4. SLAM算法测试 总结 前言 今天刚收到了官方寄来的“香橙派 AI Pro”开发板&#xff0c;这篇文章将会对香橙派 AI Pro的外形、质感、运行速度进行一个测评&#xff0c;然后我会在这个开发板上安装…

四步简单操作:轻松将iCloud照片恢复到相册

随着智能手机的普及&#xff0c;我们的生活中越来越多的照片存储在了云端&#xff0c;其中iCloud提供了便捷的照片备份和存储服务。但有时候&#xff0c;我们可能会不小心删除了在iCloud上的照片&#xff0c;或者想要将iCloud中的照片恢复到手机相册中。 在这篇文章中&#xf…

I.MX6ULL Linux 点灯实验理论及汇编点灯

系列文章目录 I.MX6ULL Linux C语言开发 I.MX6ULL Linux 点灯实验理论 系列文章目录一、I.MX6ULL GPIO二、I.MX6ULL IO 命名三、I.MX6ULL IO 复用四、I.MX6ULL IO 配置五、I.MX6ULL GPIO 配置六、I.MX6ULL GPIO 时钟使能七、硬件原理分析八、实验程序编写 一、I.MX6ULL GPIO 一…

flutter 的webview中touchstart和touchend 执行异常问题解决

效果 背景 使用flutter 调用webview内网页&#xff0c;网页内容是监听touchstart和 touchend&#xff0c;触发不同是事件&#xff0c;但是发现每次长按都 手指抬起后 才会执行 touchstart和touchend&#xff0c;满足不了我的需求&#xff0c;我的需求是当手指按下 立即执行touc…

aws emr启动standalone的flink集群

关键组件 Client&#xff0c;代码由客户端获取并做转换&#xff0c;之后提交给JobMangerJobManager&#xff0c;对作业进行中央调度管理&#xff0c;获取到要执行的作业后&#xff0c;会进一步处理转换&#xff0c;然后分发任务给众多的TaskManager。TaskManager&#xff0c;数…

解决mybatis/mybatis plus报错:Invalid bound statement (not found) 的方法汇总

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)问题&#xff0c;即在mybatis中dao接口与mapper配置文件在做映射绑定的时候接口与xml不匹配&#xff0c;要么是找不到&#xff0c;要么是找到了却匹配不到。 我的问题是项目没有把最新的方法x…

操作系统实验--终极逃课方法

找到图片里的这个路径下的文件 &#xff0c;结合当前题目名称&#xff0c;把文件内容全部删除&#xff0c;改为print print的内容为下图左下角的预期输出的内容

Databend 开源周报第 146 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 支持 Expressio…

Rocky Linux 9.4 正式版发布 - RHEL 100% 1:1 兼容免费发行版

Rocky Linux 9.4 正式版发布 - RHEL 100% 1:1 兼容免费发行版 Rocky Linux 由 CentOS 项目的创始人 Gregory Kurtzer 领导 请访问原文链接&#xff1a;Rocky Linux 9.4 正式版发布 - RHEL 100% 1:1 兼容免费发行版&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处…

20212313 2023-2024-2 《移动平台开发与实践》第5次作业

20212313 2023-2024-2 《移动平台开发与实践》第5次作业 1.实验内容 设计并开发一个地图应用系统。 该实验需提前申请百度API Key&#xff0c;调用接口实现百度地图的定位功能、地图添加覆盖物和显示文本信息。 2.实验过程 2.1 获取SHA1 &#xff08;1&#xff09;打开控制台…

【Python编程】给电脑安装最新的 Python3.12.3

笔者最近更换了新的Win11系统&#xff0c;安装最新的Python版本&#xff08;3.12.3&#xff09;尝尝鲜。据说这个版本存在一些漏洞&#xff0c;笔者将后续更新编程过程中的相关问题&#xff08;如果有&#xff09;。Python3.12.3的安装过程比较简单&#xff0c;在此进行说明。 …

mac下载安装好软件后提示已损坏

mac下载安装好软件后提示已损坏 解决方法&#xff1a; 首先确保系统安全设置已经改为任何来源。 打开任何来源后&#xff0c;到应用程序目录中尝试运行软件&#xff0c;如果仍提示损坏&#xff0c;请在应用图标上&#xff0c;鼠标右键&#xff0c;在弹出菜单中点打开。 如果…

【Pandas】深入解析`pd.read_pickle()`函数

【Pandas】深入解析pd.read_pickle()函数 &#x1f308; 欢迎莅临我的个人主页&#x1f448;这里是我深耕Python编程、机器学习和自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;并乐于分享知识与经验的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&…

WordPress子比主题美化-首页动态的图片展示

WordPress子比主题首页动态的图片展示 WordPress子比主题首页添加动态的图片展示&#xff0c;其他程序也可以用&#xff0c;复制代码到相应位置即可&#xff0c;也可作为指定分类&#xff0c;重点内容等&#xff0c;可以适合各个场景&#xff0c;需要的自取。 图片展示: 教程…

如何搭建个人观测云平台

如何搭建个人观测云平台 安装DataKit什么是DataKit&#xff1f; 仪表板指标管理监控 开通阿里云观测云服务后&#xff0c;在观测云平台页面进行下面的操作。 安装DataKit 什么是DataKit&#xff1f; DataKit 是观测云官方发布的数据采集应用&#xff0c;支持上百种数据的采集…