C++:list结构算法

news2025/8/18 22:26:27

List

1.元素在逻辑上具有线性次序,物理地址不做限制。
2.哨兵节点,header和trailer,封装后外部不可见。在这里插入图片描述
3.重载操作符[],实现下标和位置转换。
4.有序查找无序查找
5.前插入算法,首先创建新节点在这里插入图片描述
然后使new成为this节点的前驱,和this节点前驱节点的后继。在这里插入图片描述
6.后插入算法
7.基于复制的构造方法:从源列表中取出n个相邻节点,作为末节点插入新列表
8.删除:p节点的前驱与p节点的后继相连,然后释放p节点。在这里插入图片描述
9.析构:删除列表节点直到列表为空,然后释放列表头尾哨兵节点。
10.去重算法:有序、无序去重
11.排序算法:插入、选择、归并

using Rank = int; //秩
template <typename T> struct ListNode;
template <typename T> using ListNodePosi = ListNode<T>*; //列表节点位置
template <typename T> struct ListNode { //列表节点模板类(以双向链表形式实现)
// 成员
   T data; ListNodePosi<T> pred; ListNodePosi<T> 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 ); //紧随当前节点之后插入新节点
};


template <typename T> class List { //列表模板类

private:
   int _size; ListNodePosi<T> header; ListNodePosi<T> trailer; //规模、头哨兵、尾哨兵

protected:
   void init(); //列表创建时的初始化
   int clear(); //清除所有节点
   void copyNodes ( ListNodePosi<T>, int ); //复制列表中自位置p起的n项
   ListNodePosi<T> merge ( ListNodePosi<T>, int, List<T> &, ListNodePosi<T>, int ); //归并
   void mergeSort ( ListNodePosi<T> &, int ); //对从p开始连续的n个节点归并排序
   void selectionSort ( ListNodePosi<T>, int ); //对从p开始连续的n个节点选择排序
   void insertionSort ( ListNodePosi<T>, int ); //对从p开始连续的n个节点插入排序
   void radixSort(ListNodePosi<T>, int); //对从p开始连续的n个节点基数排序

public:
// 构造函数
   List() { init(); } //默认
   List ( List<T> const& L ); //整体复制列表L
   List ( List<T> const& L, Rank r, int n ); //复制列表L中自第r项起的n项
   List ( ListNodePosi<T> p, int n ); //复制列表中自位置p起的n项
// 析构函数
   ~List(); //释放(包含头、尾哨兵在内的)所有节点
// 只读访问接口
   Rank size() const { return _size; } //规模
   bool empty() const { return _size <= 0; } //判空
   T& operator[] ( Rank r ) const; //重载,支持循秩访问(效率低)
   ListNodePosi<T> first() const { return header->succ; } //首节点位置
   ListNodePosi<T> last() const { return trailer->pred; } //末节点位置
   bool valid ( ListNodePosi<T> p ) //判断位置p是否对外合法
   { return p && ( trailer != p ) && ( header != p ); } //将头、尾节点等同于NULL
   ListNodePosi<T> find ( T const& e ) const //无序列表查找
   { return find ( e, _size, trailer ); }
   ListNodePosi<T> find ( T const& e, int n, ListNodePosi<T> p ) const; //无序区间查找
   ListNodePosi<T> search ( T const& e ) const //有序列表查找
   { return search ( e, _size, trailer ); }
   ListNodePosi<T> search ( T const& e, int n, ListNodePosi<T> p ) const; //有序区间查找
   ListNodePosi<T> selectMax ( ListNodePosi<T> p, int n ); //在p及其n-1个后继中选出最大者
   ListNodePosi<T> selectMax() { return selectMax ( header->succ, _size ); } //整体最大者
// 可写访问接口
   ListNodePosi<T> insertAsFirst ( T const& e ); //将e当作首节点插入
   ListNodePosi<T> insertAsLast ( T const& e ); //将e当作末节点插入
   ListNodePosi<T> insert ( ListNodePosi<T> p, T const& e ); //将e当作p的后继插入
   ListNodePosi<T> insert ( T const& e, ListNodePosi<T> p ); //将e当作p的前驱插入
   T remove ( ListNodePosi<T> p ); //删除合法位置p处的节点,返回被删除节点
   void merge ( List<T> & L ) { merge ( header->succ, _size, L, L.header->succ, L._size ); } //全列表归并
   void sort ( ListNodePosi<T> p, int n ); //列表区间排序
   void sort() { sort ( first(), _size ); } //列表整体排序
   int deduplicate(); //无序去重
   int uniquify(); //有序去重
   void reverse(); //前后倒置(习题)
// 遍历
   void traverse ( void (* ) ( T& ) ); //遍历,依次实施visit操作(函数指针,只读或局部性修改)
   template <typename VST> //操作器
   void traverse ( VST& ); //遍历,依次实施visit操作(函数对象,可全局性修改)
}; //List


