【数据结构(邓俊辉)学习笔记】向量05——排序器

文章目录

  • 0. 概述
  • 1.统一入口
  • 2. 起泡排序
    • 2.1 起泡排序(基础版)
      • 2.1.1 算法分析
      • 2.1.2 算法实现
      • 2.1.3 重复元素与稳定性
      • 2.1.4 复杂度分析
  • 3. 归并排序
    • 3.1 有序向量的二路归并
    • 3.2 分治策略
    • 3.3 实例
    • 3.4 二路归并接口的实现
    • 3.5 归并时间
    • 3.6 排序时间
  • 4.综合评价

0. 概述

介绍下有序向量的排序器,包括起泡排序和归并排序。

1.统一入口

这类接口也是将无序向量转换为有序向量的基本方法和主要途径
在这里插入图片描述

2. 起泡排序

2.1 起泡排序(基础版)

2.1.1 算法分析

在这里插入图片描述

2.1.2 算法实现

反复调用单趟扫描交换算法,直至逆序现象完全消除。

template <typename T> //向量的起泡排序
void Vector<T>::bubbleSort ( Rank lo, Rank hi ) //assert: 0 <= lo < hi <= size
{ while ( !bubble( lo, hi-- ) ); } //逐趟做扫描交换,直至全序

算法思想:依次比较各对相邻元素,每当发现逆序即令二者彼此交换;一旦经过某趟扫描之后未发现任何逆序的相邻元素,即意味着排序任务已经完成,则通过返回标志“sorted”,以便主算法及时终止。

template <typename T>
void Vector<T>::bubble ( Rank lo, Rank hi) { //0 <= n
	bool sorted = true; //整体排序标志
	while ( ++lo < hi ) { //自左向右,逐一检查各队相邻元素
		if ( _elem[lo - 1] > _elem[lo] ) { //若逆序,则
			sorted = false; //因整体排序不能保证,需要清除排序标志
			swap ( _elem[lo - 1], _elem[lo]); //交换
		}
	}
	return sorted;
} //借助布尔型标志位sorted,可及时提前退出,而不致总是蛮力地做n - 1趟扫描交换

2.1.3 重复元素与稳定性

稳定算法的特征是,重复元素之间的相对次序在排序前后保持一致。

该起泡排序过程中元素相对位置有所调整的唯一可能是,某元素_elem[i - 1]严格大于其后继_elem[i]。也就是说,在这种亦步亦趋的交换过程中,重复元素虽可能相互靠拢,但绝对不会相互跨越。由此可知,起泡排序属于稳定算法。

2.1.4 复杂度分析

在这里插入图片描述
如图,前r个元素无序,后n-r元素按顺序排列并严格就位。

bubble()算法由内、外两层循环组成。内循环从前向后,依次比较各对相邻元素,如有必要则将其交换。

扫描交换的趟数不会超过O( r ),算法总体消耗时间不会超过O(n *r)次。

故乱序元素仅限于 A[0, n \sqrt n n )区间,最坏情况下仍需调用 bubblesort1A ()做 Ω \Omega Ω( n \sqrt n n )次调用,共做 Ω \Omega Ω(n)次交换操作和 Ω \Omega Ω(n 3 2 ^{\frac 32} 23)次比较操作,因此累计运行 Ω \Omega Ω(n 3 2 ^{\frac 32} 23)时间。
该算法可进一步优化,详见算法设计优化——起泡排序

3. 归并排序

在这里插入图片描述

3.1 有序向量的二路归并

二路归并属于迭代式算法。每步迭代中,只需比较两个待归并向量的首元素,将小者取出并追加到输出向量的末尾,该元素在原向量中的后继则成为新的首元素。如此往复,直到某一向量为空。最后,将另一非空的向量整体接至输出向量的末尾。

在这里插入图片描述
二路归并算法在任何时刻只需载入两个向量的首元素,故除了归并输出的向量外,仅需要常数规模的辅助空间。

3.2 分治策略

