【数据结构(邓俊辉)学习笔记】列表04——排序器

news2024/11/16 23:49:30

文章目录

  • 0. 统一入口
  • 1. 选择排序
    • 1.1 构思
    • 1.2 实例
    • 1.3 实现
    • 1.4 复杂度
  • 2. 插入排序
    • 2.1 构思
    • 2.2 实例
    • 2.3 实现
    • 2.4 复杂度分析
    • 2.5 性能分析
  • 3. 归并排序
    • 3.1 二路归并算法
      • 3.1.1 二路归并算法原理
      • 3.1.2 二路归并算法实现
      • 3.1.3 归并时间
    • 3.2 分治策略
      • 3.2.1 实现
      • 3.2.2 排序时间
  • 4. 总结

0. 统一入口

设置一个统一的排序操作接口。

template <typename T>
void List<T>::sort ( ListNodePosi(T) p, int n ) { //列表区间排序
	switch ( rand() % 3 ) { //随机选择排序算法。可根据具体问题的特点灵活选择或扩充
		case 1: insertionSort ( p, n ); break; //插入排序
		case 2: selectionSort ( p, n ); break; //选择排序
		default: mergeSort ( p, n ); break; //归并排序
	}
}

1. 选择排序

1.1 构思

在这里插入图片描述
在任何时刻,后缀S[r, n)已经有序,且不小于前缀S[0, r)

起泡排序需要O( n 2 n^2 n2)时间,因为挑选每个最大元素M,需做O(n)次比较和O(n)次交换。

1.2 实例

在这里插入图片描述

1.3 实现

在这里插入图片描述
算法思想:
算法迭代过程如图所示,已排序区间S[T,p+n),未排序区间U[p,T)。在未排序区间使用selectMax()接口定位最大节点,放入已排序区间。

template <typename T> //对列表中起始于位置p、宽度为n的区间做选择排序
void List<T>::selectionSort( ListNodePosi<T> p, Rank n ) { // valid(p) && Rank(p) + n <= size
   ListNodePosi<T> head = p->pred, tail = p;
   for ( Rank i = 0; i < n; i++ ) tail = tail->succ; //待排序区间为(head, tail)
   while ( 1 < n ) { //在至少还剩两个节点之前,在待排序区间内
      ListNodePosi<T> max = selectMax ( head->succ, n ); //找出最大者(歧义时后者优先)
      insert( remove( max ), tail ); //将其移至无序区间末尾(作为有序区间新的首元素)
      tail = tail->pred; n--;
   }
}

推敲:selectionSort() 算法中insert()和remove()反复new 和delete操作,此操作虽然视为常数复杂度但时间消耗时间大概为基本操作的100倍。

优化思路:

  1. 只通过对M和T两处局部引用的修改来实现同样的功能。
  2. 只需交换M和T前驱节点的数据域。
    在这里插入图片描述
template <typename T> //从起始于位置p的n个元素中选出最大者
ListNodePosi<T> List<T>::selectMax( ListNodePosi<T> p, Rank n ) {
   ListNodePosi<T> max = p; //最大者暂定为首节点p
   for ( ListNodePosi<T> cur = p; 1 < n; n-- ) //从首节点p出发,将后续节点逐一与max比较
      if ( !lt( ( cur = cur->succ )->data, max->data ) ) //若当前元素不小于max,则
         max = cur; //更新最大元素位置记录
   return max; //返回最大节点位置
}

插入和删除算法见列表02——无序列表。

lt 为 ”≥“ ,遇到重复元素时,返回秩最大者,保证算法的稳定性。

1.4 复杂度

在这里插入图片描述
θ( n 2 n^2 n2)的效率应有很大的改进空间。

2. 插入排序

2.1 构思

在这里插入图片描述

2.2 实例

在这里插入图片描述

2.3 实现

在这里插入图片描述
算法思想:前缀S[0, r)已经有序。接下来,借助有序序列的查找算法,可在该前缀中定位到不大于e的最大元素。于是只需将e从无序后缀中取出,并紧邻于查找返回的位置之后插入,使得有序前缀的范围扩大至S[0, r]。

template <typename T> //对列表中起始于位置p、宽度为n的区间做插入排序
void List<T>::insertionSort( ListNodePosi<T> p, Rank n ) { // valid(p) && Rank(p) + n <= size
   for ( Rank r = 0; r < n; r++ ) { //逐一为各节点
      insert( search( p->data, r, p ), p->data ); //查找适当的位置并插入
      p = p->succ; remove( p->pred ); //转向下一节点
   }
}

