【C++】list介绍以及模拟实现(超级详细)

chian-ocean 2024-08-07 16:05:02 阅读 77

欢迎来到我的Blog,点击关注哦💕

list的介绍和模拟实现

前言`list`介绍标准库容器` std::list` 与 `std::vector` 的优缺点缺点

`list`的基本操作构造函数`list iterator``list capcacity``list modify`

`list`模拟实现存贮结构(双向带头循环)`iterator``iterator`结构`operator!=` `operator ==``operator++``operator--`

`list`数据结构构造函数list的初始化节点初始化迭代器

`list modify`inserterase头插、头删、尾插、尾删

`list operator`交换clear

源码

前言

<code>string vector的是存储是基于物理空间上连续的,而list是作为线性的链式结构,是值得学习的。

list介绍

std::list是C++标准模板库(STL)中的一个容器适配器,它内部实现为双向链表结构。这种设计使得std::list能够在常数时间内进行任意位置的插入和删除操作,这是其相对于其他序列容器如std::vector的显著优点。std::list不支持随机访问,即无法直接通过索引来访问容器中的元素,这通常需要从头部或尾部开始迭代到目标位置.

标准库容器 std::liststd::vector 的优缺点

std::list: 作为一个双向链表,std::list 在插入和删除操作上具有优势,因为这些操作只涉及到改变相邻节点的指针,而不需要移动其他元素。此外,std::list 不需要预分配额外的内存,可以更好地处理动态内存分配,减少内存碎片.std::vector: 作为一个动态数组,std::vector 提供了高效的随机访问能力,可以通过下标直接访问任意位置的元素,其访问效率为 O(1). 此外,std::vector 通常具有较高的空间利用率和缓存友好性,因为其元素在内存中是连续存储的.

缺点

std::list: 由于非连续的内存存储,std::list 在访问元素时效率较低,因为可能需要从头开始遍历链表。此外,每个节点都需要存储额外的指针信息,这增加了内存开销.std::vector: 在 std::vector 的中间位置插入或删除元素可能会引起大量元素的移动,以维持内存的连续性,这会导致较差的性能。当 std::vector 的容量不足以容纳新增元素时,还需要进行动态扩容,这是一个成本较高的操作

vector list
底层

结构

动态顺序表,一段连续空间 带头结点的双向循环链表
随 机

访 问

支持随机访问,访问某个元素效率O(1) 不支持随机访问,访问某个元素 效率O(N)
插 入

删 除

任意位置插入和删除效率低,需要搬移元素,

时间复杂度为O(N),插入时有可能需要增容,

增容:开辟新空 间,拷贝元素,释放旧空间,导致效率更低

任意位置插入和删除效率高,不 需要搬移元

素,时间复杂度为 O(1)

插 入

删 除

底层为连续空间,不容易造成内存碎片,空间利用率 高,缓存利用率高 底层节点动态开辟,小节点容易 造成内存碎片,空间利用率低, 缓存利用率低
迭 代 器 原生态指针 对原生态指针(节点指针)进行封装
迭 代 器

失 效

在插入元素时,要给所有的迭代器重新赋值,因为插入 元素有可能会导致重新扩容,致使原来迭代器失效,删 除时,当前迭代器需要重新赋值否则会失效 插入元素不会导致迭代器失效, 删除元素时,只会导致当前迭代 器失效,其他迭代器不受影响
使 用

场 景

需要高效存储,支持随机访问,不关心插入删除效率 大量插入和删除操作,不关心随 机访问

list的基本操作

构造函数

【C++】vector介绍以及模拟实现 接口说明
list (size_type n, const value_type& val = value_type()) 构造的list中包含n个值为val的元素
list() 构造空的list
list (const list& x) 拷贝构造函数
list (InputIterator first, InputIterator last) 用[first, last)区间中的元素构造list

list iterator

此处,大家可暂时将迭代器理解成一个指针,该指针指向list中的某个节点。

函数声明 接口说明
begin + end 返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器
rbegin + rend 返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的 reverse_iterator,即begin位置

list capcacity

函数声明 接口说明
empty 检测list是否为空,是返回true,否则返回false
size 返回list中有效节点的个数

list modify

函数声明 接口说明
push_front 在list首元素前插入值为val的元素
pop_front 删除list中第一个元素
push_back 在list尾部插入值为val的元素
pop_back 删除list中最后一个元素
insert 在list position 位置中插入值为val的元素
erase 删除list position位置的元素
swap 交换两个list中的元素
clear 清空list中的有效元素

