[数据结构基础]排序算法第二弹 -- 选择排序、堆排序和冒泡排序

news2024/11/26 22:24:05

目录

一. 选择排序

1.1 选择排序的实现思路

1.2 选择排序函数代码 

1.3 选择排序的时间复杂度分析

二. 堆排序

2.1 堆排序的实现思路

2.2 堆排序函数代码

2.3 堆排序的时间复杂度分析

三. 冒泡排序

3.1 冒泡排序的基本思想

3.2 冒泡排序函数代码

3.3 冒泡排序的时间复杂度分析


前置说明:本文中所有排序算法均以排升序为例进行讲解。

一. 选择排序

1.1 选择排序的实现思路

选择排序,就是在一组数据中通过选出最大值(最小值的方法进行排序),假设要对具有n个数据的数组arr中的数据进行排序,具体实现步骤为:

  1. 取整个数组中的数据为一数据集,设begin = 0为头部数据下标,end = n - 1为尾部数据的下标,在数据集中找出最大的数据和最小的数据,排升序,最大的数据与尾部数据交换,最小的数据与头部数据交换。
  2. 经过上步,头部数据为数组中的最小值,尾部数组为数组中的最大值。取此时数组中的第2个数据到第n-1个数据为一数据集,设begin = 1为头部数据下标,end = n - 2为尾部数据的下标,最大的数据与尾部数据交换,最小的数据与头部数据交换。
  3. 重复进行步骤2,直到begin >= end时终止。

如,对arr[6] = {3,4,0,5,1,6}排升序的过程为:

  1. begin = 0,end = 5,数据集为:{3,4,0,5,1,6},最大为6,最小为0,arr[6]={0,4,3,5,1,6}。
  2. begin = 1,end = 4,数据集:{4,3,1,5},最大为4,最小为1,arr[6] = {0,1,3,4,5,6}。
  3. begin = 2,end = 3,数据集:{3,4},最大为4,最小为3,arr[6] = {0,1,3,4,5,6}。
  4. begin = 3,end = 4,排序完成。
图1.1 选择排序过程图解

1.2 选择排序函数代码 

//数据交换函数
void swap(int* px, int* py)
{
	int tmp = *px;
	*px = *py;
	*py = tmp;
}

//选择排序函数
//参数a为指向存储待排序数组首元素的指针,n为待排序元素的个数
void SelectSort(int* a, int n)
{
	//选择排序
	//每次筛选出未排序数据中最大和最小的那个,存在数组的前部和后部
	int begin = 0;  //前部数据下标
	int end = n - 1;  //后部数据下标

	while (begin < end)
	{
		int mini = begin;   //初始化最小的数据的下标
		int maxi = begin;   //初始化最大的数据的下标

		int i = 0;  //循环参数
		//for循环遍历每一个数据,找出最大的数据和最小的数据的下标
		for (i = begin + 1; i <= end; ++i)
		{
			if (a[i] > a[maxi])
			{
				maxi = i;
			}

			if (a[i] < a[mini])
			{
				mini = i;
			}
		}

		//将最小的数据放在队首,队首数据放在原最小数据位置处
		//将最大的数据放在队尾,队尾数据放在原最大数据位置处
		swap(&a[begin], &a[mini]);
		if (maxi == begin)
		{
			//如果首元素是最大的元素,那么swap(&a[begin], &a[mini])使队首数据不再是最大的数据
			//此时最大的数据被换到下标为mini的位置,更新maxi为mini
			maxi = mini;
		}
		swap(&a[end], &a[maxi]);

		--end;
		++begin;
	}
}

1.3 选择排序的时间复杂度分析

假设要对n个数据进行选择排序,总共要进行n/2次最大值最小值筛选操作,第一次筛选要遍历n个数据,第二次筛选要遍历n-2个数据,...,最后一次筛选要遍历2个数据或3个数据,设遍历一个数据就表示进行一次操作,总共要进行的操作次数为(假设最后一次遍历2个数据):