算法思想:通过递归调用将二者分别转换为有序向量,即可借助二路归并算法,得到与原向量S对应的整个有序向量
在这里插入图片描述

template <typename T> //向量归并排序
void Vector<T>::mergeSort ( Rank lo, Rank hi ) { //0 <= lo < hi <= size
	if ( hi - lo < 2 ) return; //递归基,单元素区间自然有序,否则...
	int mi = ( lo + hi ) >> 1; //以中点为界 
	mergeSort ( lo, mi ); //对前半段排序 
	mergeSort ( mi, hi ); //对后半段排序 
	merge ( lo, mi, hi ); //归并
}

3.3 实例

在这里插入图片描述

3.4 二路归并接口的实现

算法思想:创建临时数组B存放数组A的[ lo,mi)元素,数组C指向数组A的[mi,hi),调用二路归并算法,将有序向量存放在A中。在这里插入图片描述

template <typename T> //有序向量的归并
void Vector<T>::merge ( Rank lo, Rank mi, Rank hi ) { //各自有序的子向量[lo, mi)和[mi, hi)
	T* A = _elem + lo; //合并后的向量A[0, hi - lo) = _elem[lo, hi)
	int lb = mi - lo; T* B = new T[lb]; //前子向量B[0, lb) = _elem[lo, mi)
	for ( Rank i = 0; i < lb; B[i] = A[i++] ); //复制前子向量
	int lc = hi - mi; T* C = _elem + mi; //后子向量C[0, lc) = _elem[mi, hi)
	for ( Rank i = 0, j = 0, k = 0; ( j < lb ) || ( k < lc ); ) { //B[j]和C[k]中的小者续至A末尾
		if ( ( j < lb ) && ( ! ( k < lc ) || ( B[j] <= C[k] ) ) ) A[i++] = B[j++];
		if ( ( k < lc ) && ( ! ( j < lb ) || ( C[k] < B[j] ) ) ) A[i++] = C[k++];
	}
	delete [] B; //释放临时空间B
} //递归后得到完整的有序向量[lo, hi)
  • 若将后一句中的“C[k] < B[j]”改为“C[k] <= B[j]”,对算法将有何影响?

     ~~~~     经如此调整之后,虽不致影响算法的正确性(仍可排序),但不再能够保证各趟二路归并的稳定性,整个归并排序算法的稳定性也因此不能保证。                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                                                  
     ~~~~     原算法的控制逻辑可以保证稳定性。实际上,若两个子区间当前接受比较的元素分别为B[j]和C[k],则唯有在前者严格大于后者时,才会将后者转移至A[i++];反之,只要前者不大于后者(包含二者相等的情况),都会优先转移前者。由此可见,无论是子区间内部(相邻)的重复元素,还是子区间之间的重复元素,在归并之后依然能够保持其在原向量中的相对次序。

  • 若将前一句中的“B[j] <= C[k]”改为“B[j] < C[k]”,对算法将有何影响?

当待归并的子向量之间有重复元素时,循环体内的两条处理语句均会失效,两个子向量的首
元素都不会被转移,算法将在此处进入死循环。

3.5 归并时间

在这里插入图片描述
二路归并只需线性时间的结论,并不限于相邻且等长的子向量。实际上,即便子向量在物理空间上并非前后衔接(列表),且长度相差悬殊,该算法也依然可行且仅需线性时间。

3.6 排序时间

在这里插入图片描述
故:
在这里插入图片描述

不足:
路归幵算法 merge(),反复地通过 new 和 delete 操作申请和释放辅助空间。然而实验统计表明,这类操作的实际时间成本,大约是常规运算的 100 倍,故往往成为制约效率提高的瓶颈。
改进点:
可以在算法启动时,统一申请一个足够大的缓冲区作为辅助向量B[],并作为全局变量为所有递归实例公用;归并算法完成之后,再统一释放。
如此可以将动态空间申请的次数降至O(1),而不再与递归实例的总数O(n)相关。当然,这样会在一定程度上降低代码的规范性和简洁性,代码调试的难度也会有所增加。

