SGISTL源码探究-list容器(下)

手动以及使用torch.nn实现前馈神经网络实验 手动以及使用torch.nn实现前馈神经网络 手动实现前馈神经网络解决上述回归、二分类、多分类任务 利用torch.nn解决上述回归、二分类、多分类任务 在多分类任务中使用至少三种不同的激活函数 对多分类任务中的模型评估隐藏层层数和隐藏单元个数对实验结果的影响 阅读详情

前言

在上一小节我们主要介绍了list容器的迭代器及数据结构还有结点的分配及释放,构造和析构函数以及一些简单的操作。在本小节中我们将继续分析list容器提供的一些常见操作。

insert

之前关于list的构造函数的源码中都调用了insert函数,接下来,我们就来分析它,要记得,STL规定insert函数都是向前插入。
它有多个重载版本。

在指定位置前插入一个结点
iterator insert(iterator position, const T& x) {
  //申请并构造一个值为x的结点
  link_type tmp = create_node(x);
  /* 在position前插入结点tmp */
  tmp->next = position.node;
  tmp->prev = position.node->prev;
  (link_type(position.node->prev))->next = tmp;
  position.node->prev = tmp;
  //返回指向新插入结点的迭代器
  return tmp;
}

还有一个版本是不指定插入结点的值的,即调用默认的构造函数。

iterator insert(iterator position) { return insert(position, T()); }
在position前插入[first, last)迭代器指向的元素
template <class InputIterator>
void insert(iterator position, InputIterator first, InputIterator last)
{
  /* 利用insert向前插入的特性
   * 每次都朝position插入
   */
  for( ; first != last; ++first)
    insert(position, *first);
}
在position前插入n个值为x的元素

由于n可能有不同的数据类型,所以有多个重载版本,不过最后都强转成size_type进行操作。

void insert(iterator pos, size_type n, const T& x);
void insert(iterator pos, int n, const T& x) {
  insert(pos, (size_type)n, x);
}
void insert(iterator pos, long n, const T& x) {
  insert(pos, (size_type)n, x);
}

insert(iterator pos, size_type n, const T& x)实现部分

template <class T, class Alloc>
void list<T, Alloc>::insert(iterator position, size_type n, const T& x) {
  /* 由于insert的特性在position指定的位置前面插入
   * 所以调用n次insert就行了
   */
  for ( ; n > 0; --n)
    insert(position, x);
}

最后,根据这些不同的版本,其实最核心的就是insert(iterator position, const T& x)函数,其他版本内部都调用了该函数。

transfer

该函数的作用是将[first, last)范围内的元素移动到position前。
源码如下:

void transfer(iterator position, iterator first, iterator last) {
  if (position != last) {
    (*(link_type((*last.node).prev))).next = position.node;
    (*(link_type((*first.node).prev))).next = last.node;
    (*(link_type((*position.node).prev))).next = first.node;  
    link_type tmp = link_type((*position.node).prev);
    (*position.node).prev = (*last.node).prev;
    (*last.node).prev = (*first.node).prev;
    (*first.node).prev = tmp;
  }
}

看起来有点复杂,我们一步一步来分解它。
1. (*(link_type((*last.node).prev))).next = position.node;:将最后一个元素的next指向position对应的结点。
相当于把[first, last)这一堆链表尾部与原来链表的连续切断了一半(还剩一半有*last.prev连着[first, last))
2. (*(link_type((*first.node).prev))).next = last.node;:接着将first指向的结点的前驱结点的next指针指向last。
相当于把[first, last)这一堆链表首部与原来链表的联系切断了一半(还剩一半有*first.node.prev连着原来的链表)
3. (*(link_type((*position.node).prev))).next = first.node;:将position指向的结点的前驱结点的next指针指向first指向的结点。
相当于首部已经连接了一半了,剩下还需要将
4. link_type tmp = link_type((*position.node).prev);:声明一个中间变量保存position指向的结点的前驱指针
5. (*position.node).prev = (*last.node).prev;:接着将position指向的结点的前驱指针指向last结点的前驱结点(即需要转移的链表的最后一个元素)。这样就将尾部也连接了一半了。
6. (*last.node).prev = (*first.node).prev;:将last指向的结点的前驱指针指向first指向的前驱结点。这样就将原链表腾出了一大半了。最后只需将第2步中剩下的另一半*first.node.prev指向tmp即可。
7. (*first.node).prev = tmp;:将first指向的结点的前驱指针指向position原来的前驱结点。前面要保存下来是因为在第5步的时候改变了*position.node.prev的值,如果不保存,将丢失position前面的链表。