在这里插入图片描述

2.4 复杂度分析

插入操作和删除操作均只需O(1)时间。查找操作search()所需时间可在O(1)至O(n)之间浮动。

当输入序列已经有序时,该算法中的每次search()操作均仅需O(1)时间,总体运行时间为O(n)。但反过来,若输出序列完全逆序,则各次search()操作所需时间将线性递增,累计共需O( n 2 n^2 n2)时间。在等概率条件下,平均仍需要O( n 2 n^2 n2)时间,换而言之,最好情况发生概率极低。

2.5 性能分析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
结论:
在等概率条件下,平均仍需要O( n 2 n^2 n2)时间。

  • 假定序列中 n 个元素的数值为独立均匀地随机分布,以下结论成立:
  1. 列表的插入排序算法平均需做约 n 2 / 4 = O ( n 2 ) n^2/4 = O(n^2) n2/4=O(n2)次元素比较操作;
  2. 向量的插入排序算法平均需做约 n 2 / 4 = O ( n 2 ) n^2/4 = O(n^2) n2/4=O(n2)次元素移动操作;
  3. 序列的插入排序算法过程中平均有 expected-O(logn)个元素无需移动。

3. 归并排序

3.1 二路归并算法

3.1.1 二路归并算法原理

有序列表的二路归算法同二路归并的向量排序算法,见 向量05——排序器 。能够达到与有序向量二路归并同样高的效率。
在这里插入图片描述

3.1.2 二路归并算法实现

算法思想:List::merge()可以将另一有序列表L中起始于节点q、长度为m的子列表,与当前有序列表中起始于节点p、长度为n的子列表做二路归并。

template <typename T> //有序列表的归并:当前列表中自p起的n个元素,与列表L中自q起的m个元素归并
ListNodePosi<T> List<T>::merge( ListNodePosi<T> p, Rank n,List<T>& L, ListNodePosi<T> q, Rank m ) {
   ListNodePosi<T> pp = p->pred; //归并之后p可能不再指向首节点,故需先记忆,以便在返回前更新
   while ( ( 0 < m ) && ( q != p ) ) //q尚未出界(或在mergeSort()中,p亦尚未出界)之前
      if ( ( 0 < n ) && ( p->data <= q->data ) ) //若p尚未出界且v(p) <= v(q),则
         { p = p->succ; n--; } //p直接后移,即完成归入
      else //否则,将q转移至p之前,以完成归入
         { insert( L.remove( ( q = q->succ )->pred ), p ); m--; }
   return pp->succ; //更新的首节点
}

3.1.3 归并时间

        ~~~~~~~        merge()的时间成本主要消耗于其中的迭代,当且仅当后一子列表中所有节点均处理完毕时,迭代才会终止。因此,在最好情况下,共需迭代m次;而在最坏情况下,则需迭代n次。
        ~~~~~~~        总体而言,共需O(n + m)时间,线性正比于两个子列表的长度之和。

3.2 分治策略

3.2.1 实现

在这里插入图片描述

template <typename T> //列表的归并排序算法:对起始于位置p的n个元素排序
void List<T>::mergeSort( ListNodePosi<T>& p, Rank n ) { // valid(p) && Rank(p) + n <= size
   if ( n < 2 ) return; //若待排序范围已足够小,则直接返回;否则...
   Rank m = n >> 1; //以中点为界
   ListNodePosi<T> q = p; for ( Rank i = 0; i < m; i++ ) q = q->succ; //找到后子列表起点
   mergeSort( p, m ); mergeSort( q, n - m ); //前、后子列表各分别排序
   p = merge( p, m, *this, q, n - m ); //归并
} //注意:排序后,p依然指向归并后区间的(新)起点

3.2.2 排序时间

在子序列的划分阶段,向量与列表归并排序算法之间存在细微但本质的区别。前者支持循秩访问的方式,故可在O(1)时间内确定切分中点;后者仅支持循位置访问的方式,故不得不为此花费O(n)时间。幸好在有序子序列的合并阶段二者均需O(n)时间,故二者的渐进时间复杂度依然相等O(nlogn)。

尽管二路归并算法并未对子列表的长度做出任何限制,但这里出于整体效率的考虑,在划分子列表时宁可花费O(n)时间使得二者尽可能接近于等长。反之,若为省略这部分时间而不保证划分的均衡性,则反而可能导致整体效率的下降。

结论:
若为节省每次子列表的划分时间,而直接令 m = min(c, n/2),其中 c 为较小的常数(比如 5),则总体复杂度反而会上升至 O( n 2 n^2 n2)。
在这里插入图片描述