list模拟实现

存贮结构(双向带头循环)

定义一个类(C语言:结构体)存储数据,作为指向下一个上一个的节点

namespace MyList

{ -- -->

//LIst节点

template<class T>

struct _list_node

{

_list_node<T>* _next;

_list_node<T>* _prev;

T _val;

_list_node(const T& val = T())

: _next(nullptr)

, _prev(nullptr)

, _val(val)

{ }

};

}

iterator

迭代器有两种实现方式,具体应根据容器底层数据结构实现:

原生态指针,比如:vector string将原生态指针进行封装,因迭代器使用形式与指针完全相同,因此在自定义的类中必须实现以下方法:

1. 指针可以解引用,迭代器的类中必须重载operator*()

2. 指针可以通过->访问其所指空间成员,迭代器类中必须重载 oprator->()

3. 指针可以++向后移动,迭代器类中必须重载operator++()与operator++(int)

4. 迭代器需要进行是否相等的比较,因此还需要重载operator==()与operator!=()

iterator结构

三个模板参数

第一个模板参数控制类型第二个模板参数控制返回值类型const 非const第三个模板参数控制返回的list类的类型const 非const Node* _node;需要定义一个指针,指向list节点

template<class T,class Ref,class ptr>

struct _list_iterator

{

typedef _list_node<T> Node;

typedef _list_iterator<T, Ref,ptr> self;

Node* _node;

};

operator!= operator ==

//重载operator!=

bool operator!=(const self& it)const

{

return _node != it._node;

}

//重载operator==

bool operator==(const self& it)const

{

return _node == it._node;

}

operator++

//重载operator++(前置)

self& operator++()

{

_node = _node->_next;

return *this;

}

//重载operator++(后置)

self& operator++(int)

{

self tmp(*this);

_node = _node->_next;

return tmp;

}

operator--

//重载operator--(前置)

self& operator--()

{

_node = _node->_prev;

return *this;

}

//重载operator--(后置)

self& operator--(int)

{

self tmp(*this);

_node = _node->_prev;

return tmp;

}

operator* operator->

//重载operator*

Ref& operator* ()

{

return _node->_val;

}

//重载operator->

ptr operator->()

{

return _node->_val;

}

list数据结构

构造函数

list的初始化

双向带带头循环

//空list初始化

void empty_init()

{

_head = new Node;

_head->_next = _head;

_head->_prev = _head;

_size = 0;

}

节点初始化

默认构造函数默认构造函数用迭代器初始化拷贝构造函数赋值运算符重载赋值运算符重载

//默认构造函数

list()

{

empty_init();

}

//默认构造函数

list(int n, const T& val = T())

{

empty_init();

while (n--)

{

push_back(val);

}

}

//用迭代器初始化

template <class Iterator>

list(Iterator first, Iterator last)

{

empty_init();

while (first != last)

{

push_back(*first);

++first;

}

}

//拷贝构造函数

list(const list<T>& lt)

{

empty_init();

for (auto e : lt)

{

push_back(e);

}

}

//赋值运算符重载

list<T> operator=(list<T> lt)

{

swap(lt);

return *this;

}

//赋值运算符重载

~list()

{

clear();

delete _head;

}

迭代器

iterator begin()

{

return _head->_next;

}

iterator end()

{

return _head;

}

const_iterator begin()const

{

return _head->_next;

}

const_iterator end()const

{

return _head;

}

list modify

insert

在pos位置插入节点,同数据结构是一样的,在这里面不过多赘述(可以参考)

//insert

iterator insert(iterator pos, const T& x)

{

Node* newcode = new Node(x);

Node* cur = pos._node;

Node* prev = cur->_prev;

prev->_next = newcode;

newcode->_prev = prev;

newcode->_next = cur;

cur->_prev = newcode;

++_size;

return pos;

}

erase

删除迫使位置的节点,同样(可以参考)

//erase

iterator erase(iterator pos)

{

assert(pos._node != _head);

Node* cur = pos._node;

Node* prev = cur->_prev;

Node* next = cur->_next;

prev->_next = next;

next->_prev = prev;

delete cur;

--_size;

return next;

}

头插、头删、尾插、尾删

可以复用insert ersae STL标准模板库也是进行复用

// 头插

void push_front(const T& x)

{

insert(begin(), x);

}

//头删

void pop_front()