template <typename T> void List<T>::init() { //列表初始化,在创建列表对象时统一调用
   header = new ListNode<T>; //创建头哨兵节点
   trailer = new ListNode<T>; //创建尾哨兵节点
   header->succ = trailer; header->pred = NULL;
   trailer->pred = header; trailer->succ = NULL;
   _size = 0; //记录规模
}



template <typename T> //重载下标操作符,以通过秩直接访问列表节点(虽方便,效率低,需慎用)
T& List<T>::operator[] ( Rank r ) const { //assert: 0 <= r < size
   ListNodePosi<T> p = first(); //从首节点出发
   while ( 0 < r-- ) p = p->succ; //顺数第r个节点即是
   return p->data; //目标节点,返回其中所存元素
}
template <typename T> //在无序列表内节点p(可能是trailer)的n个(真)前驱中,找到等于e的最后者
ListNodePosi<T> List<T>::find ( T const& e, int 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

template <typename T> //在有序列表内节点p(可能是trailer)的n个(真)前驱中,找到不大于e的最后者
ListNodePosi<T> List<T>::search ( T const& e, int n, ListNodePosi<T> p ) const {
// assert: 0 <= n <= rank(p) < _size
   do {
      p = p->pred; n--;  //从右向左
   } while ( ( -1 < n ) && ( e < p->data ) ); //逐个比较,直至命中或越界
   return p; //返回查找终止的位置
} //失败时,返回区间左边界的前驱(可能是header)——调用者可通过valid()判断成功与否
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的前驱插入

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; //返回新节点的位置
}
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; //返回新节点的位置
}

template <typename T> //列表内部方法:复制列表中自位置p起的n项
void List<T>::copyNodes ( ListNodePosi<T> p, int n ) { //p合法,且至少有n-1个真后继节点
   init(); //创建头、尾哨兵节点并做初始化
   while ( n-- ) { insertAsLast ( p->data ); p = p->succ; } //将起自p的n项依次作为末节点插入
}
template <typename T> //复制列表中自位置p起的n项(assert: p为合法位置,且至少有n-1个后继节点)
List<T>::List ( ListNodePosi<T> p, int 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, int n ) {
   ListNodePosi<T> p = L.first();
   while ( 0 < r-- ) p = p->succ;
   copyNodes ( p, n );
}
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; //返回备份的数值
}
template <typename T> List<T>::~List() //列表析构器
{ clear(); delete header; delete trailer; } //清空列表,释放头、尾哨兵节点
template <typename T> int List<T>::clear() { //清空列表
   int oldSize = _size;
   while ( 0 < _size ) remove ( header->succ ); //反复删除首节点,直至列表变空
   return oldSize;
}
template <typename T> int List<T>::deduplicate() {
   int oldSize = _size; ListNodePosi<T> p = first();
   for ( Rank r = 0; p != trailer; p = p->succ ) //O(n)
      if ( ListNodePosi<T> q = find(p->data, r, p) )
         remove(q); //此时q与p雷同,但删除前者更为简明
      else r++; //r为无重前缀的长度
   return oldSize - _size; //删除元素总数
}
template <typename T> int List<T>::uniquify() { //成批剔除重复元素,效率更高
   if ( _size < 2 ) return 0; //平凡列表自然无重复
   int oldSize = _size; //记录原规模
   ListNodePosi<T> p = first(); ListNodePosi<T> q; //p为各区段起点,q为其后继
   while ( trailer != ( q = p->succ ) ) //反复考查紧邻的节点对(p, q)
      if ( p->data != q->data ) p = q; //若互异,则转向下一区段
      else remove ( q ); //否则(雷同),删除后者
   return oldSize - _size; //列表规模变化量,即被删除元素总数
}
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 );  }
template <typename T> void List<T>::sort ( ListNodePosi<T> p, int n ) { //列表区间排序
   
   switch ( rand() % 4 ) { //随机选取排序算法。可根据具体问题的特点灵活选取或扩充
      case 1:  insertionSort ( p, n ); break; //插入排序
      case 2:  selectionSort ( p, n ); break; //选择排序
      case 3:  mergeSort ( p, n ); break; //归并排序
      default: radixSort ( p, n ); break; //基数排序
   }
}
template <typename T> //对列表中起始于位置p、宽度为n的区间做插入排序
void List<T>::insertionSort ( ListNodePosi<T> p, int n ) { //valid(p) && rank(p) + n <= size
   for ( int r = 0; r < n; r++ ) { //逐一为各节点
      insert ( search ( p->data, r, p ), p->data ); //查找适当的位置并插入
      p = p->succ; remove ( p->pred ); //转向下一节点
   }
}
template <typename T> //对列表中起始于位置p、宽度为n的区间做选择排序
void List<T>::selectionSort ( ListNodePosi<T> p, int n ) { //valid(p) && rank(p) + n <= size
   ListNodePosi<T> head = p->pred, tail = p;
   for ( int 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--;
   }
}
template <typename T> //列表的归并排序算法:对起始于位置p的n个元素排序
void List<T>::mergeSort ( ListNodePosi<T> & p, int n ) { //valid(p) && rank(p) + n <= size
   if ( n < 2 ) return; //若待排序范围已足够小,则直接返回;否则...
   int m = n >> 1; //以中点为界
   ListNodePosi<T> q = p; for (int i = 0; i < m; i++) q = q->succ; //找到后子列表起点
   mergeSort(p, m); mergeSort(q, n - m); //前、后子列表各分别排序
   p = merge ( p, m, *this, q, n - m ); //归并
} //注意:排序后,p依然指向归并后区间的(新)起点