F(N) = N + (N-2)+(N-4) +...+4+2=\frac{1}{2}N^2+N

根据大O渐进法规则,选择排序的时间复杂度为O(N^2)

注:当待排序数据为偶数个时,最后一次筛选要遍历2个数据,当待排序数据为奇数个时,最后一次筛选要遍历3个数据。

二. 堆排序

2.1 堆排序的实现思路

首先,要明确对于排升序和排降序,应该建大堆还是小堆,记住结论:

  • 排升序,建大堆
  • 排降序,建小堆

堆排序的操作流程如下(排升序):

  • 建大堆,这里不再新开辟空间来建堆,而是将给定数据的数据顺序调整为满足大堆结构的排列。采用向下调整的方法来进行数据调整,从最后一个非叶子节点(度不为0的节点)开始向下调整,调整到根节点结束,此时数据的排列顺序已满足大堆的结构要求。
  • 交换首尾节点的数据值,此时末尾节点为堆中的最大数据。
  • 将末尾的节点排除出堆,从根节点开始,对堆进行向下调整操作。
  • 重复步骤2和步骤3,直到堆中仅剩一个数据为止。

2.2 堆排序函数代码

//数据交换函数
void swap(DataType* px, DataType* py)
{
	int tmp = *px;
	*px = *py;
	*py = tmp;
}
 
//向下调整函数
//a为存储待排序数据的数组,n为待排序数据的个数
void Adjustdown(DataType* a, int n, int parent)
{
	assert(a);
	int child = 2 * parent + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child] < a[child + 1])
			++child;
 
		if (a[parent] < a[child])
		{
			swap(&a[parent], &a[child]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}
}
 
//堆排序函数(升序)
void HeapSort(DataType* a, int n)
{
	assert(a);
 
	//先采用向下调整的方式将a中的数据建立为大堆
	//从后往前调整,叶子节点不用单独调整
	//因此,从第一个度不为0的节点开始往前调整到第一个节点即可
	int end = (n - 1 - 1) / 2;
	while(end >= 0)
	{
		Adjustdown(a, n, end);
		--end;
	}
 
	//将a排为大堆后
	//将a的数据首尾交换,排除最后一个节点,将堆进行向下调整
	//重复进行上述操作n-1次,堆(数组)中的数据变为升序
	end = n - 1;
	while (end)
	{
		swap(&a[0], &a[end]);
		Adjustdown(a, end, 0);
		--end;
	}
}

2.3 堆排序的时间复杂度分析

对N个数据进行堆排序,要先后进行建堆操作和向下调整操作。

建堆操作的时间复杂度

在堆排序中,采用向下调整的方法进行建堆。从倒数第二层开始将每个节点向下调整,假设层数为h,倒数第二层最多进行下调1次下调操作,倒数第三层最多进行2次下调操作,...,第二层最多进行h-2次下调操作,第一层最多进行h-1次下调操作,每层节点数和层数的关系为:n = 2^{h-1}n为第h层节点数。综上,通过向下调整建堆要进行调整的次数最多为:

F(N)=2^{h-2}\times 1+2^{h-3}\times2+2^{h-4}\times3...+2^1\times (h-2)+2^0\times(h-1)        (1)

2\times F(N)=2^{h-1}\times 1+2^{h-2}\times 2+2^{h-3}\times3+....+2^2\times(h-2)+2^1\times(h-1)     (2)

(2)-(1)得:

F(N)=2^{h-1} +2^{h-2}+2^1-h+1=\frac{2-2^{h}}{1-2}-h+1=2^{h}-h-1

根据二叉树结构的性质:N=2^h-1h=log(N+1)

因此:F(N)=N-log(N+1)

根据大O渐进法,建堆操作的时间复杂度为:O(N)

向下调整的时间复杂度

堆排序的基本思想是在建立堆之后交换首尾节点数据,然后向下调整根节点数据来实现的,假设堆中共有N个数据,总共要对N-1个数据向下调整,设二叉树的层数为h,每次向下调整最多调整h-1次,总共进行的向下调整操作的最多次数为:

