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

文章目录

  • 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)。

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

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

相关文章

每天五分钟深度学习:数学中常见函数中的导数

本文重点 导数是微积分学中的一个核心概念,它描述了函数在某一点附近的变化率。在物理学、工程学、经济学等众多领域中,导数都发挥着极其重要的作用。本文旨在详细介绍数学中常见函数的导数,以期为读者提供一个全面而深入的理解。 数学中常见的导数 常数函数的导数 对于常数…

Raft共识算法笔记,MIT6.824,

处理leader和follow的一个重要思路是多数投票&#xff0c;确保系统中存在奇数个服务器&#xff08;例如3台&#xff09;。进行任何操作都需要来自多数服务器的同意&#xff0c;例如3台服务器中的2台。如果没有多数同意&#xff0c;系统会等待。为什么多数投票有助于避免脑裂问题…

springboot项目 字典/枚举翻译 终极解决方案 AOP+自定义注解+递归实体字段+实体动态三级缓存+责任链+多种转换方式

目录 前言实现思路技术确定 食用方式效果使用样例项目中使用第一步 复制包第二步 实现LoadDictDatabase并将其注入容器第三步 标识需要翻译的字段第四步 标识需要翻译的方法第五步 调用需要翻译的方法 实现细节TODO 前言 字典,即在存储介质中进行存储时,为了避免业务上对其名称…

计数排序,基数排序,桶排序

目录 计数排序: 基数排序&#xff1a; 桶排序: 计数排序: 计数排序是一种非比较型整数排序算法&#xff0c;特别适用于一定范围内的整数排序。它的核心思想是使用一个额外的数组&#xff08;称为计数数组&#xff09;来计算每个值的出现次数&#xff0c;然后根据这些计数信…

[贪心] 区间选点问题

905. 区间选点 - AcWing题库 思路&#xff1a;就是将所有区间按照右端点排序&#xff0c; 然后选取一些区间的右端点 代码&#xff1a; #include <iostream> #include <algorithm> #include <vector> using namespace std; const int N 100010;typedef p…

Flask与HTTP

一、请求响应循环 “请求-响应循环”&#xff1a;客户端发出请求&#xff0c;服务器处理请求并返回响应。 Flask Web程序的工作流程&#xff1a; 当用户访问一个URL&#xff0c;浏览器便生成对应的HTTP请求&#xff0c;经由互联网发送到对应的Web服务器。Web服务器接收请求&a…

信号,信号列表,信号产生方式,信号处理方式

什么是信号 信号在我们的生活中非常常见&#xff1b;如红绿灯&#xff0c;下课铃&#xff0c;游戏团战信号&#xff0c;这些都是信号&#xff1b;信号用来提示接收信号者行动&#xff0c;但接收信号的人接收到信号会进行一系列的行为&#xff0c;完成某个动作&#xff1b;这就…

基于Java EE平台项目管理系统的设计与实现(论文 + 源码)

【免费】基于javaEE平台的项目管理系统.zip资源-CSDN文库https://download.csdn.net/download/JW_559/89267688 基于Java EE平台项目管理系统的设计与实现 摘 要 随着社会信息化的发展&#xff0c;很多的社会管理问题也一并出现了根本性变化&#xff0c;项目公司的报表及文…

【YOLO】目标检测 YOLO框架之train.py参数含义及配置总结手册(全)

1.一直以来想写下基于YOLO开源框架的系列文章&#xff0c;该框架也是日常项目开发中常用的一款工具&#xff0c;最近刚好挤时间梳理、总结下这块儿的知识体系。 2.熟悉、梳理、总结下YOLO目标检测相关知识体系&#xff0c;之前实战配置时总是临时性检索些注释含义&#xff0c;但…

JVM组成之类加载器

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

【Java】山外有山,类外还有类

【Java】山外有山&#xff0c;类外还有类 内部类是Java语言中的一种特性&#xff0c;它允许在另一个类中定义一个类。 内部类可以是静态的&#xff08;不依赖于外部类的实例&#xff09;&#xff0c;也可以是非静态的&#xff08;依赖于外部类的实例&#xff09;。 在本篇博…

在R的 RGui中,使用devtools 安装trajeR

创建于&#xff1a;2024.5.5 文章目录 1. 报错信息2. 尝试使用指定的清华镜像&#xff0c;没有解决3. 找到原因&#xff1a;官网把包删除了4. 尝试从网上下载&#xff0c;然后安装。没有成功5. 使用devtools安装5.1 尝试直接安装&#xff1a;install.packages("devtools&q…

【智能算法应用】混合粒子群算法求解CVRP问题

目录 1.算法原理2.数学模型3.结果展示4.参考文献5.代码获取 1.算法原理 【智能算法】粒子群算法&#xff08;PSO&#xff09;原理及实现 经典PSO算法用于连续空间优化问题&#xff0c;VRP问题为离散组合优化问题&#xff0c;涉及如何有效地分配一组车辆去访问多个客户点&…

OSEK的设计哲学与架构

1 前言 OSEK是为单核分布式嵌入式控制单元量身定制的实时系统&#xff0c;对事件驱动&#xff08;event driven&#xff09;的硬实时控制系统具有良好的适配性。OSEK没有强求不同软件模块间的完全兼容性&#xff0c;而是将重心放到了软件的可移植性上来。简单来说&#xff0c;与…

[报错解决]Communications link failure

报错 主机IDEA项目连接虚拟机的数据库报错。 主要报错信息有&#xff1a; com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received a…

智慧旅游引领未来风尚,科技助力旅行更精彩:科技的力量推动旅游业创新发展,为旅行者带来更加便捷、高效和智能的旅行服务

目录 一、引言 二、智慧旅游的概念与特点 &#xff08;一&#xff09;智慧旅游的概念 &#xff08;二&#xff09;智慧旅游的特点 三、科技推动旅游业创新发展 &#xff08;一&#xff09;大数据技术的应用 &#xff08;二&#xff09;人工智能技术的应用 &#xff08;…

Linux Ubuntu 开机自启动浏览器

终端输入命令&#xff1a;gnome-session-properties 打开启动设置 如果提示&#xff1a;Command ‘gnome-session-properties’ not found, but can be installed with: apt install gnome-startup-applications 则执行&#xff1a;apt install gnome-startup-applications安装…

一、写给Android开发者之harmony入门

一、创建新项目 对比 android-studio&#xff1a;ability类似安卓activity ability分为两种类型(Stage模型) UIAbility和Extensionability&#xff08;提供系统服务和后台任务&#xff09; 启动模式 1、 singleton启动模式&#xff1a;单例 2、 multiton启动模式&#xff1…

数据结构十:哈希表

本次将从概念上理解什么是哈希表&#xff0c;理论知识较多&#xff0c;满满干货&#xff0c;这也是面试笔试的一个重点区域。 目录 一、什么是哈希表 1.0 为什么会有哈希表&#xff1f; 1.1 哈希表的基本概念 1.2 基本思想 1.3 举例理解 1.4 存在的问题 1.5 总结 二、…

libcity笔记:参数设置与参数优先级

1 参数优先级 高优先级的参数会覆盖低优先级的同名参数 Libcity中的优先级顺序维&#xff1a; 命令行参数&#xff08;命令行python run_model.py时导入的&#xff09; > 用户定义配置文件&#xff08;命令行python run_model.py时由config_file导入的&#xff09; >…