typedef unsigned int U; //约定:类型T或就是U;或可转换为U,并依此定序

template <typename T> //对列表中起始于位置p、宽度为n的区间做基数排序
void List<T>::radixSort ( ListNodePosi<T> p, int n ) { //valid(p) && rank(p) + n <= size
   ListNodePosi<T> head = p->pred; ListNodePosi<T> tail = p;
   for ( int i = 0; i < n; i++ ) tail = tail->succ; //待排序区间为(head, tail)
   for ( U radixBit = 0x1; radixBit && (p = head); radixBit <<= 1 ) //以下反复地
      for ( int i = 0; i < n; i++ ) //根据当前基数位,将所有节点
         radixBit & U (p->succ->data) ? //分拣为后缀(1)与前缀(0)
            insert( remove( p->succ ), tail ) : p = p->succ;
}

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

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

相关文章

设计模式之代理模式(静态动态)代理

前言&#xff1a;二十三种设计模式中的一种&#xff0c;属于结构型模式。它的作用就是通过提供一个代理类&#xff0c;让我们在调用目标方法的时候&#xff0c;不再是直接对目标方法进行调用&#xff0c;而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥…

PHP设计模式

目录 一、使用设计模式目的 二、设计模式的七大原则 三、创建型模式&#xff08;构建型模式&#xff09; 1、单例模式 代码实例 2、工厂模式 2.1、工厂模式——简单工厂模式 简单工厂模式的代码实例 2.2、工厂模式——工厂方法模式 工厂方法模式的代码实例 2.3、工厂…

java开发环境配置及问题排查

Java程序必须运行在JVM之上&#xff0c;所以&#xff0c;我们第一件事情就是安装JDK。 JDK(Java Development Kit)&#xff0c;是Java开发工具包&#xff0c;它提供了Java的开发环境(提供了编译器javac等工具&#xff0c;用于将java文件编译为class文件)和运行环境(提 供了JVM…

Java内存模型和线程安全

Java内存模型和线程安全Java内存模型引言volatile关键字synchronized关键字Java线程Java线程安全synchronized锁优化锁优化技巧列举自旋锁锁消除锁粗化具体实现轻量级锁偏向锁Java内存模型 引言 对于多核处理器而言,每个核都会有自己单独的高速缓存,又因为这多个处理器共享同一…

JavaWeb-会话技术

JavaWeb-会话技术 1&#xff0c;会话跟踪技术的概述 对于会话跟踪这四个词&#xff0c;我们需要拆开来进行解释&#xff0c;首先要理解什么是会话&#xff0c;然后再去理解什么是会话跟踪: 会话:用户打开浏览器&#xff0c;访问web服务器的资源&#xff0c;会话建立&#xff…

反射机制.

文章目录概述两个疑问关于java.lang.Class的理解获取Class实例的方式哪些类型可以有Class对象了解类的加载器掌握加载配置文件的另一种方式创建运行时类的对象体会动态性获取运行时类的完整结构调用运行时类的制定结构每日一考动态代理概述 1、反射是动态语言的关键 2、动态语…

使用Docker打包镜像并发布

1、docker介绍 Docker 是一个开源的应用容器引擎&#xff0c;以镜像的形式进行发布。docker的图标是一个大鲸鱼驮着许多集装箱在海上航行。大鲸鱼就是docker&#xff0c;集装箱就是一个个容器。容器是完全使用沙箱机制&#xff0c;相互之间不会有任何接口&#xff0c;每个容器都…

高级Spring之BeanFactory 与 ApplicationContext 的区别

