首页
技术博客
PHP教程
数据库技术
前端开发
HTML5
Nginx
php论坛
新用户注册
|
会员登录
PHP教程
技术博客
编程问答
PNG素材
编程语言
前端技术
Android
PHP教程
HTML5教程
数据库
Linux技术
Nginx技术
PHP安全
WebSerer
职场攻略
JavaScript
开放平台
业界资讯
大话程序猿
登录
极速注册
取消
热门标签 | HotTags
import
yaml
typescript
vba
hashset
less
process
select
perl
spring
rsa
md5
object
command
bit
nodejs
io
cpython
python3
timezone
tree
go
express
solr
subset
substring
join
tags
settings
require
cPlusPlus
header
shell
uri
bitmap
split
hook
web
loops
default
jar
scala
input
hashtable
stream
netty
ip
python2
filter
actionscrip
eval
callback
replace
dagger
post
usb
java
text
include
const
php
js
golang
datetime
frameworks
blob
cookie
search
httpclient
iostream
integer
javascript
bash
window
uml
export
email
erlang
utf-8
当前位置:
开发笔记
>
编程语言
> 正文
STL源码分析list
作者:手机用户2602908893 | 来源:互联网 | 2023-10-13 04:25
相较于vector的连续线性空间,list就显得复杂许多,它的好处是每次插入或删除一个元素,就配置或释放一个元素空间。因此,list对于空间的运用有绝对的精准,一点也不浪费。而且,
相较于vector的连续线性空间,list就显得复杂许多,它的好处是每次插入或删除一个元素,就配置或释放一个元素空间。因此,list对于空间的运用有绝对的精准,一点也不浪费。而且,对于任何位置的元素插入或元素移除,list永远是常数时间。
list不仅是一个双向链表,而且还是一个环状双向链表
。另外,还有一个重要性质,插入操作和接合操作都不会造成原有的list迭代器失效,这在vector是不成立的。因为vector的插入操作可能造成记忆体重新配置,导致原有的迭代器全部失效。
甚至list的元素删除操作(erase),也只有“指向被删除元素”的那个迭代器失效,其他迭代器不受任何影响。
以下是list的节点、迭代器数据结构设计以及list的源码剖析:
[cpp]
view plaincopy
////////////////////////////////////////////////////////////////////////////////
// list结点, 提供双向访问能力
////////////////////////////////////////////////////////////////////////////////
// -------- -------- -------- --------
// | next |---------->| next |---------->| next |---------->| next |
// -------- -------- -------- --------
// | prev |<----------| prev |<----------| prev |<----------| prev |
// -------- -------- -------- --------
// | data | | data | | data | | data |
// -------- -------- -------- --------
////////////////////////////////////////////////////////////////////////////////
template
<
class
T>
struct
__list_node
{
typedef
void
* void_pointer;
void_pointer next;
void_pointer prev;
T data;
};
// 至于为什么不使用默认参数, 这个是因为有一些编译器不能提供推导能力,
// 而作者又不想维护两份代码, 故不使用默认参数
template
<
class
T,
class
Ref,
class
Ptr>
struct
__list_iterator
{
typedef
__list_iterator
iterator;
// STL标准强制要求
typedef
__list_iterator
self;
typedef
bidirectional_iterator_tag iterator_category;
typedef
T value_type;
typedef
Ptr pointer;
typedef
Ref reference;
typedef
__list_node
* link_type;
typedef
size_t
size_type;
typedef
ptrdiff_t
difference_type;
link_type node;
//迭代器内部当然要有一个普通指针,指向list的节点
__list_iterator(link_type x) : node(x) {}
__list_iterator() {}
__list_iterator(
const
iterator& x) : node(x.node) {}
// 在STL算法中需要迭代器提供支持
bool
operator==(
const
self& x)
const
{
return
node == x.node; }
bool
operator!=(
const
self& x)
const
{
return
node != x.node; }
// 以下对迭代器取值(dereference),取的是节点的数据值
reference operator*()
const
{
return
(*node).data; }
// 以下是迭代器的成员存取运算子的标准做法
pointer operator->()
const
{
return
&(operator*()); }
// 前缀自加,对迭代器累加1,就是前进一个节点
self& operator++()
{
node = (link_type)((*node).next);
return
*
this
;
}
// 后缀自加, 需要先产生自身的一个副本, 然会再对自身操作, 最后返回副本
self operator++(
int
)
{
self tmp = *
this
;
++*
this
;
return
tmp;
}
// 前缀自减
self& operator--()
{
node = (link_type)((*node).prev);
return
*
this
;
}
self operator--(
int
)
{
self tmp = *
this
;
--*
this
;
return
tmp;
}
};
////////////////////////////////////////////////////////////////////////////////
// list不仅是个双向链表, 而且还是一个环状双向链表
////////////////////////////////////////////////////////////////////////////////
// end() 头结点 begin()
// ↓ ↓ ↓
// -------- -------- -------- --------
// ---->| next |---------->| next |---------->| next |---------->| next |------
// | -------- -------- -------- -------- |
// | --| prev |<----------| prev |<----------| prev |<----------| prev |<--| |
// | | -------- -------- -------- -------- | |
// | | | data | | data | | data | | data | | |
// | | -------- -------- -------- -------- | |
// | | | |
// | | -------- -------- -------- -------- | |
// ---|-| next |<----------| next |<----------| next |<----------| next |<--|--
// | -------- -------- -------- -------- |
// ->| prev |---------->| prev |---------->| prev |---------->| prev |----
// -------- -------- -------- --------
// | data | | data | | data | | data |
// -------- -------- -------- --------
////////////////////////////////////////////////////////////////////////////////
// 默认allocator为alloc, 其具体使用版本请参照
template
<
class
T,
class
Alloc = alloc>
class
list
{
protected
:
typedef
void
* void_pointer;
typedef
__list_node
list_node;
// 专属之空间配置器,每次配置一个节点大小
typedef
simple_alloc
list_node_allocator;
public
:
typedef
T value_type;
typedef
value_type* pointer;
typedef
value_type& reference;
typedef
list_node* link_type;
typedef
size_t
size_type;
typedef
ptrdiff_t
difference_type;
typedef
__list_iterator
iterator;
protected
:
link_type node ;
// 只要一个指针,便可表示整个环状双向链表
// 分配一个新结点, 注意这里并不进行构造,
// 构造交给全局的construct, 见
link_type get_node() {
return
list_node_allocator::allocate(); }
// 释放指定结点, 不进行析构, 析构交给全局的destroy
void
put_node(link_type p) { list_node_allocator::deallocate(p); }
// 产生(配置并构造)一个节点, 首先分配内存, 然后进行构造
// 注: commit or rollback
link_type create_node(
const
T& x)
{
link_type p = get_node();
construct(&p->data, x);
return
p;
}
// 析构结点元素, 并释放内存
void
destroy_node(link_type p)
{
destroy(&p->data);
put_node(p);
}
protected
:
// 用于空链表的建立
void
empty_initialize()
{
node = get_node();
// 配置一个节点空间,令node指向它
node->next = node;
// 令node头尾都指向自己,不设元素值
node->prev = node;
}
// 创建值为value共n个结点的链表
// 注: commit or rollback
void
fill_initialize(size_type n,
const
T& value)
{
empty_initialize();
__STL_TRY
{
// 此处插入操作时间复杂度O(1)
insert(begin(), n, value);
}
__STL_UNWIND(clear(); put_node(node));
}
public
:
list() { empty_initialize(); }
iterator begin() {
return
(link_type)((*node).next); }
// 链表成环, 当指所以头节点也就是end
iterator end() {
return
node; }
// 头结点指向自身说明链表中无元素
bool
empty()
const
{
return
node->next == node; }
// 使用全局函数distance()进行计算, 时间复杂度O(n)
size_type size()
const
{
size_type result = 0;
distance(begin(), end(), result);
return
result;
}
size_type max_size()
const
{
return
size_type(-1); }
reference front() {
return
*begin(); }
reference back() {
return
*(--end()); }
////////////////////////////////////////////////////////////////////////////////
// 在指定位置插入元素
////////////////////////////////////////////////////////////////////////////////
// insert(iterator position, const T& x)
// ↓
// create_node(x)
// p = get_node();-------->list_node_allocator::allocate();
// construct(&p->data, x);
// ↓
// tmp->next = position.node;
// tmp->prev = position.node->prev;
// (link_type(position.node->prev))->next = tmp;
// position.node->prev = tmp;
////////////////////////////////////////////////////////////////////////////////
iterator insert(iterator position,
const
T& x)
{
link_type tmp = create_node(x);
// 产生一个节点
// 调整双向指针,使tmp插入进去
tmp->next = position.node;
tmp->prev = position.node->prev;
(link_type(position.node->prev))->next = tmp;
position.node->prev = tmp;
return
tmp;
}
// 指定位置插入n个值为x的元素, 详细解析见实现部分
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);
}
// 在链表前端插入结点
void
push_front(
const
T& x) { insert(begin(), x); }
// 在链表最后插入结点
void
push_back(
const
T& x) { insert(end(), x); }
// 移除迭代器position所指节点
iterator erase(iterator position)
{
link_type next_node = link_type(position.node->next);
link_type prev_node = link_type(position.node->prev);
prev_node->next = next_node;
next_node->prev = prev_node;
destroy_node(position.node);
return
iterator(next_node);
}
// 擦除一个区间的结点, 详细解析见实现部分
iterator erase(iterator first, iterator last);
void
resize(size_type new_size,
const
T& x);
void
resize(size_type new_size) { resize(new_size, T()); }
void
clear();
// 删除链表第一个结点
void
pop_front() { erase(begin()); }
// 删除链表最后一个结点
void
pop_back()
{
iterator tmp = end();
erase(--tmp);
}
list(size_type n,
const
T& value) { fill_initialize(n, value); }
list(
int
n,
const
T& value) { fill_initialize(n, value); }
list(
long
n,
const
T& value) { fill_initialize(n, value); }
~list()
{
// 释放所有结点 // 使用全局函数distance()进行计算, 时间复杂度O(n)
size_type size()
const
{
size_type result = 0;
distance(begin(), end(), result);
return
result;
}
clear();
// 释放头结点
put_node(node);
}
list
& operator=(
const
list
& x);
protected
:
////////////////////////////////////////////////////////////////////////////////
// 将[first, last)内的所有元素移动到position之前
// 如果last == position, 则相当于链表不变化, 不进行操作
////////////////////////////////////////////////////////////////////////////////
// 初始状态
// first last
// ↓ ↓
// -------- -------- -------- -------- -------- --------
// | next |-->| next |-->| next | | next |-->| next |-->| next |
// ... -------- -------- -------- ... -------- -------- -------- ...
// | prev |<--| prev |<--| prev | | prev |<--| prev |<--| prev |
// -------- -------- -------- -------- -------- --------
//
// position
// ↓
// -------- -------- -------- -------- -------- --------
// | next |-->| next |-->| next |-->| next |-->| next |-->| next |
// ... -------- -------- -------- -------- -------- -------- ...
// | prev |<--| prev |<--| prev |<--| prev |<--| prev |<--| prev |
// -------- -------- -------- -------- -------- --------
//
// 操作完成后状态
// first
// |
// --------------|--------------------------------------
// | ------------|------------------------------------ | last
// | | ↓ | | ↓
// -------- | | -------- -------- -------- | | -------- --------
// | next |-- | ----->| next |-->| next | | next |----- | -->| next |-->| next |
// ... -------- | | -------- -------- ... -------- | | -------- -------- ...
// | prev |<--- | ---| prev |<--| prev | | prev |<-- | -----| prev |<--| prev |
// -------- | | -------- -------- -------- | | -------- --------
// | | | |
// | ------ | |
// ------- | ------------------------------ |
// | | | |
// | | | -----------------------------
// | | | |
// | | | | position
// | | | | ↓
// -------- -------- | | | | -------- -------- -------- --------
// | next |-->| next |-- | | -->| next |-->| next |-->| next |-->| next |
// ... -------- -------- | | -------- -------- -------- -------- ...
// | prev |<--| prev |<--- ------| prev |<--| prev |<--| prev |<--| prev |
// -------- -------- -------- -------- -------- --------
////////////////////////////////////////////////////////////////////////////////
void
transfer(iterator position, iterator first, iterator last)
{
if
(position != last)
// 如果last == position, 则相当于链表不变化, 不进行操作
{
(*(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;
}
}
public
:
// 将链表x移动到position所指位置之前
void
splice(iterator position, list& x)
{
if
(!x.empty())
transfer(position, x.begin(), x.end());
}
// 将链表中i指向的内容移动到position之前
void
splice(iterator position, list&, iterator i)
{
iterator j = i;
++j;
if
(position == i || position == j)
return
;
transfer(position, i, j);
}
// 将[first, last}元素移动到position之前
void
splice(iterator position, list&, iterator first, iterator last)
{
if
(first != last)
transfer(position, first, last);
}
void
remove(
const
T& value);
void
unique();
void
merge(list& x);
void
reverse();
void
sort();
};
// 销毁所有结点, 将链表置空
template
<
class
T,
class
Alloc>
void
list
::clear()
{
link_type cur = (link_type) node->next;
while
(cur != node)
{
link_type tmp = cur;
cur = (link_type) cur->next;
destroy_node(tmp);
}
// 恢复node原始状态
node->next = node;
node->prev = node;
}
// 链表赋值操作
// 如果当前容器元素少于x容器, 则析构多余元素,
// 否则将调用insert插入x中剩余的元素
template
<
class
T,
class
Alloc>
list
& list
::operator=(
const
list
& 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++;
if
(first2 == last2)
erase(first1, last1);
else
insert(last1, first2, last2);
}
return
*
this
;
}
// 移除容器内所有的相邻的重复结点
// 时间复杂度O(n)
// 用户自定义数据类型需要提供operator ==()重载
template
<
class
T,
class
Alloc>
void
list
::unique()
{
iterator first = begin();
iterator last = end();
if
(first == last)
return
;
iterator next = first;
while
(++next != last)
{
if
(*first == *next)
erase(next);
else
first = next;
next = first;
}
}
// 假设当前容器和x都已序, 保证两容器合并后仍然有序
template
<
class
T,
class
Alloc>
void
list
::merge(list
& x)
{
iterator first1 = begin();
iterator last1 = end();
iterator first2 = x.begin();
iterator last2 = x.end();
// 注意:前提是,两个lists都已经递增排序
while
(first1 != last1 && first2 != last2)
if
(*first2 < *first1)
{
iterator next = first2;
transfer(first1, first2, ++next);
first2 = next;
}
else
++first1;
if
(first2 != last2)
transfer(last1, first2, last2);
}
STL源码分析--list
android
asp.net
php
jsp
数据库
list
windows
html
js
写下你的评论吧 !
吐个槽吧,看都看了
会员登录
|
用户注册
推荐阅读
io
网络流24题——试题库问题
题目描述:假设一个试题库中有n道试题。每道试题都标明了所属类别。同一道题可能有多个类别属性。现要从题库中抽取m道题组成试卷。并要求试卷包含指定类型的试题。试设计一个满足要求的组卷算 ...
[详细]
蜡笔小新 2024-11-22 11:33:55
go
UVALive 8201 - BBP 公式计算圆周率
在1995年,Simon Plouffe 发现了一种特殊的求和方法来表示某些常数。两年后,Bailey 和 Borwein 在他们的论文中发表了这一发现,这种方法被命名为 Bailey-Borwein-Plouffe (BBP) 公式。该问题要求计算圆周率 π 的第 n 个十六进制数字。 ...
[详细]
蜡笔小新 2024-11-21 18:32:57
express
WPF菜单控件前景与背景颜色设置指南
尽管在WPF中工作了一段时间,但在菜单控件的样式设置上遇到了一些基础问题,特别是关于如何正确配置前景色和背景色。 ...
[详细]
蜡笔小新 2024-11-22 15:30:54
express
在Notepad++中配置Markdown语法高亮及实时预览功能
本文详细介绍了如何在Notepad++中配置Markdown语法高亮和实时预览功能,包括必要的插件安装和设置步骤。 ...
[详细]
蜡笔小新 2024-11-22 13:03:49
express
解决映射文件中重复属性字段问题
探讨如何在映射文件中处理重复的属性字段,以避免数据操作时出现错误。 ...
[详细]
蜡笔小新 2024-11-22 11:48:50
io
为何Compose与Swarm之后仍有Kubernetes的诞生?
探讨在已有Compose和Swarm的情况下,Kubernetes是如何以其独特的设计理念和技术优势脱颖而出,成为容器编排领域的领航者。 ...
[详细]
蜡笔小新 2024-11-22 09:26:11
settings
解决iOS应用推送通知错误:未找到有效aps-environment权限
在尝试加载支持推送通知的iOS应用程序的Ad Hoc构建时,遇到了‘no valid aps-environment entitlement found for application’的错误提示。本文将探讨此错误的原因及多种可能的解决方案。 ...
[详细]
蜡笔小新 2024-11-21 19:26:31
tags
SIP基础概览
本文介绍了SIP(Session Initiation Protocol,会话发起协议)的基本概念、功能、消息格式及其实现机制。SIP是一种在IP网络上用于建立、管理和终止多媒体通信会话的应用层协议。 ...
[详细]
蜡笔小新 2024-11-21 17:42:08
require
binlog2sql,你该知道的数据恢复工具
binlog2sql,你该知道的数据恢复工具 ...
[详细]
蜡笔小新 2024-11-22 18:58:43
go
解析 .NET 中的 AJAX 技术
Asynchronous JavaScript and XML (AJAX) 的流行很大程度上得益于 Google 在其产品如 Google Suggest 和 Google Maps 中的应用。本文将深入探讨 AJAX 在 .NET 环境下的工作原理及其实现方法。 ...
[详细]
蜡笔小新 2024-11-22 18:18:57
io
Python3爬虫入门:pyspider的基本使用[python爬虫入门]
Python学习网有大量免费的Python入门教程,欢迎大家来学习。本文主要通过爬取去哪儿网的旅游攻略来给大家介绍pyspid ...
[详细]
蜡笔小新 2024-11-22 18:00:41
require
Ubuntu 14.04 环境下搭建 Caffe(仅限 CPU)
本文详细介绍了如何在 Ubuntu 14.04 系统上搭建仅使用 CPU 的 Caffe 深度学习框架,包括环境准备、依赖安装及编译过程。 ...
[详细]
蜡笔小新 2024-11-22 16:43:30
io
Redis 数据类型及其应用场景
本文详细介绍了 Redis 中的主要数据类型,包括 String、Hash、List、Set、ZSet、Geo 和 HyperLogLog,并提供了每种类型的基本操作命令和应用场景。 ...
[详细]
蜡笔小新 2024-11-22 15:36:30
go
Vue CLI 基础入门指南
本文详细介绍了 Vue CLI 的基础使用方法,包括环境搭建、项目创建、常见配置及路由管理等内容,适合初学者快速掌握 Vue 开发环境。 ...
[详细]
蜡笔小新 2024-11-22 14:48:35
io
Android 中的布局方式之线性布局
nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ...
[详细]
蜡笔小新 2024-11-22 11:20:34
手机用户2602908893
这个家伙很懒,什么也没留下!
Tags | 热门标签
import
yaml
typescript
vba
hashset
less
process
select
perl
spring
rsa
md5
object
command
bit
nodejs
io
cpython
python3
timezone
tree
go
express
solr
subset
substring
join
tags
settings
require
RankList | 热门文章
1
SQL SERVER 2005 最小安装经验_MySQL
2
ERROR 1222 (21000): The used SELECT statements have a differ_MySQL-mysql教程
3
mysql千万级数据大表该如何优化?_MySQL
4
MySQL动态创建表,数据分表的存储过程_MySQL
5
通过sql语句将blob里的char取出来转成数字保存在其它字段_MySQL
6
SQLServer 2005 自动备份数据库的方法分享(附图解教程)_MySQL-mysql教程
7
MYSQL where 1=1判定中的作用说明_MySQL
8
常用的SQL例句 数据库开发所需知识_MySQL
9
mysql如果数据不存在,则插入新数据,否则更新的实现方法_MySQL
10
MYSQL事件查看器使用介绍_MySQL
11
mysql三种批量增加的性能分析_MySQL-mysql教程
12
MySQL如何导入csv格式数据文件解决方案_MySQL
13
mysql 复制过滤重复如何解决_MySQL-mysql教程
14
mysql 表维护与改造代码分享_MySQL
15
优化mysql的limit offset的例子_MySQL-mysql教程
PHP1.CN | 中国最专业的PHP中文社区 |
DevBox开发工具箱
|
json解析格式化
|
PHP资讯
|
PHP教程
|
数据库技术
|
服务器技术
|
前端开发技术
|
PHP框架
|
开发工具
|
在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved |
京公网安备 11010802041100号
|
京ICP备19059560号-4
| PHP1.CN 第一PHP社区 版权所有