【排序算法(二)】选择排序(直接选择排序堆排序)

news2024/11/15 10:42:31

在这里插入图片描述

​📝个人主页:@Sherry的成长之路
🏠学习社区:Sherry的成长之路(个人社区)
📖专栏链接:数据结构
🎯长路漫漫浩浩,万事皆有期待

文章目录

    • 1、直接选择排序
      • 1.1 排序思路
      • 1.2 代码实现
      • 1.3 特性及复杂度
    • 2、堆排序
      • 2.1 排序思路
      • 2.2 代码实现
      • 2.3 特性及复杂度
  • 3.总结:

上一篇博客:【排序算法】排序算法介绍及插入排序 ( 直接插入排序 && 希尔排序 )

1、直接选择排序

1.1 排序思路

选择排序的思想非常简单:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。

选择排序的步骤:

在元素集合 array[i]−−array[n−1] 中选择最大(小)的数据元素,若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换

在剩余的 array[i]−−array[n−2](array[i+1]−−array[n−1])集合中,重复上述步骤,直到集合剩余 1个元素

下面用动图展示过程:
在这里插入图片描述

1.2 代码实现

假设我们排升序,那么每次就要选最小数的下标mini ,遍历数组,找出 mini ,然后交换,让起始位置begin++ ,直到begin=n 停止,此刻数组就有序了。

void Swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}
void SelectSort(int* a, int n)
{
	// 选 1 个数
	int begin = 0;

	while (begin < n)
	{
		int mini = begin;

		for (int i = begin + 1; i < n; i++)
		{
			if (a[i] < a[mini])
			{
				mini = i;
			}
		}

		Swap(&a[begin], &a[mini]);
		begin++;
	}
}

这是单次选 1个数的情况,实际上我们还可以一次选两个数mini 和maxi 对应最小和最大值

这里代码需要做出一些改变,增加一个终止位置 end 。循环找 mini 和maxi ,每次找到之后交换begin 和 mini 对应的值和 end 和 maxi 对应的值,将begin++,end−− ,直到 begin≥end 。

注意
假设序列已经找到了mini 和maxi :

在这里插入图片描述

begin 和maxi 的位置重合了,那么begin 和mini 在交换的时候,就把最小值换到了 begin(maxi) 处,最大值被换走了 。那么接下来交换 end 和maxi 的时候,就把 最小值 换到了end 处。

这就导致了排序错误,在 begin==maxi 时,在交换过begin 和mini 的值后,原 mini 的值为当前的最大值,那么就把 maxi=mini ,让最大值下一次能交换到正确的位置。

void Swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}
void SelectSort(int* a, int n)
{
	int begin = 0, end = n - 1;

	while (begin < end)
	{
		int mini = begin, maxi = begin;

		for (int i = begin + 1; i <= end; i++)
		{
			if (a[i] < a[mini])
			{
				mini = i;
			}

			if (a[i] > a[maxi])
			{
				maxi = i;
			}
		}

		Swap(&a[begin], &a[mini]);
		// 特殊处理
		//当a数组里第1个元素是最大值时,此时经过上面的Swap,最大值的位置已经更改了,所以需要修正最大值的位置,让下一个Swap正确交换
        if (begin == maxi)
		{
			maxi = mini;
		}
		Swap(&a[end], &a[maxi]);
		begin++;
		end--;
	}
}

1.3 特性及复杂度

选择排序无论是选单边,还是两边一起选,时间复杂度都是 O(N^2)。
因为总是要每次遍历选出 最小或最大下标,然后进行数据交换。

所以选择排序的效率是不太行的,一般不常用,但是它的思路很简单。

空间复杂度是O(1) ,因为没有开辟额外空间 。

特性
直接选择排序逻辑好理解,但效率不是很好,实际中很少使用。
时间复杂度:O(N^2) 。
空间复杂度:O(1) 。
稳定性:不稳定。

2、堆排序

2.1 排序思路

堆排序算法基本思想:是利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种,通过堆来进行数据选择。需要注意的是,排升序要建大堆(大根堆),排降序建小堆(小根堆)。

2.2 代码实现

1.首先通过向下调整或向上调整的方式建堆,推荐向下调整,因为效率更高,具体原因及复杂度分析看这篇博客:【树与二叉树】堆的时间复杂度详解以及堆的应用—堆排序、TOP - K问题
向上调整建堆:模拟插入的过程
向下调整建堆:模拟删除的过程,这里从倒数的第一个非叶子节点开始调整
在这里插入图片描述

2.堆创建后,如何进行排序 (升序、降序)
升序建大堆;降序建小堆。不是说升序建小堆;降序建大堆不行,而是因为不好:
如果排升序建小堆:
  1、选出最小的数,最小的数就放在第一个位置
  2、接着就要选次小的数,再选次小的数 … … 不断的选下去,如何选 ?
只能对剩下的 sz-1、sz-2、sz-3 … 个数继续建堆。可想这样的代价是很高的 —— 建堆的时间复杂度是 O(N),整个时间复杂度就是 O(N^2),堆的价值没有体现,不如直接循环遍历
在这里插入图片描述
如果排升序建大堆:
  1、选出最大的数,与最后一个数交换位置
  2、怎么选出次大、次次大 ?