ApplicationContext接口 SpringBootApplication public class A01 {private static final Logger log LoggerFactory.getLogger(A01.class);public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {//启动SpringBoot程序…

Python Scipy 显著性检验

Scipy 显著性检验显著性检验&#xff08;significance test&#xff09;就是事先对总体&#xff08;随机变量&#xff09;的参数或总体分布形式做出一个假设&#xff0c;然后利用样本信息来判断这个假设&#xff08;备择假设&#xff09;是否合理&#xff0c;即判断总体的真实情…

Linux基本功系列之userdel命令实战

春节马上要到了&#xff0c;大街上到处都是张灯结彩&#xff0c;喜气洋洋的景象&#xff0c;你们那里也开始了吗&#xff1f; 文章目录一. userdel命令概述二. 语法格式及常用选项语法格式及常用参数三. 参考案例3.1 删除用户但不删除家目录等相关的文件3.2 把用户彻底删除3.3 …

【Linux05-进程控制】进程创建、进程等待、进程退出、进程程序替换(附简易shell实现)

前言 本期分享进程控制的内容。 博主水平有限&#xff0c;不足之处望请斧正&#xff01; 进程的控制主要分为四点&#xff1a; 进程创建进程退出进程等待进程程序替换 进程创建 怎么创建 通过fork创建。 #fork 是什么 创建子进程的函数。&#xff08;使用已经介绍过&am…

Python基础学习 -- 概念

一、变量python的变量定义起来比较随意&#xff0c;不用定义数据类型a123b"123"系统会自动识别a为数值&#xff0c;b为字符串二、关键字定义变量名字的时候&#xff0c;要避开下面的关键字&#xff0c;但是可以通过大小写区分&#xff0c;as123;#错误定义As123;print…

阿里云服务器ECS

云服务器 ECS云服务器ECS&#xff08;Elastic Compute Service&#xff09;是一种简单高效、处理能力可弹性伸缩的计算服务。帮助您构建更稳定、安全的应用&#xff0c;提升运维效率&#xff0c;降低IT成本&#xff0c;使您更专注于核心业务创新。为什么选择云服务器ECS选择云服…

音频如何分割成两段音频?这些实用方法值得收藏

有些时候&#xff0c;我们从网上下载的音频素材可能会出现体积较大、播放时间长等情况&#xff0c;而我们却只需要其中的一小段。这个时候我们就需要借助一些音频分割软件来将重要的音频片段提取出来&#xff0c;从而有助于缩小音频文件的占比以及存储。那么如何如何分割音频呢…

JVM进修之路(一)程序计数器与虚拟机栈

JVM 定义&#xff1a;JVM:Java Virtual Machine&#xff0c;也就是Java运行时所需要的环境&#xff08;Java二进制字节码运行时所需要的环境&#xff09; 好处&#xff1a; 1.java代码一次编写&#xff0c;跨平台运行 2.自动内存管理&#xff0c;垃圾回收 3.数组下标越界检查 4…

千锋Node.js学习笔记

千锋Node.js学习笔记 文章目录千锋Node.js学习笔记写在前面1. 认识Node.js2. NVM3. NPM4. NRM5. NPX6. 模块/包与CommonJS7. 常用内置模块1. url2. querystring3. http4. 跨域jsonpcorsmiddleware&#xff08;http-proxy-middleware&#xff09;5. 爬虫6. events7. File System…

Mysql常用命令练习(一)

Mysql常用命令练习&#xff08;一&#xff09;一、数据库的三层结构二、数据库2.1、创建数据库2.2、查看、删除数据库2.3、备份和恢复数据库三、表3.1、创建表mysql常用的数据类型(列类型)创建表查看表查看表结构练习3.2、修改表修改表名修改表的字符集添加列修改列删除列练习3…

轻量级网络模型ShuffleNet

在学习ShuffleNet内容前需要简单了解卷积神经网络和MobileNet的相关内容&#xff0c;大家可以去看我之前的一篇博客MobileNet发展脉络&#xff08;V1-V2-V3&#xff09;&#xff0c;&#x1f197;&#xff0c;接下来步入正题~卷积神经网络被广泛应用在图像分类、目标检测等视觉…

易盾sdk引起项目的整体耗时问题?

大家好&#xff1a; 我是烤鸭。今年年初的时候&#xff0c;项目接入易盾sdk之后&#xff0c;随着接口调用次数增多(用到易盾sdk的接口)&#xff0c;项目整体性能变差。写篇文章做个复盘记录&#xff0c;其实同事已经写过了&#xff0c;我借鉴部分再拓展一些。 问题描述 突然收…

【JavaEE初阶】第五节.多线程 ( 基础篇 ) 线程安全问题(上篇)

目录 文章目录 前言 一、线程安全的概述 1.1 什么是线程安全问题 1.2 存在线程安全问题的实例 二、线程安全问题及其解决办法 2.1 案例分析 2.2 造成线程不安全的原因 2.3 线程加锁操作解决原子性 问题 &#xff1b; 2.3.1 什么是加锁 2.3.2 使用 synchronized关键字…