建议对照着步骤自己画一下图,这样有助于理解。

splice

该函数的作用是拼接。
有多个重载版本,版本的不同导致其具体的功能也不同。

splice(iterator position, list& x)

该函数的作用是将x拼接到position前。
源码如下:

void splice(iterator position, list& x) {
  //调用transfer将x移动到position前
  if (!x.empty())
    transfer(position, x.begin(), x.end());
}
void splice(iterator position, list&, iterator i)

该函数的作用是将迭代器i所指的元素拼接到position前。
源码如下:

void splice(iterator position, list&, iterator i) {
  iterator j = i;
  ++j;
  //若position和i指向的元素相同或者i指向的元素本来就在position前面,直接return
  if (position == i || position == j) return;
  //否则将[i, j)移动到position前
  transfer(position, i, j);
}
void splice(iterator position, list&, iterator first, iterator last)

该函数的作用是将[first, last)范围内的元素拼接到position前

void splice(iterator position, list&, iterator first, iterator last) {
  if (first != last)
    transfer(position, first, last);
}

很明显position不能位于[first, last)之间,否则transfer会出错。

remove

该函数的作用是将list中的所有值为value的结点都移除。
源码如下:

template <class T, class Alloc>
void list<T, Alloc>::remove(const T& value) {
  iterator first = begin();
  iterator last = end();
  //遍历整个list
  while (first != last) {
    iterator next = first;
    ++next;
    //若等于,则调用erase删除该元素
    if (*first == value) erase(first);
    first = next;
  }
}
unique

该函数的作用是删除值相同的连续的元素,直至该元素剩最后一个。

template <class T, class Alloc>
void list<T, Alloc>::unique() {
  iterator first = begin();
  iterator last = end();
  if (first == last) return;
  iterator next = first;
  while (++next != last) {
    /* 比较前后两个元素的值
     * 若相等,则进行删除
     * 否则移动first迭代器,跟进到next
     */
    if (*first == *next)
      erase(next);
    else
      first = next;
    /* 可能会因为erase删除操作使next失效
     * 所以需要更新next,保持与first指向相同元素
     */
    next = first;
  }
}
merge

该函数的作用是将list x合并到该list上,合并的顺序是递增排序,并且要求合并前,两个list中的元素已经是递增的。

template <class T, class Alloc>
void list<T, Alloc>::merge(list<T, Alloc>& x) {
  iterator first1 = begin();
  iterator last1 = end();
  iterator first2 = x.begin();
  iterator last2 = x.end();
  //两个链表还没到结尾
  while (first1 != last1 && first2 != last2)
    /* 若*first2 < *first1
     * 则将first2指向的元素移动到first1指向的元素前面
     * 否则first1++,寻找下一个插入点
     */
    if (*first2 < *first1) {
      iterator next = first2;
      transfer(first1, first2, ++next);
      first2 = next;
    }
    else
      ++first1;
  /* 若first2 != last2
   * 证明x的元素还没全部被移动到list上
   * 将剩余的元素移动到last1前面
   */
  if (first2 != last2) transfer(last1, first2, last2);
}
reverse

该函数将list逆置,即逆转链表

template <class T, class Alloc>
void list<T, Alloc>::reverse() {
  //空list直接返回
  if (node->next == node || link_type(node->next)->next == node) return;
  /* 以下便是链表逆置操作
   * 先把first++是因为第一个元素并不需要动
   * 只需要把后面的元素依次搬到第一个元素前就行了
   * 还是借助了transfer操作
   */
  iterator first = begin();
  ++first;
  while (first != end()) {
    iterator old = first;
    ++first;
    transfer(begin(), old, first);
  }
}    

sort

该函数用于对list进行排序。list容器不能使用STL中对sort算法,因为sort算法只支持random_access_iterator_tag型的迭代器。
所以只好自己声明一个了。

template <class T, class Alloc>
void list<T, Alloc>::sort() {
  //空list,直接返回
  if (node->next == node || link_type(node->next)->next == node) return;
  //新的lists作为中介数据存放区
  list<T, Alloc> carry;
  list<T, Alloc> counter[64];
  int fill = 0;
  while (!empty()) {
    carry.splice(carry.begin(), *this, begin());
    int i = 0;
    while(i < fill && !counter[i].empty()) {
      counter[i].merge(carry);
      carry.swap(counter[i++]);
    }
    carry.swap(counter[i]);         
    if (i == fill) ++fill;
  }

  for (int i = 1; i < fill; ++i) counter[i].merge(counter[i-1]);
  swap(counter[fill-1]);
}