F(N)=(N-1)\times h = (N-1)log(N+1)

根据大O渐进法,堆排序中调整过程的时间复杂度为:O(NlogN)

整个堆排序的时间复杂度

整个堆排序要进行建堆操作和向下调整操作,时间复杂度记O(N+NlogN),根据大O渐进法规则,堆排序时间复杂度可表示为:O(NlogN)

三. 冒泡排序

3.1 冒泡排序的基本思想

冒泡排序的基本思想是交换,通过相邻两数的交换,达到排升序(降序)的目的,给定含有n个数据的数组arr,对arr中的数据排升序的操作步骤为:

  1. 从arr中的第二个数据开始到最后一个数据,每个数据与它前面的那个数据进行比较,如果前面的数据大于后面的数据,则交换两个数据,经过这一趟排序,数组中最后面的数为最大的数据。
  2. 从数组中第二个数据开始到倒数第二个数据,每个数据与它前面的那个数据进行比较,如果前面的数据大于后面的数据,则交换两个数据,此时数组中最后一个数据和倒数第二个数据分别为最大的数据和次大的数据。
  3. 重复步骤1和2,直到完成排序。
图3.1 冒泡排序的第一趟排序实现流程

图3.1展示了对数组arr[6] = {6,5,4,3,2,1}的第一趟排序的实现过程,经过一趟排序,arr中最大的值到了最后面的位置,第二趟排序对arr中的前5个数据进行相同的操作即可,第二趟排序结束后arr应当变为arr[6]={4,3,2,1,5,6}。

  • 冒泡排序的优化:

设想,如果单趟排序未发生数据交换,那就表示数据已经有序,数据有序就可以终止循环。因此,可以定义一个变量exchange来表示单趟排序是否发生数据交换,可以取exchange=1表示发生了数据交换,exchange=0表示没发生数据交换,每结束一次单趟循环都对exchange的值进行检查,如果exchange==0成立,则排序终止。

3.2 冒泡排序函数代码

//冒泡排序函数
void bubbleSort(int* a, int n)
{
	int i = 0;  //循环参数,表示冒泡排序趟数
	int j = 0;  //循环参数,表示下标

	for (i = 0; i < n - 1; ++i) //单趟排序
	{
		int exchange = 0;   //数据交换标识符,如果单趟排序发生数据交换为1,否则为0
		for (j = 0; j < n - 1 - i; ++j)
		{
			//排升序,如果前面的数大于后面则交换
			if (a[j] > a[j + 1])
			{
				exchange = 1;
				int tmp = a[j];
				a[j] = a[j + 1];
				a[j + 1] = tmp;
			}
		}

		//如果单趟排序未发生数据交换flag=0,则表示数据已经有序,终止排序即可
		if (0 == exchange)
		{
			break;
		}
	}
}

3.3 冒泡排序的时间复杂度分析

设待排序的数据个数为N,总共要进行N-1趟冒泡排序,假设冒泡排序过程中不发生break终止循环,第一趟冒泡排序最多进行N-1次数据交换,第二趟冒泡排序最多进行N-2次数据交换,...,最后一趟冒泡排序最多进行1次数据交换,因此,总共要进行数据交换的次数为:

F(N)=(N-1)+(N-2)+...+2+1=\frac{(N-1)(N-1+1)}{2}=\frac{1}{2}N^2-\frac{1}{2}N

根据大O渐进法规则,冒泡排序的时间复杂度为O(N^2)

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

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

相关文章

【微服务】Gateway统一网关

更多内容点击查看微服务学习专栏 一.引入 我们为什么需要网关&#xff1f; 当我们所有的服务摆在那里允许任何人发送请求访问是不是不太安全&#xff1f; 不是所有的业务都是对外公开的&#xff01; Gateway网关是我们服务的守门神&#xff0c;是所有微服务的统一入口&…

