您的位置:首页 > 新闻 > 会展 > 【C++】list介绍以及模拟实现(超级详细)

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

2024/10/17 7:31:20 来源:https://blog.csdn.net/Cayyyy/article/details/140921383  浏览:    关键词:【C++】list介绍以及模拟实现(超级详细)

欢迎来到我的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`
      • insert
      • erase
      • 头插、头删、尾插、尾删
    • `list operator`
      • 交换
      • clear
  • 源码

前言

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 的容量不足以容纳新增元素时,还需要进行动态扩容,这是一个成本较高的操作
vectorlist
底层
结构
动态顺序表,一段连续空间带头结点的双向循环链表
随 机
访 问
支持随机访问,访问某个元素效率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结构

  • 三个模板参数
    1. 第一个模板参数控制类型
    2. 第二个模板参数控制返回值类型const 非const
    3. 第三个模板参数控制返回的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的初始化

  • 双向带带头循环
_head
//空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;}};//listtemplate<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());}//insertiterator 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;}//eraseiterator 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;};}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com