一些操作符重载函数

operator ==
template <class T, class Alloc>
inline bool operator==(const list<T,Alloc>& x, const list<T,Alloc>& y) {
  typedef typename list<T,Alloc>::link_type link_type;
  link_type e1 = x.node;
  link_type e2 = y.node;
  link_type n1 = (link_type) e1->next;
  link_type n2 = (link_type) e2->next;
  //依次比较结点的值
  for ( ; n1 != e1 && n2 != e2 ;
          n1 = (link_type) n1->next, n2 = (link_type) n2->next)
    if (n1->data != n2->data)
      return false;
  //若最后长度也相等,就返回true,否则返回false
  return n1 == e1 && n2 == e2;
}
operator =
template <class T, class Alloc>
list<T, Alloc>& list<T, Alloc>::operator=(const list<T, Alloc>& x) {
  if (this != &x) {
    iterator first1 = begin();
    iterator last1 = end();
    const_iterator first2 = x.begin();
    const_iterator last2 = x.end();
    //遍历并依次赋值
    while (first1 != last1 && first2 != last2) *first1++ = *first2++;
    /* 当x已经先遍历完了,则将list多余的元素删掉
     * 否则需要插入新的元素
     */
    if (first2 == last2)
      erase(first1, last1);
    else
      insert(last1, first2, last2);
  }
  return *this;
}

注意需要返回引用,否则无法连续赋值。

小结

本小节主要介绍了关于list容器的大部分操作。由于它使用的数据结构是双向链表,可能就需要你对链表比较熟悉了,特别是transfer函数的理解,还有sort函数,sort函数的操作确实比较复杂,这是也不多讲了,反而越讲越乱。
下一节我们将分析deque容器,它的数据结构稍微比list还有vector都要复杂一些。

Avellaneda & Stoikov’s market-making strategy 工程化处理 前2篇文章详细介绍了AS存货模型的做市策略,这次对于这些公式里面一些希腊字母的取值是如何计算的,如何做工程化并应用到生产环境中做一些说明。这个里面有一些重要参数:很直观,可以直接取值的有:s = current market mid price (中间价,也就是【最佳卖价+最佳买价】/2)q = quantity of assets in inventory of base asset (也就是买入多少价值的股票数量) 阅读详情

相关推荐

CentOS8静默安装Oracle11.2.0.1

背景 新安装oracle,用静默安装方式就不需要安装图形界面了,省时省力。 前期准备工作与图形化安装一样,如创建用户|用户组、环境变量、内核参数、创建目录等. 可参考图形化安装,链接:https://blog.youkuaiyun.com/heroicpoem/article/details/104229369 环境 平台 版本 操作系统 centOS 8 Oracle 11gR2 1.响应文件 响应文件的内容就是图形化安装时手工设置的选项内容。 解压后,database/response/db

heroicpoem的专栏 4518

3.C++STL源码理解

C++STL源码理解第一部分:(1)STL六大组件及功能(3)STL特性:第二部分:空间配置器(1)STL allocator:(2) construct函数(3)空间配置器alloc(4)第二配置器:一个内存池、16个free-list;(5)三个复制填充函数第三部分:迭代器与traits编程技巧1.iterator是一种smart pointer2.偏特化(类模板的部分具体化)3.traits机制:特征萃取机4.五种iterator_category5.iterator_traits第四部分:序列式容器

107 3908

抖音a_bogus参数逆向避坑指南:从环境检测到XHR请求的全流程解析

本文详细解析了抖音a_bogus参数在v1.0.1.19版本下的逆向工程全流程,重点介绍了如何通过PyV8Env框架进行有效的补环境操作,以绕过严格的环境检测。内容涵盖从环境初始化、关键事件触发到模拟XHR请求获取签名的完整步骤,并提供了可运行的Python代码示例和常见检测点的避坑指南,帮助开发者稳定生成有效的a_bogus参数。

tech5的博客 600

【C++】-- STL之vector详解

vecotr是可以改变大小的数组的序列容器,其特点有: (1)vector采用连续空间来存储元素,可以使用下标访问vector元素,访问元素和数组一样方便。vector大小可以动态改变,而且会被容器自动处理,这一点数组无法做到。 (2)vector使用动态分配分配数组来存储元素,插入新元素时,vector数组为了增加存储空间,会分配一个新数组,再把所有元素全部移动到这个数组里,代价相对高一些。...

局部变量和全局变量 2566