4.综合评价

  • 起泡排序最坏情况总需要O( n 2 n^2 n2)
  • 归并排序最坏情况下为O(nlogn)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】体系结构和进程管理

目录 一、认识冯诺依曼体系结构 1.1 概念 1.2 组成 1.3 存储分级 1.4 有关冯诺依曼的问题 二、操作系统 2.1 概念和功能 2.2 如何理解操作系统的 "管理" 2.3 操作系统的用户、系统调用和库函数概念 三、进程 3.1 基本概念 3.2 描述进程-进程控制块PCB …

C语言:数据结构(双向链表)

目录 1、双向链表的结构2、顺序表和双向链表的优缺点分析3、双向链表的实现 1、双向链表的结构 注意&#xff1a;这⾥的“带头“跟前面我们说的“头节点”是两个概念&#xff0c;实际前面的在单链表阶段称呼不严谨&#xff0c;但是为了更好的理解就直接称为单链表的头节点。 带…

QT之信号和槽

在刚刚了解Qt的时候&#xff0c;为了通过按钮显示 hello world 的时候曾说明过信号与槽&#xff0c;虽然没有细说&#xff0c;不过也算是接触过。 而本文就会细细说明什么是 Qt 的信号与槽。 概念初识 在 linux 学进程相关的内容的时候&#xff0c;曾了解过信号是操作系统控…

【STM32】快速使用F407通用定时器输出可变PWM

网上的文章太啰嗦&#xff0c;这里直接开始。 使用的是STM32CubeIDE&#xff0c;HAL。以通用定时器TIM12在 通道2上输出1KHz的PWM为例。 要确定输出的引脚、定时器连接在哪里。 TIM2、3、4、5、12、13、14在APB1上&#xff0c;最大计数频率84M。 TIM1、8、9、10、11在APB2…

Python 与 TensorFlow2 生成式 AI(二)

原文&#xff1a;zh.annas-archive.org/md5/d06d282ea0d9c23c57f0ce31225acf76 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第四章&#xff1a;教授网络生成数字 在前一章中&#xff0c;我们涵盖了神经网络模型的构建基块。在这一章中&#xff0c;我们的第一个项目…

HIVE启动步骤

不如意的时候不要尽往悲伤里钻 想想有笑声的日子 启动HIEV 1.启动虚拟机Hadoop集群 2.连接Linux 3.start-all.sh 4.hive 5.hive启动时报错 当我们启动Hadoop集群时 启动hive可能会出现卡在true处不动的情况 那么我们只需要做一个操作就可以解决问题啦 hdfs haadmin -transitio…

ASP.NET数据存储与交换系统设计

摘 要 该系统以Microsoft Visual Studio 2003作为开发工具&#xff0c;选用SQL Server 2000数据库来实现数据存储&#xff0c;并设计开发了一种基于B/S模式的数据存储与交换系统。该系统完成了用户注册管理、后台管理和用户空间管理功能&#xff1b;为每个用户提供了个人的存…

数据库(MySQL)—— DQL语句(基本查询和条件查询)

数据库&#xff08;MySQL&#xff09;—— DQL语句&#xff08;基本查询和条件查询&#xff09; 什么是DQL语句基本查询查询多个字段字段设置别名去除重复记录 条件查询语法条件 我们今天进入MySQL的DQL语句的学习&#xff1a; 什么是DQL语句 MySQL中的DQL&#xff08;Data Q…

【ARM 裸机】NXP 官方 SDK 使用

在前几节中&#xff0c;学习了如何编写汇编的 led 驱动、C 语言的 led 驱动、模仿 STM32 进行开发&#xff0c;我们都是自己写外设寄存器的结构体&#xff0c;外设非常的多&#xff0c;写起来费时费力&#xff0c;NXP 针对 I.MX6ULL 编写了一个 SDK 包&#xff0c;这个 SDK 包就…

python的输入输出(爽文,备忘,查询,友好)

