【数据结构(邓俊辉)学习笔记】列表02——无序列表

news2024/12/28 3:07:58

文章目录

  • 0.概述
  • 1.插入与构造
    • 1.1 插入
      • 1.1.1 前插入
      • 1.1.2后插入
      • 1.1.3 复杂度
    • 1.2 基于复制构造
      • 1.2.1 copyNodes()
      • 1.2.2 基于复制构造
      • 1.2.3 复杂度
  • 2.删除与析构
    • 2.1 删除
      • 2.1.1 实现
      • 2.1.2 复杂度
    • 2.2 析构
      • 2.2.1 释放资源及清除节点
      • 2.2.2 复杂度
  • 3.查找
    • 3.1 实现
    • 3.2 复杂度
  • 4.唯一化
    • 4.1 实现
    • 4.2 复杂度
  • 5.遍历

0.概述

介绍下无序列表相关接口,相关接口需要依赖ListNode类,接口如下:

using Rank = unsigned int; //秩

template <typename T> struct ListNode;
template <typename T> using ListNodePosi = ListNode<T>*; //列表节点位置
template <typename T> struct ListNode { //列表节点模板类(以双向链表形式实现)
// 成员
   T data; ListNodePosi<T> pred, succ; //数值、前驱、后继
// 构造函数
   ListNode() {} //针对header和trailer的构造
   ListNode ( T e, ListNodePosi<T> p = NULL, ListNodePosi<T> s = NULL )
      : data( e ), pred( p ), succ( s ) {} //默认构造器
// 操作接口
   ListNodePosi<T> insertAsPred( T const& e ); //紧靠当前节点之前插入新节点
   ListNodePosi<T> insertAsSucc( T const& e ); //紧随当前节点之后插入新节点
};

1.插入与构造

1.1 插入

将节点插至列表的插入算法接口多种,

template <typename T> 
ListNodePosi<T> List<T>::insertAsFirst( T const& e )
   { _size++; return header->insertAsSucc( e ); } // e当作首节点插入

template <typename T> 
ListNodePosi<T> List<T>::insertAsLast( T const& e )
   { _size++; return trailer->insertAsPred( e ); } // e当作末节点插入

template <typename T> 
ListNodePosi<T> List<T>::insert( ListNodePosi<T> p, T const& e )
   { _size++; return p->insertAsSucc( e ); } // e当作p的后继插入

template <typename T> 
ListNodePosi<T> List<T>::insert( T const& e, ListNodePosi<T> p )
   { _size++; return p->insertAsPred( e ); } // e当作p的前驱插入

但都可转化为列表节点对象的前插入或后插入接口。

1.1.1 前插入

在这里插入图片描述
拓扑连接关系如图a,b,c,d。

注意:列表规模记录的更新由上层调用者负责。另外,得益于头哨兵节点(头尾节点)的存在,即便当前节点为列表的首节点也适用。

template <typename T> 
ListNodePosi<T> List<T>::insert( T const& e, ListNodePosi<T> p ) 
   { _size++; return p->insertAsPred( e ); } // e当作p的前驱插入

template <typename T> //将e紧靠当前节点之前插入于当前节点所属列表(设有哨兵头节点header)
ListNodePosi<T> ListNode<T>::insertAsPred( T const& e ) {
   ListNodePosi<T> x = new ListNode( e, pred, this ); //创建新节点
   pred->succ = x; pred = x; //设置正向链接
   return x; //返回新节点的位置
}

参数e在参数p前,以方便理解接口语义。insert(e,p),节点p前插入元素e。

1.1.2后插入

template <typename T> 
ListNodePosi<T> List<T>::insert( ListNodePosi<T> p, T const& e )//接口与语义对应,参数e在p后
   { _size++; return p->insertAsSucc( e ); } // e当作p的后继插入

template <typename T> //将e紧随当前节点之后插入于当前节点所属列表(设有哨兵尾节点trailer)
ListNodePosi<T> ListNode<T>::insertAsSucc( T const& e ) {
   ListNodePosi<T> x = new ListNode( e, this, succ ); //创建新节点
   succ->pred = x; succ = x; //设置逆向链接
   return x; //返回新节点的位置
}

1.1.3 复杂度

上述两种插入操作过程,仅涉及局部的两个原有节点和一个新节点,且不含任何迭代或递归。若假设当前节点已经定位,不计入此前的查找所消耗的时间,则它们都可在常数时间内完成。

1.2 基于复制构造

在这里插入图片描述

1.2.1 copyNodes()