机器自动翻译古文拼音 - 十大宋词 - 桂枝香 金陵怀古 王安石

桂枝香金陵怀古 北宋王安石 登临送目&#xff0c;正故国晚秋&#xff0c;天气初肃。 千里澄江似练&#xff0c;翠峰如簇。 归帆去棹斜阳里&#xff0c;背西风&#xff0c;酒旗斜矗。 彩舟云淡&#xff0c;星河鹭起&#xff0c;画图难足。 念往昔、繁华竞逐&#xff0c;叹门外…

【Node.js实战】一文带你开发博客项目之初识Express(安装Express,处理路由,中间件机制)

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;也会涉及到服务端 &#x1f4c3;个人状态&#xff1a; 在校大学生一枚&#xff0c;已拿多个前端 offer&#xff08;秋招&#xff09; &#x1f680;未…

JAVA SE复习(第2章 Java基础语法)

本文笔记来自硅谷柴林燕老师的笔记 只为自己看笔记方便使用 不做他用第2章 Java基础语法2.1 注释&#xff08;annotation&#xff09;&#xff08;掌握&#xff09;注释&#xff1a;就是对代码的解释和说明。其目的是让人们能够更加轻松地了解代码。为代码添加注释&#xff0c;…

2023年web类第一期总结

&#x1f340;本人简介&#xff1a; 吉师大一最爱逃课的网安混子、 华为云享专家、阿里云专家博主、腾讯云自媒体分享计划博主、 华为MindSpore优秀开发者、迷雾安全团队核心成员&#xff0c;CSDN2022年运维与安全领域第15名 &#x1f341;本人制作小程序以及资源分享地址&am…

计算机相关专业混体制的解决方案(国企之银行与券商)

文章目录1、各大银行1.1 银行的分类1.2 信息科技部&#xff08;工作内容&#xff0c;招聘条件&#xff0c;待遇&#xff09;2、各大券商2.1 证券公司待遇2.1 证券公司要求3、其他金融3.1 保险公司3.2 其他金融国企之银行与券商的适用对象&#xff1a; 如果你技术好&#xff0c;…

队列--专题讲解

文章目录模拟队列queue头文件定义基本操作循环队列queue优先队列priority_queue银行大厅排队训练挑战模拟队列 先进先出 队列大概模样 代码截屏&#xff1a; queue 头文件 #include <queue>定义 //队列 queue<int> q;基本操作 //向队尾插入一个元素q.push()…

24考研数学每日一题(带解析)

题目来源于武老师的每日一题&#xff0c;答案是自己做的&#xff0c;不太严谨&#xff0c;仅供参考 2022年11月1日 知识点&#xff1a;函数定义域 答案&#xff1a; 函数定义域是指自变量x的取值范围&#xff0c;不可以把x1作为自变量&#xff0c;x才是自变量&#xff0c;同…

高级数据结构:树状数组详解(c++实现 求解动态数组区间和问题)

文章目录引入树状数组c完整代码引入 什么是树状数组&#xff1f;&#xff1f;&#xff1f; 解决数据压缩里的累积频率&#xff08;Cumulative Frequency&#xff09;的计算问题&#xff0c;现多用于高效计算数列的前缀和&#xff0c; 区间和&#xff0c;这样的问题可以使用树…

蓝桥杯重点(C/C++)(随时更新,更新时间:2023.1.27)

点关注不迷路&#xff0c;欢迎推荐给更多人 目录 1 技巧 1.1 取消同步&#xff08;节约时间&#xff0c;甚至能多骗点分&#xff0c;最好每个程序都写上&#xff09; 1.2 万能库&#xff08;可能会耽误编译时间&#xff0c;但是省脑子&#xff09; 1.3 蓝桥杯return 0…

深度学习:CSPNet

