数据结构之选择排序

news2024/11/13 9:11:57

目录

直接选择排序

选择排序的时间复杂度

堆排序

向上调整算法

向下调整算法

向上调整算法建立堆

向下调整算法建立堆

堆排序整体代码

堆排序的时间复杂度


直接选择排序

在之前讲插入排序时,我们讲了这样的一个应用场景,我们在斗地主摸牌时,会一张一张的摸,然后采用插入排序的方法将手中的牌变得有序,但是还有一种摸牌的方式,就是将所有的牌先发到手里,然后在所有牌中找出最大的和最小的放到牌的两端,重复上述步骤,依次挑选最大的和最小的放到两端,直到将手中的牌排好序,这其实就是生活中直接选择排序的应用。在数据结构中,直接选择排序又是怎样实现的呢?

 直接插入排序的思想:先进行单趟的排序,找出最小和最大的两个数,并且将最小的数和最大的数依次交换到数组的头部和尾部,然后重复单趟排序的操作,直到将数组整个排有序。

直接选择排序图示如下:

直接选择排序代码如下:

void Swap(int* p1, int* p2)
{
	int temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}

void SelectSort(int* a, int size)
{
	int begin = 0, end = size - 1;

	while (begin < end)
	{
		int max = begin, min = begin;
		//先进行单趟排序,遍历每个元素,找出最大最小的两个数的位置
		for (int i = begin+1; i <= end; i++)//这里让begin+1的原因是,自己没有必要和自己比较
		{
			if (a[i] < a[min])
			{
				min = i;
			}
			if (a[i] > a[max])
			{
				max = i;
			}
		}
//单趟排序找到最大和最小的元素之后,将最小的值与begin位置上的元素交换,将最大的值与end位置上的元素交换
		Swap(&a[begin], &a[min]);
//当begin位置上的元素就是最大的元素时,将最小的元素和begin位置上的元素交换之后,最大的元素的位置就会发生改变
//max位置上的元素并不是最大的元素,min位置上的元素就是最大的元素,所以我们得把此时的min赋值给max,然后再做交换
		if (max == begin)
			max = min;
		Swap(&a[end], &a[max]);
		//让begin++,end--,再进行下一趟排序,找出最大的和最小的放置在两端
		begin++;
		end--;

	}
}
int main()
{
	int arr[] = {100,99,27,92,99,220,28,78,18,8 };
	SelectSort(arr, sizeof(arr) / sizeof(int));
	for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

 大家有没有注意到这样一段代码:

我们进行了一趟排序,找出了最大的元素和最小的元素,直接将最大的元素和最小的元素分别与end位置和begin位置上的元素交换即可,为什么还要把进行一次判断呢?

因为我们要排除一种极端的情况:

看下图:

其实对于上述情况,我们还有一种解决办法,如果begin位置的元素就是最大的元素,我们可以先让大的元素和end位置上的元素进行交换,然后再让最小的元素和begin位置上的元素进行交换,但是,上述情况只是在先交换最小元素的基础上修正的代码,大家可以根据自己情况进行选择。

选择排序的时间复杂度

时间复杂度:最好最坏都是O(N^2)

因为即使数组已经有序了编译器是无法分辨的,还是得每次找出最大的数和最小的数,第一趟比较每个元素比较2次所以总共比较2*(size-1)次,第二趟比较比较2*(size-3)次,所以其复杂度为等差数列的前n项求和,不管最好最坏,选择排序都是这个执行流程,所以直接选择排序的时间复杂度为O(N^2)。

稳定性:不稳定

堆排序

堆排序其实最重要的就是两个算法,向上调整和向下调整算法,先通过向上调整和向下调整算法建立堆,然后建好堆之后,再次采用向下调整算法调整元素的位置,最终实现排序。 

以下给出具体的代码,对代码不理解的小伙伴可以去看堆那几期的内容。点击这里 and 这里

向上调整算法

void AdjustUp(int* a, int child)
{
	assert(a);
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		//因为是大堆,所以父亲节点的值大于孩子节点的值,如果父亲节点的值小于孩子节点的值,就需要交换
		if (a[parent] < a[child])
		{
			int tmp = a[parent];
			a[parent] = a[child];
			a[child] = tmp;
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

向下调整算法

void AdjustDown(int* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		//因为是大堆,向下调整时,为了避免从堆顶继续调整,所以必须选最大的孩子作为child
		if (child + 1 < n && a[child + 1] > a[child])
		{
			++child;
		}
		//因为是大堆,所以,如果父亲小于孩子,需要进行交换
		if (a[parent] < a[child])
		{
			int tmp = a[parent];
			a[parent] = a[child];
			a[child] = tmp;
			parent = child;
			child = parent * 2 + 1;

		}
		else
		{
			break;
		}
	}
}

向上调整算法建立堆

for (int i = 1; i < n; i++)
	{
		AdjustUp(a, i);
	}

向上调整算法建堆,这里的i表示的就是向上调整算法里的child,对某个元素采用向上调整算法的前提是这个元素的前面的所有元素必须构成大堆或者小堆,对于一个数组而言,如果数组只有一个元素,我们就可以将这个数组看成大堆或者小堆,所以我们可以单独将第一个元素看成是一个大堆或者小堆,所以就可以从第二个元素要开始进行向上调整算法,然后后面的依此逻辑,直到将整个数组建成堆,与其说是建堆,不如说是调堆。

向下调整算法建立堆

	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}

向下调整算法建堆,这里的i可以看做向下调整算法里的parent,一个元素要采用向下调整算法,必须保证当前元素所在节点的左右子树都必须为大堆或者小堆。叶子节点没有左右子树,所以不能对叶子节点采用向下调整算法, 但是可以对最后一个叶子节点的父亲节点进行向下调整算法,因为最后一个叶子节点的父亲节点的左右子树都是叶子节点,叶子节点可以看成是一个大堆或者小堆,所以我们要从最后一个叶子节点的父亲节点开始进行向下调整算法。

堆排序整体代码

void HeapSort(int* a,int n)
{
 
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
	for (int end = n - 1; end >= 0; end--)
	{
		HPDataType tmp = a[0];
		a[0] = a[end];
		a[end] = tmp;
		AdjustDown(a, end, 0);
	}
}
int main()
{
	int arr[] = { 1000,999,27,9,99,20,28,78,18,88 };
	HeapSort(arr, sizeof(arr) / sizeof(int));
	for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

运行截图:

堆排序的时间复杂度

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

稳定性:不稳定

注意:虽然向上调整算法和向下调整算法都可以建立堆,但是我们建议使用向下调整算法建立堆,因为向上调整算法的时间复杂度为O(N*logN),而向下调整算法建立堆的时间复杂度为O(N)。

以上便是选择排序的所有内容。

本期的内容到此结束。^_^

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

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

相关文章

PyInstaller 打包 Python 脚本为 .exe 可执行文件闪退、No Model named XXX问题

文章目录 前言.exe 可执行文件闪退No Model named XXXPython 环境问题查看当前python路径查看当前python环境使用的site-package路径 个人简介 前言 在上一篇文章中&#xff0c;我们介绍了如何将 Python 脚本打包为 .exe 可执行文件&#xff0c;但有时候打包生成的 .exe 文件会…

慢SQL诊断

最近经常遇到技术开发跑来问我慢SQL优化相关工作&#xff0c;所以干脆出几篇SQL相关优化技术月报&#xff0c;我这里就以公司mysql一致的5.7版本来说明下。 在企业中慢SQL问题进场会遇到&#xff0c;尤其像我们这种ERP行业。 成熟的公司企业都会有晚上的慢SQL监控和预警机制。…

阿里云cdn设置相同的域名路径访问不同的oss目录

1.设置回源配置&#xff0c;添加回源URL改写 2.设置跨域&#xff0c;cdn的跨域优先oss 3.回源设置

前端 三种解决跨域问题 jsonp 、CORS、代理服务器 解决跨域全家桶

我的报错情况是 后端接口是3000 前端本地接口是8080&#xff0c;最后出现跨域 1.什么是跨域&#xff1f; 首先跨域是一种安全机制&#xff0c;是在开发上线前考虑到的安全问题并且需要采取合适的手段去避免这个问题带来的程序错误,接口跨域可以后端处理,也可以前端处理&#x…

Docker的安装与简单操作命令

目录 前言 docker的安装 基础docker操作 容器管理 镜像管理 容器镜像封装与加载 前言 前文简单说明了容器技术出现的背景&#xff0c;与对docker做了结构上的介绍Container容器技术简介-CSDN博客https://blog.csdn.net/qq_72569959/article/details/134814887 讲到dock…

Day07 Liunx高级系统设计8-线程

概述 进程与线程 进程 : 系统分配资源的基本单位 , 可以简单理解为一个正在进行的程序 线程 : 操作系统调度的最小单位 , 就是一段代码的执行顺序 注意&#xff1a; 1, 一个进程必须要有一个线程 , 该线程被称为主线程 2, 一个进程可以有多个线程 , 除主线程外的其他线程都是…

玻色量子袁为出席中国移动第四届科技周量子计算算法与应用分论坛

9月12日&#xff0c;中国移动第四届科技周“量子计算算法与应用”分论坛在北京成功举办&#xff0c;中国移动研究院院长黄宇红发表致辞&#xff0c;中国移动未来研究院院长崔春风全程主持。玻色量子作为光量子计算领域真机测试与场景应用的标杆企业应邀出席&#xff0c;玻色量子…

散点图,何须图,折线图混放在一个echarts

散点图&#xff0c;何须图&#xff0c;折线图混放在一个echarts option {tooltip: {trigger: axis,axisPointer: {type: cross,crossStyle: {color: #999}}},legend: {data:[盒须图1,盒须图2,折线图,散点图]},xAxis: [{type: category,data: [周一,周二,周三,周四,周五,周六…

智能优化算法应用:基于花授粉算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于花授粉算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于花授粉算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.花授粉算法4.实验参数设定5.算法结果6.参考文…

vue3 + ts 防抖指令,节流指令,复制指令

vue3 ts 自定义指令 防抖指令&#xff0c;节流指令&#xff0c;复制指令 本文使用了 element-ui , element-plus 官网 源文件 https://admin.spicyboy.cn/#/directives/debounceDirect 新建 copy.ts 文件 &#xff08;复制指令&#xff09; import type { Directive, Di…

重新思考U-Net在医学超声图像分割中的应用(NU-net)

Rethinking the Unpretentious U-net for Medical Ultrasound Image Segmentation 摘要&#xff1a; 乳腺肿瘤分割是帮助我们描述和定位肿瘤区域的关键步骤之一。然而&#xff0c;乳腺肿瘤形态多变、边界模糊、强度分布相似&#xff0c;给乳腺肿瘤的准确分割带来了挑战。近年…

java系列-LinkedHashMap

1.插入新节点时&#xff0c;会将该节点加到链表尾部 public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>{/*** The head (eldest) of the doubly linked list.*/transient LinkedHashMapEntry<K,V> head;/*** The tail (young…

C语言数据结构-栈和队列

文章目录 1 栈的表示与实践1.1 栈的概念及结构1.2 定义数据结构1.3 初始化与销毁1.4 入栈1.5 出栈1.6 栈顶的数据1.7 栈的个数1.8 栈是否是空1.9 打印栈 2 队列的表示与实现2.1 队列的概念与结构2.2 队列的数据结构定义2.3 队列的初始化与销毁2.4 入队2.5 出队2.6 取队头数据2.…

enumerate()函数讲解+同时获取索引和对应的元素值+实例

enumerate()函数是Python内置函数&#xff0c;用于在遍历可迭代对象&#xff08;如列表、字符串、元组等&#xff09;时&#xff0c;同时获取索引和对应的元素值。 它的主要作用是在循环过程中方便地获取索引和元素。 下面以两个例子来进行介绍理解。 目录 一、例子1 二、例…

打造‘产业大数据综合服务平台’,助力智慧园区建设!

随着大数据、人工智能、云计算、物联网等新一代信息技术的发展与应用&#xff0c;我国各类型园区正在向“智慧园区”转型升级&#xff0c;逐步开启数字化、智能化的运营管理模式。智慧园区的建设不仅需要基础设施的智慧化&#xff0c;更要实现园区规划、运营、管理、服务的智慧…

plf::list原理分析

plf::list是一个比std::list性能要好的另外一种实现&#xff0c;根据作者的性能测试&#xff1a; 293% faster insertion 57% faster erasure 17% faster iteration 77% faster sorting 70% faster reversal 91% faster remove/remove_if 63% faster unique 811% faster clear …

Vue 3 + Tailwind CSS:打造现代化项目的完美组合

Vue 3 Tailwind CSS&#xff1a;打造现代化项目的完美组合 本篇教程将向你介绍如何将 Tailwind CSS 与 Vue 3 项目搭配使用&#xff0c;为你的项目提供现代化的 UI 呈现和开发体验。通过本文的逐步演示和示例代码&#xff0c;你将很快掌握在 Vue 3 中集成和使用 Tailwind CSS…

YOLOv7改进实验:一文了解YOLOv7如何打印FPS指标

💡该教程为改进YOLOv7指南,属于《芒果书》📚系列,包含大量的原创首发改进方式🚀 💡🚀🚀🚀本博客内含改进源代码,按步骤操作运行改进后的代码即可 💡更方便的统计更多实验数据,方便写作 新增YOLOv7打印FPS指标 完善(一键YOLOv7打印FPS指标) 文章目录 完善…

【Tomcat】java.net.BindException “Address already in use: NET_Bind“

问题 17:37 Error running Tomcat 7.0.76: Unable to open debugger port (127.0.0.1:14255): java.net.BindException "Address already in use: NET_Bind"调整 把14255 改成 49963就正常了 附件 netstat -aon|findstr "49963" taskkill -f -pid xxx…

机械中常用的一些术语

目录 一、OEMSOP:SOP编写指南 WI(标准作业指导书):标准作业程序 &#xff08;SOP&#xff09;:SOP和WI的区别&#xff1a;一、PFC、FMEA、PCP、WIPPAP、PSW&#xff1a;APQP&#xff1a;BOM&#xff08;Bill of Material&#xff09;物料清单DV&#xff08;设计验证&#xff09…