尽管这里提供了多种形式,以允许对原列表的整体或局部复制,但其实质过程均大同小异,都可概括和转化为底层内部方法copyNodes()。

template <typename T> //列表内部方法:复制列表中自位置p起的n项
void List<T>::copyNodes( ListNodePosi<T> p, Rank n ) { // p合法,且至少有n-1个真后继
   init(); //创建头、尾哨兵节点并做初始化
   while ( n-- ) { insertAsLast( p->data ); p = p->succ; } //将起自p的n项依次作为末节点插入
}

1.2.2 基于复制构造

基于上述copyNodes()方法可以实现多种接口,通过复制已有列表的区间或整体,构造出新列表。

template <typename T> //复制列表中自位置p起的n项(assert: p为合法位置,且至少有n-1个后继节点)
List<T>::List( ListNodePosi<T> p, Rank n ) { copyNodes( p, n ); }

template <typename T> //整体复制列表L
List<T>::List( List<T> const& L ) { copyNodes( L.first(), L._size ); }

template <typename T> //复制L中自第r项起的n项(assert: r+n <= L._size)
List<T>::List( List<T> const& L, Rank r, Rank n ) {
   ListNodePosi<T> p = L.first();
   while ( 0 < r-- ) p = p->succ;
   copyNodes ( p, n );
}

1.2.3 复杂度

复制接口的总体复杂度应为O(r + n + 1)。

2.删除与析构

2.1 删除

2.1.1 实现

在这里插入图片描述
删除节点后,列表的拓扑连接关系如图a,b,c,d。

template <typename T> 
T List<T>::remove( ListNodePosi<T> p ) { //删除合法节点p
   T e = p->data; //备份待删除节点的数值(假定T类型可直接赋值)
   p->pred->succ = p->succ; p->succ->pred = p->pred; //短路联接
   delete p; _size--; //释放节点,更新规模
   return e; //返回备份的数值
} //O(1)

2.1.2 复杂度

以上过程仅涉及常数次基本操作,故若不计入此前为查找并确定位置p所消耗的时间,列表的节点删除操作可在常数时间内完成。

2.2 析构

在这里插入图片描述

2.2.1 释放资源及清除节点

template <typename T> 
List<T>::~List() //列表析构器
{ clear(); delete header; delete trailer; } //清空列表,释放头、尾哨兵节点

template <typename T> 
Rank List<T>::clear() { //清空列表
   Rank oldSize = _size;
   while ( 0 < _size ) remove ( header->succ ); //反复删除首节点,直至列表变空
   return oldSize;
}

列表的析构需首先调用clear()接口删除并释放所有对外部有效的节点,然后释放内部的头、尾哨兵节点。

2.2.2 复杂度

这里的时间消耗主要来自clear()操作,该操作通过remove()接口反复删除列表的首节点。因此,clear()方法以及整个析构方法的运行时间应为O(n),线性正比于列表原先的规模。

3.查找

3.1 实现

在这里插入图片描述

在这里插入图片描述

template <typename T> //在无序列表内节点p(可能是trailer)的n个(真)前驱中,找到等于e的最后者
ListNodePosi<T> List<T>::find( T const& e, Rank n, ListNodePosi<T> p ) const {
   while ( 0 < n-- ) //(0 <= n <= Rank(p) < _size)对于p的最近的n个前驱,从右向左
      if ( e == ( p = p->pred )->data ) return p; //逐个比对,直至命中或范围越界
   return NULL; //p越出左边界意味着区间内不含e,查找失败
} //失败时,返回NULL

注意:若列表中待查找的元素e有多个,返回秩最大者。

3.2 复杂度

以上算法的思路及过程,与无序向量的顺序查找算法Vector::find()相仿,故时间复杂度也应是O(n),线性正比于查找区间的宽度。

4.唯一化

4.1 实现

在这里插入图片描述

在这里插入图片描述
与算法Vector::deduplicate()类似,这里也是自前向后依次处理各节点p,一旦通过find()接口在p的前驱中查到雷同者,则随即调用remove()接口将其删除。

4.2 复杂度

与无序向量的去重算法一样,该算法总共需做O(n)步迭代。每一步迭代中find()操作所需的时间线性正比于查找区间宽度,即当前节点的秩;列表节点每次remove()操作仅需常数时间。因此,总体执行时间应为:
1 + 2 + 3 + … + n = n∙(n + 1) / 2 = O ( n 2 ) (n^2) (n2)
相对于无序向量,尽管此处节点删除操作所需的时间减少,但总体渐进复杂度并无改进。

