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

复现基于混频因子模型的GDP即时预测方法 基于混频因子模型的GDP即时预测方法复现 本文复现了混频因子模型(MIDAS)在GDP即时预测中的应用。该方法整合7个月度宏观经济变量(工业增加值、CPI、PMI等)与季度GDP数据,通过状态空间模型实现高频预测。技术实现包括:1)使用PCA提取潜在共同因子;2)构建状态空间模型处理混频数据;3)通过卡尔曼滤波进行参数估计。实证结果显示模型预测效果良好(测试集R²=0.82),其中工业增加值对因子1贡献最大(0.82),CPI对因子2贡献显著(0.78)。该方法为决策者提供了更及时的季度GDP估算。 阅读详情

前言

在上一小节我们主要介绍了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都要复杂一些。

[Python + UG NX 二次开发新手教程] 第五课 采用Python / UF 开发模式 在 NX 二次开发里看到的“UF”是的缩写,它是 Siemens 提供的集合,也是整个 NX Open 体系里最贴近内核、历史最悠久的一套接口。相当于 NX 的“系统调用”——只要 NX 能做的事,UF 几乎都能调得动。Siemens 从 NX 12 开始把原来的 C 接口(UF)全部打包进了官方 Python 模块,命名空间为 NXOpen.UF.*,所以只要 NX 安装目录里带了 NXOpen\UF 子模块,就能在 Python 里直接调用 UF 函数。 阅读详情

相关推荐

Visual SLAM算法笔记

最近在学习SLAM算法,这里作为阅读笔记记录和总结一下。对经典的单目SLAM/VO算法做了摘要和总结,比如Nister VO、MonoSLAM、PTAM、ORB-SLAM、LSD-SLAM、SVO、DSO等,对几个单目和IMU的融合算法也做了简单总结,比如MSCKF、OKVIS、IMU-Preintegration等。

MulinB的技术博客 3万+

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

颠覆性创新的测量框架与跨领域实证分析

颠覆性创新作为科技发展的重要驱动力,其核心在于实现知识体系的范式转换。从技术原理看,这类创新通常具备知识替代、网络重构和跨领域渗透三大特征,通过引用网络分析和文本挖掘等方法可量化评估其影响力。在工程实践中,CD指数、DI指数等测量工具帮助研究者识别具有突破潜力的成果,而深度学习等新兴技术进一步提升了评估精度。当前AI、半导体等领域的热门技术发展表明,模块化架构和产业协同能有效促进颠覆性创新。面对全球范围内创新颠覆性下降的趋势,优化科研评价体系、构建跨学科协作网络成为关键应对策略。

weixin_29735807的博客 321

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

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

局部变量和全局变量 2566

从零到一:PrimeTime静态时序分析入门指南

本文详细介绍了PrimeTime静态时序分析(STA)的入门指南,从基础概念到实际应用全面覆盖。内容包括时序路径分类、PrimeTime环境搭建、时序约束设置与验证、时序分析与报告解读等核心内容,帮助工程师快速掌握STA技术,解决数字芯片设计中的时序收敛问题。

weixin_28730131的博客 268

H.264 Profile与Level详解:如何为不同应用场景选择合适的编码配置

本文深入解析了H.264编码标准中的Profile与Level概念,指导开发者如何为不同应用场景选择合适的配置。文章对比了Baseline、Main和High三大Profile的特性与适用场景,并解释了Level如何限定视频的分辨率、帧率与码率上限。通过实战指南,帮助读者在视频通话、在线点播和专业制作等场景中,平衡画质、兼容性与性能,实现高效编码。

rgv2345678的博客 238

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

Docker入门教程:在腾讯云轻量服务器上部署你的第一个容器化应用 (2025)

还在为“在我电脑上明明是好的”而烦恼?本篇Docker入门教程,用通俗易懂的比喻解释镜像、容器的核心概念,并手把手带你在腾讯云轻量服务器上,从运行Nginx到编写Dockerfile打包自己的Python应用,轻松掌握容器化部署。

Clownseven的博客 2084

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

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

一个人的战争 6089

173、LLC谐振变换器的PCB设计实战(布线)

LLC谐振变换器PCB设计实战要点 LLC谐振变换器的PCB设计核心在于控制寄生参数,避免高频振铃和EMI问题。功率回路需确保电流路径最短且紧耦合,谐振电容焊盘需加宽以减少发热。驱动走线应独立于功率回路,避免干扰,并合理控制宽度和长度。变压器绕组结构影响共模噪声,建议原边在外层并加屏蔽铜箔。地平面设计需根据功率等级选择整块或分割,大功率下功率地与信号地应单点连接。谐振电流采样推荐四端开尔文电阻以减少电感干扰。散热需优化铜皮和过孔布局,功率器件优先放置顶层。布局应先确定谐振腔,再排布控制电路。经验上需实测寄生

Jack010169的博客 46

MIPI D-PHY的校准测试

MIPI的校准测试困扰了很久,今天终于得以解决!

一只豌豆象的博客 3919

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

博途PLC模糊PID控制(量化因子和比例因子)

参数"量化因子和比例因子",都是为了对清晰值进行比例变换而设置的,其作用是使变量按一定比例进行放大或缩小,以便和我们的模糊论域相匹配完成一一映射。这种变换对整个系统的工作性能也是有影响的。(其实相当于额外引入了比例作用,所以说模糊PID的调节是个系统工程)。限于本人能力,文中难免出现错误和不足,欢迎大家批评和指正。博途PLC的模糊PID控制完整算法可以参看下面这篇文章博途PLC模糊PID控制详解(SCL完整源代码+参数)_RXXW_Dor的博客-优快云博客。......

RXXW_Dor的博客 5269

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

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

Move_now的博客 478

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

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

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

SGISTL源码阅读十一 list容器上

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

Lyn&Xx 263

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

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、付费专栏及课程。

余额充值