Python中的输入输出主要涉及到输入函数和输出函数。 输出函数&#xff1a;print() print() 函数用于将信息输出到屏幕上。它可以输出字符串、变量的值&#xff0c;以及其他各种数据类型。 name "Alice" age 30 print("姓名:", name, "年龄:&quo…

OpenCV如何实现背投(58)

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV直方图比较(57) 下一篇&#xff1a;OpenCV如何模板匹配(59) 目标 在本教程中&#xff0c;您将学习&#xff1a; 什么是背投以及它为什么有用如何使用 OpenCV 函数 cv::calcBackP…

轻松下载小程序短剧视频,你不可错过的神器!

探索小程序的精彩短剧&#xff0c;一款神器让您的收藏变得触手可及。轻松下载&#xff0c;无损享受&#xff0c;每个精彩瞬间&#xff0c;都不让你错过。让这款下载神器成为你掌中的宝藏&#xff0c;开启随时随地的精彩观影体验。 这个神器就是下载高手 下载小程序视频的工具…

GoLang Gin实际使用

所有代码同步到Admin/gitDemo - Gitee.comhttps://gitee.com/mec-deployment-team_0/git-demo/tree/dev/ 1.创建Gin框架 一般设计一个常规的web项目&#xff0c;都需要以下几个模块 runApp 主函数&#xff0c;运行整个项目routes 路由控制&#xff0c;管理跳转以及路由分组co…

CTF-Show nodejs

web334 下载附件&#xff0c;有两个文件 在Character.toUpperCase()函数中&#xff0c;字符ı会转变为I&#xff0c;字符ſ会变为S。 在Character.toLowerCase()函数中&#xff0c;字符İ会转变为i&#xff0c;字符K会转变为k。 所以用ctfſhow 123456登录就可以出flag了 w…

力扣刷题第一天:消失的数字

大家好啊&#xff0c;从今天开始将会和大家一起刷题&#xff0c;从今天开始小生也会开辟新的专栏。&#x1f61c;&#x1f61c;&#x1f61c; 目录 第一部分&#xff1a;题目描述 第二部分&#xff1a;题目分析 第三部分&#xff1a;解决方法 3.1 思路一&#xff1a;先排序…

LeetCode1005:K次取反后最大化的数组和

题目描述 给你一个整数数组 nums 和一个整数 k &#xff0c;按以下方法修改该数组&#xff1a; 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。 重复这个过程恰好 k 次。可以多次选择同一个下标 i 。 以这种方式修改数组后&#xff0c;返回数组 可能的最大和 。 解题思想 第…

AC自动机

AC自动机 AC自动机有一个很出色的功能&#xff1a;实现多模式匹配。 多模式匹配&#xff1a;模式串有多个&#xff0c;主串只有一个&#xff0c;要进行多次模式串匹配。如果用KMP就要一个一个模式串进行匹配&#xff0c;效率低。AC自动机就可以做到&#xff0c;只要经过一些预…

深度学习中权重初始化的重要性

深度学习模型中的权重初始化经常被人忽略&#xff0c;而事实上这是非常重要的一个步骤&#xff0c;模型的初始化权重的好坏关系到模型的训练成功与否&#xff0c;以及训练速度是否快速&#xff0c;效果是否更好等等&#xff0c;这次我们专门来看看深度学习中的权重初始化问题。…

RuoYi-Vue-Plus (SPEL 表达式)

RuoYi-Vue-Plus 中SPEL使用 DataScopeType 枚举类中&#xff1a; /*** 部门数据权限*/DEPT("3", " #{#deptName} #{#user.deptId} ", " 1 0 "), PlusDataPermissionHandler 拦截器中定义了解析器&#xff1a; buildDataFilter 方法中根据注解的…

基于电磁激励原理利用视触觉传感器估计抓取力矩的方法

由于触觉感知能使机器人通过其触觉传递获取丰富的接触信息&#xff0c;触觉感知已经成为机器人机械臂的一种流行的感知方式。而在触觉传感器可获取的各种信息中&#xff0c;通过外界接触从抓取物体传递到机器人手指的力矩等信息&#xff0c;在完成各种指令的实现尤为重要。如图…