2025.06.20【pacbio】|PB甲基化分析结果的统计与可视化介绍

本文介绍了PacBio甲基化数据的统计分析与可视化方法。主要内容包括:1) 分析甲基化位点分布、比例及染色体分布;2) 修饰基序(Motif)的统计与热图展示;3) 全基因组和局部区域测序深度可视化;4) 使用IGV查看单个reads修饰情况。文章提供了R代码示例,涵盖柱状图、分布图、热图等多种可视化形式,并建议了差异甲基化分析等进阶方法,帮助科研人员深入挖掘甲基化数据。

2066

#1机器学习(西瓜书)自学记录

《机器学习 》 Datawhale 组队学习, 吃瓜自学笔记。

qq_74168740的博客 1149

SGISTL源码阅读十二 list容器中

SGISTL源码阅读十二 list容器中 前言 在list容器上中我们介绍了list的数据结构,构造,空间分配等。接下来我们继续分析list的一些相关操作。 深入源码 begin()/end //返回头节点 iterator begin() { return (link_type)((*node).next); } const_iterator begin() const { return (li...

Lyn&Xx 255

项目篇 | 图书管理系统 | 管理员模块 | 概述

图书管理系统管理员模块概述

百栗.的博客 404

《STL源码剖析》-- stl_list.h

// Filename: stl_list.h // Comment By: 凝霜 // E-mail: mdl2009@vip.qq.com // Blog: http://blog.youkuaiyun.com/mdl13412 /* * * Co

一个人的战争 6089

使用STM32H7点亮RGB屏幕

希望本文能对你有所帮助!RGB屏幕是一种具有红、绿、蓝三种颜色通道的显示设备,通过控制每个通道的亮度,可以显示出丰富多彩的图像和动画。要使用STM32H7控制RGB屏幕,我们需要连接适当的引脚,并编写适当的代码来控制每个颜色通道的亮度。在主循环中,我们先点亮红色通道,然后延迟一段时间,接着点亮绿色通道,再延迟一段时间,最后点亮蓝色通道,并再次延迟。需要注意的是,示例代码中的延迟循环使用了一个简单的计数器实现,但实际应用中可能需要更精确的延迟方式,例如使用定时器中断或操作系统提供的延迟函数。

QoyOle的博客 923

用 ABAP OAuth 2.0 Client 直连 SAP SuccessFactors:从体系到落地的一条龙实践指南

ABAP SMI允许在后端对外部服务的OData端点进行定义与自定义化配置,并以受信连接的方式暴露给ABAP应用调用。它并不替代OAuth,而是扮演端点与自定义参数的集中化定义者,最终认证依旧由完成。对于需要在ABAP后端发起临时或补充型OData调用的场景,SMI是一个轻量而清晰的集成入口。scope的客户端应用已经完成注册,API DomainCompany ID、证书或密钥都已归档。ABAP的Profile与Client已建立并绑定,OA2C_GRANT可见绿色访问状态。SMI。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 286

list