堆的结构没有被破坏,且最后一个数不看做堆,左右子树依旧是大堆,向下调整即可
最多调整 log2N 次,整体的时间复杂度是 O(N*log2N)
在这里插入图片描述

综上:升序建大堆、降序建小堆
在这里插入图片描述

//实现父子交换的函数
void Swap(int* px, int* py)
{
	int temp = *px;
	*px = *py;
	*py = temp;
}
//实现调整
void AdjustDown(int* arr, int sz, int parent)
{
	//确定左孩子的下标
	int child = parent * 2 + 1;
	//孩子的下标超出数组的范围就停止
	while (child < sz)
	{
		//确定左右孩子中较小/大的那个
			//左孩子大于右孩子,所以让child记录较小孩子的下标 || (arr[child]<arr[child+1]记录较大孩子的下标)
		if (arr[child] > arr[child + 1] && child + 1 < sz)
		{
			child++; //(当只有一个左孩子时,会越界,且后面使用时会发生非法访问)
		}
		//判断父亲和小孩子
			//小孩子小于父亲,则交换,且继续调整 || (arr[child]>arr[parent]大孩子大于父亲,则交换,且继续调整)
		if (arr[child] < arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			//迭代
			parent = child;
			//重新确定左孩子的下标(当最后的叶子节点是parent时,这时去确定child会以读的方式越界,但可以不关心)
			child = parent * 2 + 1;
		}
		//小孩子大于父亲,则停止调整
		else
		{
			break;
		}
	}
}
//堆排序 -> 效率更高
void HeapSort(int* arr, int sz)
{
	//建堆
	int i = 0;
	//从最后一棵树开始调整,也就是最后一个节点的父亲
	for (i = (sz - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(arr, sz, i);
	}
}

2.3 特性及复杂度

堆排序的时空复杂度在之前的文章已经详细分析过
详情见这篇博客:【树与二叉树】堆的时间复杂度详解以及堆的应用—堆排序、TOP - K问题

时间复杂度:O(N*logN),空间复杂度 O(1) 。

特性
使用堆来选择数据,效率高了很多。
时间复杂度:O(N*logN) 。
空间复杂度:O(1) 。
稳定性:不稳定。

3.总结:

今天我们认识并具体学习了选择排序算法中的直接选择排序和堆排序。下一篇博客,我们将继续学习选择排序中的冒泡排序和快速排序。希望我的文章和讲解能对大家的学习提供一些帮助。

当然,本文仍有许多不足之处,欢迎各位小伙伴们随时私信交流、批评指正!我们下期见~

在这里插入图片描述

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

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

相关文章

腾讯云轻量应用服务器搭建网站教程(WordPress为例)

腾讯云轻量应用服务器搭建WordPress网站教程&#xff0c;先安装WordPress应用镜像&#xff0c;然后远程连接轻量应用服务器获取WP用户名和密码&#xff0c;域名DNS解析到轻量服务器IP地址&#xff0c;登陆WordPress后台管理全过程&#xff0c;腾讯云百科来详细说下腾讯云轻量服…

作用域、闭包的理解、GC算法

1、全局作用域的变量是不会自动的的释放掉的 例如在 <script >let title "hello world"function coming(){alter(title) }</script> 只要不关闭网页或者浏览器&#xff0c;我们都可以在控制台调用 2、作用域环境我们可以理解为一块内存数据 3、函数作…

研报精选230416

目录 【行业230416国联证券】汽车行业4月周报&#xff1a;上海车展期待值高&#xff0c;政策加码一体化压铸【行业230416国联证券】农林牧渔行业周报&#xff1a;低猪价加深去化幅度&#xff0c;高标准农田建设推进【个股230416安信证券_万华化学】Q1业绩超预期&#xff0c;TDI…

靶机精讲之Prime

主机发现 192靶机 扫描靶机 进行对端口的服务和版本的扫描 保存信息 扫描UDP 一般都是http优先然后再shh vuln脚本扫描 web渗透 查看源码 目录爆破 dirb 翻刚才扫到的目录 dirb指定文件类型扫描 读取文件 保存 浏览链接 wfuzz模糊测试 专门扫PHP 只有两目录 浏览页面 模糊…

react2:children属性 - props进阶 - 生命周期

children属性&#xff1a;父想给子组件传递内容&#xff0c;都需要通过children占位 children属性&#xff1a;类似vue中的slot效果 props 类型验证 &#xff1a;现在都是 typescript 替代了 ref 放普通标签上可以获取dom节点 ref 放组件上获取组件实例&#xff0c;可以调用组件…

「C/C++」C/C++内存四大分区

博客主页&#xff1a;何曾参静谧的博客 文章专栏&#xff1a;「C/C」C/C学习 目录术语一、文本区&#xff08;Text Segment&#xff09;/ 代码区二、数据区&#xff08;Data Segment&#xff09;/ 全局区三、栈区&#xff08;Stack Segment&#xff09;四、堆区&#xff08;Hea…

Redis应用问题解决

16. Redis应用问题解决 16.1 缓存穿透 16.1.1 问题描述 key对应的数据在数据源并不存在&#xff0c;每次针对此key的请求从缓存获取不到&#xff0c;请求都会压到数据源&#xff0c;从而可能压垮数据源。比如用一个不存在的用户id获取用户信息&#xff0c;不论缓存还是数据库…

美元霸权的潜在风险——无锚货币,为什么都要刺激消费

第三章 美元体系的风险结构与定价 金句导读 结果平等 没有智慧&#xff0c;优胜劣汰 没有良心。竞争都不充分。 年轻人有出路&#xff0c;穷人有活路&#xff0c;先富起来的人才能有后路。 名词解释&#xff1a; 对冲风险&#xff1a;我是卖面包的的&#xff0c;我还囤很多面…

【持续更新篇】SLAM视觉特征点汇总+ORB特征点+VINS前端

Harris角点 opencv函数 cornerHarris提取输入图像的Harris角点 检测原理 检测思想&#xff1a;使用一个固定窗口在图像上进行任意方向的滑动&#xff0c;对比滑动前后的窗口中的像素灰度变化程度&#xff0c;如果存在任意方向上的滑动&#xff0c;都有较大灰度变化&#xf…

Jetpack Compose 实战 宝可梦图鉴

文章目录前言实现效果一、架构介绍二、一些的功能点的介绍加载图片并获取主色,再讲主色设置为背景一个进度缓慢增加的圆形进度条单Activity使用navigation跳转Compose可组合项返回时页面重组的问题hiltViewModel()主要参考项目总结前言 阅读本文需要一定compose基础&#xff0…

Python爬虫|全国补充耕地项目数量爬取与分析——多进程案例

一、使用的库 import requests from lxml import etree import time import random import re import openpyxl import openpyxl from pyecharts.charts import Bar, Pie from pyecharts import options as opts from multiprocessing.dummy import Pool 二、数据爬取思路 1…

手拉手Centos7安装配置Redis7

Redis&#xff08;Remote Dictionary Server )&#xff0c;即远程字典服务&#xff0c;是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并提供多种语言的API。 Redis是一个NoSQL数据库&#xff0c;常用缓存(cache) Re…

Spark 安装及WordCount编写(Spark、Scala、java三种方法)

Spark 官网&#xff1a;Apache Spark™ - Unified Engine for large-scale data analytics Spark RDD介绍官网&#xff1a;https://spark.apache.org/docs/2.2.0/api/scala/index.html#org.apache.spark.rdd.RDD 下载好spark解压mv到软件目录 linux>mv spark-xxx-xxx /op…

统计套利策略

统计套利策略套利策略跨品种套利标的择时风控套利策略 套利是&#xff0c;某种商品在&#xff08;在同一市场或不同市场&#xff09;拥有两个价格的情况下&#xff0c;以较低的价格买进&#xff0c;较高的价格卖出&#xff0c;从而实现获利的交易方式。 比如咖啡店里有小杯、…

【jvm系列-04】精通运行时数据区共享区域---堆

JVM系列整体栏目 内容链接地址【一】初识虚拟机与java虚拟机https://blog.csdn.net/zhenghuishengq/article/details/129544460【二】jvm的类加载子系统以及jclasslib的基本使用https://blog.csdn.net/zhenghuishengq/article/details/129610963【三】运行时私有区域之虚拟机栈…

chapter-6数据库设计原则

以下课程来源于MOOC学习—原课程请见&#xff1a;数据库原理与应用 考研复习 数据库设计 数据库设计是基于应用系统需求分析中对数据的需求&#xff0c;解决数据的抽象、数据的表达和数据的存储等问题&#xff0c;其目标是设计出一个满足应用要求&#xff0c;简洁、高效、规范…

【c语言】二维数组

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 给大家跳段街舞感谢支持&#xff01;ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ…

「计算机控制系统」3. 计算机控制系统的数学描述

差分方程 Z变换 脉冲传递函数 计算机控制系统的响应 文章目录差分方程基础知识差分方程的解Z变换定义与性质求Z变换Z变换表求Z反变换用Z变换解差分方程脉冲传递函数脉冲传递函数与差分方程的相互转化开环脉冲传递函数闭环脉冲传递函数计算机控制系统的响应差分方程 基础知识 …

Photoshop CS6安装包下载及安装教程(Photoshop 2016)

下载链接&#xff1a; https://pan.quark.cn/s/f961759b36cc “Adobe Photoshop是一款集图像扫描、编辑修改、图像制作、广告创意、图像输入输出于一体的图形图像处理软件,简称ps,对于这款软件大家应该并不陌生,而今天小编带来的是Photoshop2023中文版,也是该系列的最新版本,不…

WAF攻防-菜刀冰蝎哥斯拉流量通讯特征绕过检测反制感知

文章目录菜刀-流量&绕过&特征&检测特征绕过检测冰蝎3-流量&绕过&特征&检测特征通讯过程检测绕过哥斯拉-流量&绕过&特征&检测特征Other使用Proxifier进行流量转发至Burp抓包分析(使用Wireshake也可以) 菜刀-流量&绕过&特征&检…