5.遍历

在这里插入图片描述

template <typename T> 
void List<T>::traverse( void ( *visit )( T& ) ) //借助函数指针机制遍历
{  for ( ListNodePosi<T> p = header->succ; p != trailer; p = p->succ ) visit ( p->data );  }


template <typename T> template <typename VST> //元素类型、操作器
void List<T>::traverse( VST& visit ) //借助函数对象机制遍历
{  for ( ListNodePosi<T> p = header->succ; p != trailer; p = p->succ ) visit ( p->data );  }

该接口的设计思路与实现方式,与向量的对应接口如出一辙,复杂度也相同O(n)。

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

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

相关文章

FFmpeg学习记录(四)——SDL音视频渲染实战

1.SDL使用的基本步骤 SDL Init/sDL _Quit()SDL_CreateWindow()/SDL_DestoryWindow()SDL CreateRender() SDL_Windows *windows NULL;SDL_Init(SDL_INIT_VIDEO);window SDL_CreateWindow("SDL2 Windows",200,200, 640,480,SDL_WINDOW_SHOWN);if(!window) {printf(&…

【C语言回顾】数据在内存中的存储

前言1. 概述2. 大小端字节序和字节序判断2.1 大端字节序&#xff08;Big-Endian&#xff09;2.2 小端字节序&#xff08;Little-Endian&#xff09;2.3 判断字节序的示例 3. 数据在内存中的存储3.1 整数在内存中的存储3.2 浮点数在内存中的存储 结语 ↓ 上期回顾: 【C语言回顾】…

STM32 01

1、编码环境 1.1 安装keil5 1.2 安装STM32CubeMX 使用STM32CubeMX可以通过界面的方式&#xff0c;快速生成工程文件 安装包可以从官网下载&#xff1a;https://www.st.com/zh/development-tools/stm32cubemx.html#overview 安装完要注意更新一下固件包的位置&#xff0c;因为…

A股上市公司财务松弛数据集(2000-2022年)

01、数据介绍 财务松弛是指企业在运营过程中&#xff0c;由于各种原因导致其财务状况出现一定程度的松弛或宽裕状态。这种状态通常表现为企业持有较多的现金和流动性资产&#xff0c;同时负债相对较少&#xff0c;或者企业有较多的未使用授信额度等。 本数据包括&#xff1a;…

伺服电机初识

目录 一、伺服电机的介绍二、伺服电机的基本原理三、伺服电机的技术特点四、伺服电机的分类五、实际产品介绍1、基本技术规格&#xff1a;2、MD42电机硬件接口3、通讯协议介绍3.1 通讯控制速度运行3.2 通讯控制位置运行3.3 通讯控制转矩运行 4、状态灯与报警信息 一、伺服电机的…

C语言之整形提升和算术转换

目录 前言 一、整形提升 二、算术转换 总结 前言 本文主要介绍C语言中的整形提升和算术转换的概念和意义&#xff0c;以及例题帮助理解&#xff0c;了解之后&#xff0c;我们就能知道在C语言中&#xff0c;字符型变量如何计算以及如果变量的类型、字节大小不一致的情况下&am…

JVM组成之类加载器

类加载器&#xff08;ClassLoader&#xff09;&#xff1a;是Java虚拟机提供给应用程序去实现获取类和接口字节码数据的技术。 类加载器多数是有Java编写的&#xff0c;也有部分是c编写的&#xff0c;负责接收来自外部的二进制数据&#xff0c;然后执行JNI&#xff08;也就是本…

2010NOIP普及组真题 2. 接水问题

线上OJ&#xff1a; 一本通&#xff1a;http://ybt.ssoier.cn:8088/problem_show.php?pid1950 解法一、朴素模拟 核心思想&#xff1a; 朴素模拟&#xff1a; 1、先给每个b[i]水龙头分配一个人a[i]&#xff0c;b[i] 表示水龙头的剩余时间。同时标记该水龙头为 used 使用中 2…

(论文阅读-优化器)A Cost Model for SPARK SQL

目录 Abstract 1 Introduction 2 Related Work 3 Background and Spark Basics 4 Cost Model Basic Bricks 4.1 Cluster Abastraction and Cost Model Parameters 4.2 Read 4.3 Write 4.4 Shuffle Read 4.5 Broadcast 5 Modeling GPSJ Queries 5.1 Statistics and S…

交互中的“互”难以产生的原因

脑机交互技术的目标是通过分析和解读大脑活动&#xff0c;将其与特定的意图、指令或行为连接起来。通过训练和分析&#xff0c;可以建立起大脑活动与特定行为或意图之间的关联模型&#xff0c;从而实现脑机交互的应用&#xff0c;例如控制外部设备、传递信息等。然而&#xff0…

视频教程下载:为 GPTs 商店构建 10 个 GPTs获得被动收入

欢迎来到 AI 驱动的内容创作新时代 - GPT 商店。这门综合课程是您成为定制和利用 GPT 模型解决多样化应用的专家的路线图。无论你是错过了应用商店革命的初始浪潮还是乘着它取得了成功&#xff0c;这都是你站在下一个重大数字飞跃前沿的机会。 课程模块&#xff1a; - 介绍 Ch…

抓包证书安装到安卓7.0+手机

前言: 首先理解一下,这个不只是证书到浏览器,而是抓包证书到安卓7.0+手机上的文章; 还有一点区分,在浏览器上装的证书,只是让抓包工具可以抓取手机浏览器的包,而不是抓取手机app上的包; 如果你的证书只是简单的在浏览器下进行安装,那么你的手机app是走不了代理网络的…

iOS - Undefined symbols: 解决方法

Undefined symbols: 是让人苦恼的报错&#xff0c;如何知道是 哪个 symbols 不对呢&#xff1f; 今天探索到下面的方法&#xff1a; 1、点击导航上方 最右侧的按钮&#xff0c;查看历史报错 2、选中报错信息&#xff0c;右键选择 Expand All Transcripts 在出现的详细信息面…

【Redis】Redis命令(一)

1.基本命令 1.1.切换DB 默认使用的是 0 号 DB&#xff0c;可以通过 select db 索引来切换 DB 1.2.查看 key 数量 dbsize 命令可以查看当前数据库中 key 的数量 1.3.删除当前库中数据 flushdb 命令仅仅删除的是当前数据库中的数据&#xff0c;不影响其它库 1.4.删除所有库中数据…

Spring Cloud架构进化实操:Eureka、Apollo、OpenFeign、Ribbon、Zuul组件

文章目录 前言一、引出二、服务注册与发现2.1 创建Eureka注册中心2.1.1 引入pom依赖2.1.2 配置yaml2.1.3 启动服务21.4 测试访问 2.2 创建服务提供者2.2.1 配置yaml2.2.2 启动服务2.2.3 测试访问 2.3 创建服务消费者2.3.1 服务提供者接口2.3.2 服务消费者调用接口 三、负载均衡…

如何高速下载,百度 阿里 天翼 等网盘内的内容

如何高速下载&#xff0c;百度 阿里 天翼 等网盘内的内容&#x1f3c5; 前言教程下期更新预报&#x1f3c5; 前言 近段时间经常给大家分享各种视频教程&#xff0c;由于分享的资料是用迅雷网盘存的&#xff0c;但是绝大部分用户都是使用的某度&#xff0c;阿某的这些网盘&…

OpenCV如何使用 GDAL 读取地理空间栅格文件(72)

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇:OpenCV的周期性噪声去除滤波器(70) 下一篇 :OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 目录 目标 代码&#xff1a; 解释&#xff1a; 如何使用 GDAL 读取栅格数据 注意 …

DS:顺序表、单链表的相关OJ题训练(1)

欢迎各位来到 Harper.Lee 的学习小世界&#xff01; 博主主页传送门&#xff1a;Harper.Lee的博客主页 想要一起进步的uu可以来后台找我交流哦&#xff01; 在DS&#xff1a;单链表的实现 和 DS&#xff1a;顺序表的实现这两篇文章中&#xff0c;我详细介绍了顺序表和单链表的…

【区块链】比特币架构

比特币架构 2009年1月&#xff0c;在比特币系统论文发表两个月之后&#xff0c;比特币系统正式运行并开放了源码&#xff0c;标志着比特币网络的正式诞生。通过其构建的一个公开透明、去中心化、防篡改的账本系统&#xff0c;比特币开展了一场规模空前的加密数字货币体验。在区…

javascript 练习 写一个简单 另类录入 电脑组装报价表 可打印

数据格式 &#xff08;1代表cpu、2代表主板、3代表内存、。。。&#xff09; 1i3 12100 630 2H610 480 3DDR4 3200 16G 220 4500G M.2 299 5300W电源 150 6小机箱 85 7GT 730G 4G 350 8WD 2T 399 9飞利浦 24Led 580 主代码 Html JS <!DOCTYPE html> <html lang&qu…