4. 总结

  1. 选择排序:U[o,r) + S[r,n)。从未排序元素中挑选最大者并使之就位。时间复杂度为θ( n 2 n^2 n2),移动操作远远小于起泡排序。
  2. 插入排序:S[o,r) + U[r,n)。从未排序元素中挑选最大者并使之就位。输入敏感型算法,最好情况1次比较,0次交换,累计O(n)时间(发生概率低)。最坏情况第k次迭代,需O(k)次比较,1次交换,累计O( n 2 n^2 n2)时间。
  3. 归并排序:前提是在划分子列表时宁可花费O(n)时间使得二者尽可能接近于等长,渐进复杂度为O(nlogn)。

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

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

相关文章

源支付V7开源版,源支付开源版,已去除安装扩展

源支付V7开源版&#xff0c;源支付开源版&#xff0c;已去除安装扩展 上传源码 设置伪静态 已去除安装扩展&#xff0c;直接上传就可以安装 开源版通道少了好几个 视频教程&#xff1a;https://www.bilibili.com/video/BV1mZ42177VY/

基于OpenCV-DNN的YOLOv9目标检测实现

⚠申明&#xff1a; 未经许可&#xff0c;禁止以任何形式转载&#xff0c;若要引用&#xff0c;请标注链接地址。 全文共计3077字&#xff0c;阅读大概需要3分钟 &#x1f308;更多学习内容&#xff0c; 欢迎&#x1f44f;关注&#x1f440;【文末】我的个人微信公众号&#xf…

力扣打卡第二天