list概述 相较于vector的连续空间,list就显得复杂的多,它的好处是每次插入或删除一个元素,就配置或释放一个元素空间。 list的节点(node) STL list的节点(node)结构: template &lt; class T&gt; struct __list_node{   typedef void* void_pointer;   void_pointer ...

xiaomimi1993的博客 178

把 OData V4 服务真正发布出去:事务码 /IWFND/V4_ADMIN 在 SAP S/4HANA On-Premise 里的定位与用法

本文介绍了SAP S/4HANA中的事务码/IWFND/V4_ADMIN,这是SAP Gateway管理OData V4服务的关键工具。该事务码主要负责将后端注册的OData V4服务以服务组形式发布到前端,并维护系统别名路由、测试入口和日志跳转。与OData V2不同,V4采用服务组概念以支持跨服务导航,类似商场统一管理多个专柜。文章还通过实际案例说明,未发布服务组会导致BAS等工具无法发现OData V4服务,并提供了验证服务组状态的访问路径和ABAP示例代码。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 207

SGISTL源码探究-list容器(上)

前言在本小节中,我们将介绍序列式容器之一的list,它采用的数据结构是环状双向链表,而前面分析的vector是线性存储的。list对于插入数据,删除数据的效率很高,并且迭代器不会轻易失效。但是它的迭代器并不像vector那样是原生指针,所以它的类型可能并不是random_access_iterator_tag型,并且需要自己内嵌那五种相应型别。那么接下来,我们就正式进入到list容器的源码。 首先

Move_now的博客 478

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

SGISTL源码阅读十三 list容器下

SGISTL源码阅读十三 list容器下 前言 我们将继续介绍list的相关操作 深入源码 remove remove的作用是删除指定值的节点 template &lt;class T, class Alloc&gt; void list&lt;T, Alloc&gt;::remove(const T&amp; value) { iterator first = begin(); ite...

Lyn&Xx 196

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

SGISTL源码阅读十一 list容器上

SGISTL源码阅读十一 list容器上 list概述 之前我们学习了连续线性空间的vector,当容量不够用时它会简单粗暴地直接扩大到原来的两倍,难免会造成一定的空间浪费。 而list就不一样,它的好处是每插入或者删除一个元素,就配置或释放一个元素空间。它对空间的使用绝对精准,一点也不浪费。而且在元素的插入删除操作上,时间复杂度是常数级别,对于vector插入删除操作相当的麻烦。 深入源码 l...

Lyn&Xx 263

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

SGISTL源码-容器 笔记

本文是在阅读SGI STL v3.3源码中,遇到的一些问题及有意思的点。本文建立在已经观看过侯捷老师STL源码剖析课程的基础上。是在阅读源码过程中,对其中的内容进行补充、修饰。

weixin_46065476的博客 596

SGISTL源码阅读十八 queue(队列)

SGISTL源码阅读十八 queue(队列) 前言 和上一篇文章提到过的stack一样,queue也是一种配接器(adapter),它们的实现非常类似。 它是一种(First In First Out,FIFO)的数据结构,也没有提供遍历或指定位置访问等操作,只能从末端添加元素,顶部取出元素,所以它也没有迭代器。 深入源码 queue的定义部分 #ifndef __STL_LIMITED_DEFA...

Lyn&Xx 313

C++ List容器的底层实现

和 array、vector 这些容器迭代器的实现方式不同,由于 list 容器的元素并不是连续存储的,所以该容器迭代器中,必须包含一个可以指向 list 容器的指针,并且该指针还可以借助重载的 *、++、–、==、!可以看到,list 容器定义的每个节点中,都包含 *prev、*next 和 myval。注:不同于双向循环链表,使用双向链表实现的 list 容器,其内部通常包含 2 个指针,并分别指向链表中头部的空白节点和尾部的空白节点(也就是说,其包含 2 个空白节点)。

qq_41897304的博客 509

STL中list容器的底层实现原理

关于list容器的底层实现原理

Feeliamn的博客 166

STL源码—list

在SGI STL中,list容器是一个循环的双向链表,它的内存空间效率较前文介绍的vector容器高。因为vector容器的内存空间是连续存储的,且在分配内存空间时,会分配额外的可用空间;而list容器的内存空间不一定是连续存储,内存之间是采用迭代器或节点指针进行连接,并且在插入或删除数据节点时,就配置或释放一个数据节点,并不会分配额外的内存空间,这两个操作过程都是常数时间。

xietingcandice的专栏 742

STL源码剖析(list)

SGI STL中list是使用环状双向链表实现的。它的结点结构定义如下: 1 template <class T> 2 struct __list_node { 3 typedef void* void_pointer; 4 void_pointer next; 5 void_pointer prev; 6 T data; 7 };...

weixin_30488085的博客 150

《STL源码剖析》学习笔记——第四章:序列式容器 slist

目录 1. slist概述 2. slist的节点 3. slist的迭代器 4. slist的数据结构 5. slist的测试实例 1. slist概述 SGI STL另提供一个单向链表slist。slist和list的主要差别在于,前者的迭代器属于单向的Forward Iterator,后者的迭代器属于双向的BidirectionalIterator。根据STL的习惯,插入操作会将新元素插入于指定位置之前。作为单向链表,slist没有任何方便的方法可以回头定出前一个位置,因此它必须从头找起

baidu_15952103的博客 308

【C++ 学习 ⑧】- STL 简介

从 STL 是什么、STL 的版本、STL 的 6 大组件和 13 个头文件、学习 STL 的 3 个境界以及 STL 的缺陷这五个方面来介绍 STL。

melonyzzZ的博客 2250

学习笔记----STL源码剖析

自己的学习笔记

2301_80428401的博客 684

[C++]---STL中的栈与队列进一步理解(stl中的底层实现

STL中的栈与队列进一步理解(stl中的底层实现

zjjaibc的博客 504
上一篇: SGISTL源码探究-list容器(上)
下一篇: SGISTL源码探究-deque容器(上)
UKey_
博客等级 码龄10年 258粉丝 186原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值