深度学习&#xff1a;CSPNet前言解决的问题Method目前主流网络存在的问题Partial Dense BlockPartial Transition LayerExact Fusion Model实验前言 CSPNet 是作者 Chien-Yao Wang 于 2019 发表的论文 CSPNET: A NEW BACKBONE THAT CAN ENHANCE LEARNING CAPABILITY OF CNN。也…

【C语言】数据结构-单链表

主页&#xff1a;114514的代码大冒险 qq:2188956112&#xff08;欢迎小伙伴呀hi✿(。◕ᴗ◕。)✿ &#xff09; Gitee&#xff1a;庄嘉豪 (zhuang-jiahaoxxx) - Gitee.com 文章目录 目录 文章目录 前言&#xff08;链表的优势&#xff09; 一、单链表是什么 二、单链表操作…

Hadoop基础之《(2)—Hadoop概述》

一、Hadoop是什么 1、Hadoop是一个由Apache基金会所开发的分布式系统基础架构。 2、主要解决&#xff0c;海量数据的存储和海量数据的分析计算问题。 3、广义上来说&#xff0c;Hadoop通常是指一个更广泛的概念—Hadoop生态圈。 二、Hadoop的三大发行版本 Apache版本&#x…

【论文简述】Long-range Attention Network for Multi-View Stereo(WACV 2021)

一、论文简述 1. 第一作者&#xff1a;Xudong Zhang 2. 发表年份&#xff1a;2021 3. 发表期刊&#xff1a;WACV 4. 关键词&#xff1a;MVS、注意力、级联、监督回归 5. 探索动机&#xff1a;先前的方法忽略了像素之间的依赖关系&#xff0c;并且期望回归的方式效率不高。…

Kettle(10):switch/case组件

1 机智的体育老师——条件判断 有一天,体育老师要让班上的男女同学分别排成两队。但这个班上还有几名同学,很特殊——他们是蜘蛛!!所以,机智的体育老师需要把他们排成三队,男同学一队,女同学一队,蜘蛛一队。 体育老师要做一件非常重要的事情:判断学生是男孩还是女孩、…

联想E14 开机黑屏报错2102:Detection error on HDD0 (Main HDD)

环境&#xff1a; 联想E14 Win10专业版 问题描述&#xff1a; 联想E14 开机黑屏报错2102&#xff1a;Detection error on HDD0 (Main HDD) 解决方案&#xff1a; 1.关机重启 2.尝试拆解&#xff0c;拔出硬盘数据线&#xff0c;重新安装

Scanpy 单细胞基因分析

参考&#xff1a;https://www.bilibili.com/video/BV1sq4y1C7Qx/ https://scanpy-tutorials.readthedocs.io/en/latest/pbmc3k.html 代码下载&#xff1a;scanpy分析scRNA-seq数据基本流程&#xff08;含scanpy seurat两大工具对比&#xff09; 链接: https://pan.baidu.com/s…

华为机试 HJ33 整数与IP地址间的转换

华为机试题 HJ33 整数与IP地址间的转换 一、题目描述 描述原理&#xff1a;ip地址的每段可以看成是一个0-255的整数&#xff0c;把每段拆分成一个二进制形式组合起来&#xff0c;然后把这个二进制数转变成一个长整数。举例&#xff1a;一个ip地址为10.0.3.193每段数字 …

C# - JSON Schema validation

C# - JSON Schema validation引言如何生成 C# 类 JSON Schema利用在线工具利用 Visual Studio利用 NJsonSchema验证 JSON Schema针对 JSON Schema 字符串针对 C# 类 Schema引言 针对 API 测试&#xff0c;我们需要验证 Response 中的一些字段类型&#xff0c;是否缺省等&#…

Linux操作系统精讲之高级IO

代码在&#xff1a; https://github.com/sjmshsh/System-Call-Learn 通过阅读本篇文章&#xff0c;你可以收获&#xff1a; 理解五种IO模型的基本概念&#xff0c;重点是IO多路转接掌握select&#xff0c;poll&#xff0c;epoll系统调用接口并实现简易的TCP服务器理解epoll的…