206. 反转链表 class Solution { public:ListNode* reverseList(ListNode* head) {// //迭代法// ListNode *pre nullptr;// ListNode *curr head;// while(curr){// ListNode *next curr -> next;// curr -> next pre;// pre curr;// curr next;/…

【实时数仓架构】方法论

笔者不是专业的实时数仓架构&#xff0c;这是笔者从其他人经验和网上资料整理而来&#xff0c;仅供参考。写此文章意义&#xff0c;加深对实时数仓理解。 一、实时数仓架构技术演进 1.1 四种架构演进 1&#xff09;离线大数据架构 一种批处理离线数据分析架构&#xff0c;…

SFOS1:开发环境搭建

一、简介 最近在学习sailfish os的应用开发&#xff0c;主要内容是QmlPython。所以&#xff0c;在开发之前需要对开发环境&#xff08;virtualBox官方SDKcmake编译器python&#xff09;进行搭建。值得注意的是&#xff0c;我的开发环境是ubuntu22.04。如果是windows可能大同小异…

ZooKeeper以及DolphinScheduler的用法

目录 一、ZooKeeper的介绍 数据模型 ​编辑 操作使用 ①登录客户端 ​编辑 ②可以查看下面节点有哪些 ③创建新的节点&#xff0c;并指定数据 ④查看节点内的数据 ⑤、删除节点及数据 特殊点&#xff1a; 运行机制&#xff1a; 二、DolphinScheduler的介绍 架构&#…

回溯法——(1)装载问题(C语言讲解)

目录 一、装载问题 1.问题概括&#xff1a; 2.解决方案&#xff08;思路&#xff09;&#xff1a; 3.图片讲解&#xff08;超详细&#xff09;&#xff1a; 4.代码分析&#xff1a; 二、算法改进&#xff1a;引入上界函数 1.问题概念&#xff1a; 2.图片讲解&#xff1a…

Room简单实操

1. Room介绍&#xff0c;直接Copy官网介绍&#xff1a; Room 持久性库在 SQLite 上提供了一个抽象层&#xff0c;以便在充分利用 SQLite 的强大功能的同时&#xff0c;能够流畅地访问数据库。具体来说&#xff0c;Room 具有以下优势&#xff1a; 提供针对 SQL 查询的编译时验…

深入理解分布式事务⑧ ---->MySQL 事务的实现原理 之 MySQL 事务流程(MySQL 事务执行流程 和 恢复流程)详解

目录 MySQL 事务的实现原理 之 MySQL 事务流程&#xff08;MySQL 事务执行流程 和 恢复流程&#xff09;详解MySQL 事务流程1、MySQL 事务执行流程1-1&#xff1a;MySQL 事务执行流程如图&#xff1a; 2、MySQL 事务恢复流程2-1&#xff1a;事务恢复流程如下图&#xff1a; MyS…

基于点灯Blinker的ESP8266远程网络遥控LED

本文介绍基于ESP8266模块实现的远程点灯操作&#xff0c;手机侧APP选用的是点灯-Blinker&#xff0c;完整资料及软件见文末链接 一、ESP8266模块简介 ESP8266是智能家居等物联网场景下常用的数传模块&#xff0c;具有强大的功能&#xff0c;通过串口转WIFI的方式可实现远距离…

区块链扩容:水平扩展 vs.垂直扩展

1. 引言 随着Rollups 的兴起&#xff0c;区块链扩容一直集中在模块化&#xff08;modular&#xff09;vs. 整体式&#xff08;monolithic&#xff09;之争。 如今&#xff0c;模块化与整体式这种一分为二的心理模型&#xff0c;已不适合于当前的扩容场景。本文&#xff0c;将展…

【C语言回顾】字符函数、字符串函数,内存函数

前言1. 字符函数1.1 字符分类函数1.2 字符转换函数1.2.1 tolower&#xff08;将大写字母转化为小写字母&#xff09;1.2.2 toupper&#xff08;将小写字母转化为大写字母&#xff09; 2. 字符串函数2.1 求字符串长度函数 strlen2.2 字符串输入函数 gets()&fgets()2.2.1 get…

虚拟机网络实现桥接模式

虚拟机网络实现桥接模式 虚拟化软件&#xff1a;VMware 17 Linux&#xff1a;rocky8_9 主机&#xff1a;Win10 文章目录 虚拟机网络实现桥接模式1. 桥接模式介绍2. 查看Win本机的网络信息&#xff08;以笔记本电脑以WiFi联网为例&#x…

vue快速入门(五十五)插槽基本用法

注释很详细&#xff0c;直接上代码 上一篇 新增内容 当传输内容只有一种时的基础写法 源码 App.vue <template><div id"app"><h1>被淡化的背景内容</h1><my-dialog><!-- 插槽内容:文字以及dom结构都可以传 --><span>你确…

【LLM 论文】背诵增强 LLM:Recitation-Augmented LM

论文&#xff1a;Recitation-Augmented Language Models ⭐⭐⭐ ICLR 2023, Google Research, arXiv:2210.01296 Code&#xff1a;github.com/Edward-Sun/RECITE 文章目录 论文速读 论文速读 论文的整体思路还是挺简单的&#xff0c;就是让 LLM 面对一个 question&#xff0c;…

蓝桥杯-路径之谜

题目描述 小明冒充X星球的骑士&#xff0c;进入了一个奇怪的城堡。城堡里面什么都没有&#xff0c;只有方形石头铺成的地面。 假设城堡的地面时n*n个方格。如下图所示。 按习俗&#xff0c;骑士要从西北角走到东南角。可以横向或者纵向移动&#xff0c;但是不能斜着走&#x…

详解SDRAM基本原理以及FPGA实现读写控制(一)

文章目录 一、SDRAM简介二、SDRAM存取结构以及原理2.1 BANK以及存储单元结构2.2 功能框图2.3 SDRAM速度等级以及容量计算 三、SDRAM操作命令3.1 禁止命令&#xff1a; 4b1xxx3.2 空操作命令&#xff1a;4b01113.3 激活命令&#xff1a;4b00113.4 读命令&#xff1a;4b01013.5 写…

使用docker-compose编排Lnmp(dockerfile) 完成Wordpress

目录 一、 Docker-Compose 1.1Docker-Compose介绍 1.2环境准备 1.2.1准备容器目录及相关文件 1.2.2关闭防火墙关闭防护 1.2.3下载centos:7镜像 1.3Docker-Compose 编排nginx 1.3.1切换工作目录 1.3.2编写 Dockerfile 文件 1.3.3修改nginx.conf配置文件 1.4Docker-Co…

GDPU Java 天码行空10

&#xff08;一&#xff09;实验目的 1、掌握JAVA中文件、IO类及其构造方法&#xff1b; 2、重点掌握文件类型所具有的文件操作方法&#xff1b; 3、重点掌握IO中类所具有的IO操作方法&#xff1b; 4、熟悉递归调用的思想及应用&#xff1b; 5、掌握IO中读写常用方法。 &…

鸿蒙UI复用

鸿蒙UI复用 简介BuilderBuilder的使用方式一Builder的使用方式二Builder的使用方式三 Component使用Component复用UI 简介 在页面开发过程中&#xff0c;会遇到有UI相似的结构&#xff0c;如果每个UI都单独声明一份&#xff0c;会产生大量冗余代码&#xff0c;不利于阅读。遇到…