{

erase(begin());

}

//尾插

void push_back(const T& x)

{

insert(begin());

}

//尾删

void pop_back()

{

erase(--end());

}

list operator

交换

void swap(list<T> lt)

{

std::swap(_head, lt._head);

std::swap(_size, lt._size);

}

clear

析构函数也是利用了clear

//删除

void clear()

{

iterator it = begin();

while (it != end())

{

it = erase(it);

++it;

}

}

源码

#pragma once

#include <iostream>

#include <assert.h>

namespace MyList

{

//节点

template<class T>

struct _list_node

{

_list_node<T>* _next;

_list_node<T>* _prev;

T _val;

_list_node(const T& val = T())

: _next(nullptr)

, _prev(nullptr)

, _val(val)

{ }

};

//迭代器

template<class T,class Ref,class ptr>

struct _list_iterator

{

typedef _list_node<T> Node;

typedef _list_iterator<T, Ref,ptr> self;

Node* _node;

_list_iterator (Node* node)

:_node(node)

{ }

//重载operator*

Ref& operator* ()

{

return _node->_val;

}

//重载operator->

ptr operator->()

{

return _node->_val;

}

//重载operator++(前置)

self& operator++()

{

_node = _node->_next;

return *this;

}

//重载operator++(后置)

self& operator++(int)

{

self tmp(*this);

_node = _node->_next;

return tmp;

}

//重载operator--(前置)

self& operator--()

{

_node = _node->_prev;

return *this;

}

//重载operator--(后置)

self& operator--(int)

{

self tmp(*this);

_node = _node->_prev;

return tmp;

}

//重载operator!=

bool operator!=(const self& it)const

{

return _node != it._node;

}

//重载operator==

bool operator==(const self& it)const

{

return _node == it._node;

}

};

//list

template<class T>

class list

{

typedef _list_node<T> Node;

public:

typedef _list_iterator<T, T&,T*> iterator;

typedef _list_iterator<T, const T&,const T*> const_iterator;

//空list初始化

void empty_init()

{

_head = new Node;

_head->_next = _head;

_head->_prev = _head;

_size = 0;

}

//用n个val初始化

list(int n, const T& val = T())

{

empty_init();

while (n--)

{

push_back(val);

}

}

//用迭代器初始化

template <class Iterator>

list(Iterator first, Iterator last)

{

empty_init();

while (first != last)

{

push_back(*first);

++first;

}

}

//默认构造函数

list()

{

empty_init();

}

//拷贝构造函数

list(const list<T>& lt)

{

empty_init();

for (auto e : lt)

{

push_back(e);

}

}

//赋值运算符重载

list<T> operator=(list<T> lt)

{

swap(lt);

return *this;

}

//析构函数

~list()

{

clear();

delete _head;

}

//删除

void clear()

{

iterator it = begin();

while (it != end())

{

it = erase(it);

++it;

}

}

//迭代器

iterator begin()

{

return _head->_next;

}

iterator end()

{

return _head;

}

const_iterator begin()const

{

return _head->_next;

}

const_iterator end()const

{

return _head;

}

//void push_back(const T& x)

//{

//Node* newcode = new Node(x);

//Node* tail = _head->_prev;

//tail->_next = newcode;

//newcode->_next = _head;

//newcode->_prev = tail;

//_head->_prev = newcode;

//++_size;

//}

// 头插

void push_front(const T& x)

{

insert(begin(), x);

}

//头删

void pop_front()

{

erase(begin());

}

//尾插

void push_back(const T& x)

{

insert(begin());

}

//尾删

void pop_back()

{

erase(--end());

}

//insert

iterator insert(iterator pos, const T& x)

{

assert(pos._node != _head);

Node* newcode = new Node(x);

Node* cur = pos._node;

Node* prev = cur->_prev;

prev->_next = newcode;

newcode->_prev = prev;

newcode->_next = cur;

cur->_prev = newcode;

++_size;

return pos;

}

//erase

iterator erase(iterator pos)

{

assert(pos._node != _head);

Node* cur = pos._node;

Node* prev = cur->_prev;

Node* next = cur->_next;

prev->_next = next;

next->_prev = prev;

delete cur;

--_size;

return next;

}

//大小

size_t size()const

{

return _size;

}

//交换

void swap(list<T> lt)

{

std::swap(_head, lt._head);

std::swap(_size, lt._size);

}

private:

Node* _head;

size_t _size;

};

}



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。