【C++语法堂】STL标准库学习_list容器

目录

STL标准库学习_list

【1】List的介绍及使用

【2】List常用的接口

【2.1】构造函数

【2.2】析构函数

【2.3】迭代器相关

【2.4】容量相关

【2.5】元素访问相关

【2.6】修改相关

【2.7】运行相关

【2.8】观察相关

【2.9】非成员函数重载

【3】list模拟实现

【3.1】list节点结构框架

【3.2】 list类结构框架

【3.2】 无参构造DHList()

【3.3】initializer_list构造函数DHList(initializer_list il)

【3.4】迭代器构造DHList(InputIterator first, InputIterator last)

【3.5】析构函数~DHList()

【3.6】拷贝构造DHList(const DHList& list)

【3.7】赋值拷贝DHList& operator=(DHList list)

【3.8】尾插PushBack(const T& val)

【3.9】头插void PushFront(const T& val)

【3.10】尾删void PopBack()

【3.11】头删void PopFront()

【3.12】指定位置插入iterator Insert(iterator pos, const T& val)

【3.13】指定位置删除iterator Erase(iterator pos)

【3.14】 判断空bool Empty()

【3.15】 返回大小size_t Size()

【3.16】交换void Swap(List& list)

【3.17】清空void Clear()

【3.18】 正向迭代器

【3.19】 list完整类

【3.20】vector和list的对比


STL标准库学习_list

【1】List的介绍及使用

  • list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
  • list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
  • list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。
  • 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
  • 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)
  • 详细的STL文档请参考:

https://cplusplus.com/reference/list/list/

【2】List常用的接口

【2.1】构造函数

// 默认构造
explicit list (const allocator_type& alloc = allocator_type());
​
// 构造的list中包含n个值为val的元
explicit list (size_type n, const value_type& val = value_type(),const allocator_type& alloc = allocator_type());
​
// 迭代器构造
template 
list (InputIterator first, InputIterator last,const allocator_type& alloc = allocator_type());
​
// 拷贝构造
list (const list& x);
list& operator= (const list& x);

【2.2】析构函数

// 析构函数
~list();

【2.3】迭代器相关

// 正向迭代器
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
​
// 反向迭代器
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
reverse_iterator rend();
const_reverse_iterator rend() const;
​
// C++11
const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
const_reverse_iterator crbegin() const noexcept;
const_reverse_iterator crend() const noexcept;

【2.4】容量相关

// 检查空
bool empty() const;
​
// 返回大小
size_type size() const;
​
// 返回最大元素数
size_type max_size() const;

【2.5】元素访问相关

// 返回list的第一个节点中值的引用
reference front();
const_reference front() const;
​
// 返回list的最后一个节点中值的引用
reference back();
const_reference back() const;

【2.6】修改相关

// 将新内容分配给容器
template 
void assign (InputIterator first, InputIterator last);
void assign (size_type n, const value_type& val);
​
// 在list首元素前插入值为val的元素
void push_front (const value_type& val);
​
// 删除list中第一个元素
void pop_front();
​
// 在list尾部插入值为val的元素
void push_back (const value_type& val);
​
// 删除list中最后一个元素
void pop_back();
​
// 在list position 位置中插入值为val的元素
iterator insert (iterator position, const value_type& val);
void insert (iterator position, size_type n, const value_type& val);
template 
void insert (iterator position, InputIterator first, InputIterator last);
​
// 删除list position位置的元素
iterator erase (iterator position);
iterator erase (iterator first, iterator last);
​
// 交换两个list中的元素
void swap (list& x);
​
// 清空list中的有效元素
void clear();
​
// 调整大小
void resize (size_type n, value_type val = value_type());
​
// c++11
template 
void emplace_front (Args&&... args);
​
template 
void emplace_back (Args&&... args);
​
template 
iterator emplace (const_iterator position, Args&&... args);

【2.7】运行相关

// 转移(将链表转移到另一个链表中)
void splice (iterator position, list& x);
void splice (iterator position, list& x, iterator i);
void splice (iterator position, list& x, iterator first, iterator last);
// 给定一个值,自己查找到删除
void remove (const value_type& val);
// 满足某个条件删除值
template 
void remove_if (Predicate pred);
​
// 去重
void unique();
template 
void unique (BinaryPredicate binary_pred);
​
// 合并
void merge (list& x);
template 
void merge (list& x, Compare comp);
​
// 排序
void sort();
template 
void sort (Compare comp);
​
// 逆置
void reverse();

【2.8】观察相关

allocator_type get_allocator() const;

【2.9】非成员函数重载

template 
bool operator== (const list& lhs, const list& rhs);

template 
bool operator<  (const list& lhs, const list& rhs);

template 
bool operator<  (const list& lhs, const list& rhs);

template 
bool operator<= (const list& lhs, const list& rhs);

template 
bool operator>  (const list& lhs, const list& rhs);

template 
bool operator>= (const list& lhs, const list& rhs);

template 
void swap (list& x, list& y);

【3】list模拟实现

【C++语法堂】STL标准库学习_list容器_第1张图片

强调一下head是哨兵结点,本身不存储数据,作用只是指向头和尾!

【3.1】list节点结构框架

// 《带头双向循环链表 - 数据结构》
// 模板参数
template
// 结构体的默认成员是public.
struct ListNode
{
public:
    // 显示声明构造函数
    ListNode(const T& val = T())    // 匿名构造
        : _prev(nullptr)
        , _next(nullptr)
        , _data(val)
    {}
​
public:
    ListNode* _prev;        // 指向上一个节点.
    ListNode* _next;        // 指向下一个节点.
    T _data;                // 存储的数据
};

这里对于T()说明一下!这里是匿名构造,对于传来的参数类型不确定,可能是内置类型,也可能是类对象!所以不能想当然比如说默认赋值0!需要使用对象的构造函数.

【3.2】 list类结构框架

创建链表节点:链表节点本身指向自己。

// 《带头双向循环链表 - 类结构》
template
class List
{
private:
    typedef ListNode Node;   
private:
    Node* _head;
    size_t _size;
};

【3.2】 无参构造DHList()

// 《带头双向循环链表 - 无参构造函数》
// 构造函数(无参构造函数)
DHList() {
    EmptyInitialize();
}
​
​
/ 《带头双向循环链表 - 创建节点初始化》
void EmptyInitialize()
{
    _head = new Node(T());
    _head->_prev = _head;
    _head->_next = _head;
    _size = 0;
}

【3.3】initializer_list构造函数DHList(initializer_list il)

// initializer_list构造函数
DHList(initializer_list il) {
    EmptyInitialize();
    typename initializer_list::iterator it = il.begin();
    while (it != il.end()) {
        PushBack(*it);
        ++it;
    }
}
​

【3.4】迭代器构造DHList(InputIterator first, InputIterator last)

// 构造函数(迭代器构造)
// 构造函数(迭代器构造)
template
DHList(InputIterator first, InputIterator last) {
    EmptyInitialize();
    while (first != last) {
        PushBack(*first);
        ++first;
    }
}
    
// 《带头双向循环链表 - 创建节点初始化》
void EmptyInitialize()
{
    _head = new Node(T());
    _head->_prev = _head;
    _head->_next = _head;
    _size = 0;
}

【3.5】析构函数~DHList()

// 《带头双向循环链表 - 析构函数》
~DHList() {
    Clear();
    delete _head;
    _head = nullptr;
}

【3.6】拷贝构造DHList(const DHList& list)

// 链表拷贝构造函数 - 传统写法
//DHList(const DHList& list)
//{
//  // 创造节点,初始化节点
//  EmptyInitialize();
//  for (const T& e : list)
//  {
//      PushBack(e);
//  }
//}
​
// 链表拷贝构造函数 - 现代写法
DHList(const DHList& list) {
    // 创造节点,初始化节点
    EmptyInitialize();
    DHList temp = DHList(list.begin(), list.end());
    Swap(temp);
}

【3.7】赋值拷贝DHList& operator=(DHList list)

// 运算符重载:[] - 传统写法
/*DHList& operator=(const DHList& list)
{
    if (this != &list)
    {
        this->Clear();
        for (const auto& v : list)
        {
            PushBack(v);
        }
​
        return *this;
    }
}*/
​
// 运算符重载:[] - 现代写法
DHList& operator=(DHList list) {
    Swap(list);
    return *this;
}
​

【3.8】尾插PushBack(const T& val)

【C++语法堂】STL标准库学习_list容器_第2张图片

 

// 《链表尾插》
void PushBack(const T& val)
{
    // 创建一个新的节点.
    Node* newNode = new Node(val);
    // 找到尾节点.
    Node* tail = _head->_prev;

    // 链接
    // 尾节点->下一个节点 指向新的节点
    tailNode->_nextNode = newNode;
    // 头节点->上一个节点 指向新的节点
    _head->_prevNode = newNode;
    // 新节点->下一个节点 指向头节点
    newNode->_nextNode = _head;
    // 新节点->上一个节点 指向尾节点(更改完成tailNode就不是最后一个节点了)
    newNode->_prevNode = tailNode;
}

【3.9】头插void PushFront(const T& val)

【C++语法堂】STL标准库学习_list容器_第3张图片

// 头插数据
void PushFront(const T& val) {
    // 创建新的节点
    Node* newNode = new Node(val);
    // 找到链表头节点的下一个节点
    Node* nextNode = _head->_nextNode;

    // 头节点的下一个节点->上一个节点 指向新节点
    nextNode->_prevNode = newNode;
    // 头节点->下一个节点 指向新节点
    _head->_nextNode = newNode;
    // 新节点->下一个节点 指向原头节点的下一个节点
    newNode->_nextNode = nextNode;
    // 新节点->上一个节点 指向头节点
    newNode->_prevNode = _head;
}

【3.10】尾删void PopBack()

【C++语法堂】STL标准库学习_list容器_第4张图片

// 尾删数据
void PopBack() {
    // 判断链表不为空链表
    assert(!Empty());

    // 找到链表最后一个节点
    Node* pTail = _head->_prevNode;
    // 找到链表最有一个节点的上一个节点
    Node* pFirst = pTail->_prevNode;
    // 头节点->上一个节点值 指向链表最后一个节点的上一个节点
    _head->_prevNode = pFirst;
    // 链表最后一个节点->上一个节点的下一个节点 指向头节点
    pFirst->_nextNode = _head;
    // 释放
    delete pTail;
    pTail = nullptr;
}

【3.11】头删void PopFront()

【C++语法堂】STL标准库学习_list容器_第5张图片

// 头删数据
void PopFront() {
    // 判断链表不为空链表
    assert(!Empty());
    
    // 找到链表第一个节点 
    Node* pFirst = _head->_nextNode;
    // 找到链表第二个节点
    Node* pSecond = pFirst->_nextNode;
    // 头节点->下一个节点 指向第二个节点
    _head->_nextNode = pSecond;
    // 第二个节点->上一个节点 指向头节点
    pSecond->_prevNode = _head;
    // 释放
    delete pFirst;
    pFirst = nullptr;
}

【3.12】指定位置插入iterator Insert(iterator pos, const T& val)

由于C++特别喜欢复用,我们也发现实现一个Insert()就可以实现以上的功能。

【C++语法堂】STL标准库学习_list容器_第6张图片

// 《带头双向循环链表 - 尾插》
void PushBack(const T& val)
{
    Insert(end(), val);
}

// 《带头双向循环链表 - 头插》
void PushFront(const T& val)
{
    Insert(begin(), val);
}

// 《带头双向循环链表 - 指定位置插入》
iterator Insert(iterator pos, const T& val) {
    assert(pos != nullptr);
    // 创建新的节点
    Node* newNode = new Node(val);
    // 传递的pos位置是迭代器,先从迭代器中拿到节点.
    Node* pCurNode = pos._node;
    Node* pPosPrevNode = pCurNode->_prevNode;
    // pos上一个节点->下一个节点 指向新的节点
    pPosPrevNode->_nextNode = newNode;
    // pos上一个节点 指向新节点
    pCurNode->_prevNode = newNode;
    // 新节点上一个节点指向 原pos上一个节点
    newNode->_prevNode = pPosPrevNode;
    // 新节点下一个节点指向 原pos节点
    newNode->_nextNode = pCurNode;

    ++_size;
    return iterator(newNode);
}

【3.13】指定位置删除iterator Erase(iterator pos)

同样根据C++的特性实现Erase(),实现以上所有的功能。

【C++语法堂】STL标准库学习_list容器_第7张图片

// 《带头双向循环链表 - 尾删》
void PopBack()
{
    Erase(--end());
}

// 《带头双向循环链表 - 头删》
void PopFront()
{
    Erase(begin());
}

// 《带头双向循环链表 - 指定位置删除》
iterator Erase(iterator pos) {
    assert(pos != nullptr);
    assert(!Empty());

    // 获取pos下一个节点
    Node* prevNode = pos._node->_prevNode;
    // 获取pos上一个节点
    Node* nextNode = pos._node->_nextNode;

    // pos上一个节点->下一个节点 指向pos下一个节点
    prevNode->_nextNode = nextNode;
    // pos下一个节点->上一个节点 指向pos上一个节点
    nextNode->_prevNode = prevNode;
    // 释放pos位置节点
    delete pos._node;

    --_size;
    return iterator(nextNode);
}

【3.14】 判断空bool Empty()

// 判断为空
bool Empty() {
    return _head->_nextNode == _head;
}

【3.15】 返回大小size_t Size()

// 返回大小
size_t Size() {
    return _size;
}

【3.16】交换void Swap(List& list)

// 《链表内部交换》
void Swap(List& list)
{
    std::swap(_head, list._head);
    std::swap(_size, list._size);
}

【3.17】清空void Clear()

// 《带头双向循环链表 - 清理链表保留头节点》
void Clear()
{
    // 直接使用内部的迭代器
    iterator itBegin = begin();
    while (itBegin != end())
    {
        itBegin = Erase(itBegin);
    }
}

【3.18】 正向迭代器

        iterator 原生指针,数组结构正好支持迭代器的行为。

        iterator不能和顺序表一样,iterator 原生指针(Node*),不能满足迭代器行为。我们可以用类封装+运算符重载支持。

// 链表struct结构:迭代器
// typedef __list_iterator iterator;
// typedef __list_iterator const_iterator;
template
struct __ListIterator {
    typedef DListNode Node;
    typedef __ListIterator Self;
    Node* _node;
    
    // 构造函数
    __ListIterator(Node* pNode) 
        : _node(pNode) 
    {};

    // 运算符重载:*
    Ref operator*() { 
        return _node->_data; 
    }

    // 运算符重载:->
    Ptr operator->() { 
        return &_node->_data; 
    }


    // 运算符重载:++前置
    Self& operator++() {
        _node = _node->_nextNode;
        return *this;
    }

    // 运算符重载:++后置
    Self operator++(int)
    {
        Self temp(*this);
        _node = _node->_next;
        return temp;
    }

    // 运算符重载:--前置
    Self& operator--() {
        _node = _node->_prevNode;
        return *this;
    }

    // 运算符重载:--后置
    Self operator--(int)
    {
        Self temp(*this);
        _node = _node->_prev;
        return temp;
    }

    // 运算符重载:!=
    bool operator!=(const Self& it) { 
        return _node != it._node; 
    }

    // 运算符重载:==
    bool operator==(const Self& it) { 
        return _node == it._node; 
    }
};


// 《迭代器》
typedef __ListIterator iterator;
// typedef __ListConstIterator const_iterator;
typedef __ListIterator const_iterator;
iterator begin()
{
    return iterator(_head->_next);
}
iterator end()
{
    return iterator(_head);
}

const_iterator begin() const
{
    return const_iterator(_head->_next);
}
const_iterator end() const
{
    return const_iterator(_head);
}

【3.19】 list完整类

#include 
#include 
using namespace std;

namespace ShaXiang_DList1 {
    /* 带头双向循环链表 - 数据结构*/
    template
    struct DListNode {
        // 构造函数(带默认构造函数)
        DListNode(const T& val = T()) 
            : _prevNode(nullptr)
            , _nextNode(nullptr)
            , _data(val) 
        {};
    
        DListNode* _prevNode;    // 指向上一个节点地址
        DListNode* _nextNode;    // 指向下一个节点地址
        T _data;                // 存储数据值
    };

    /* 带头双向循环链表 - 迭代器类 */
    template
    struct __ListIterator {
        typedef DListNode Node;
        typedef __ListIterator Self;
        Node* _node;
        
        // 构造函数
        __ListIterator(Node* pNode) 
            : _node(pNode) 
        {};

        // 运算符重载:*
        Ref operator*() { 
            return _node->_data; 
        }

        // 运算符重载:->
        Ptr operator->() { 
            return &_node->_data; 
        }


        // 运算符重载:++前置
        Self& operator++() {
            _node = _node->_nextNode;
            return *this;
        }

        // 运算符重载:++后置
        Self operator++(int)
        {
            Self temp(*this);
            _node = _node->_next;
            return temp;
        }

        // 运算符重载:--前置
        Self& operator--() {
            _node = _node->_prevNode;
            return *this;
        }

        // 运算符重载:--后置
        Self operator--(int)
        {
            Self temp(*this);
            _node = _node->_prev;
            return temp;
        }

        // 运算符重载:!=
        bool operator!=(const Self& it) { 
            return _node != it._node; 
        }

        // 运算符重载:==
        bool operator==(const Self& it) { 
            return _node == it._node; 
        }
    };


    /* 带头双向循环链表 - 类*/
    template
    class DList {
    // 公有取别名
    public:
        typedef DListNode Node;
        typedef __ListIterator iterator;
        typedef __ListIterator const_iterator;

    // 私有成员变量
    private:
        Node* _head;
        size_t _size;

    // 公开成员函数
    public:
        // 迭代器
        iterator begin() { 
            return iterator(_head->_nextNode);
        }
        iterator end() { 
            return iterator(_head);
        }

        const_iterator begin() const {
            return const_iterator(_head->_nextNode);
        }
        const_iterator end() const { 
            return const_iterator(_head);
        }

    // 公有成员函数
    public:
        // 构造函数(无参构造函数)
        DList() {
            EmptyInitialize();
        }

        // 构造函数(迭代器构造)
        template
        DList(InputIterator first, InputIterator last) {
            EmptyInitialize();
            while (first != last) {
                PushBack(*first);
                ++first;
            }
        }

        // 析构函数
        ~DList() {
            Clear();
            delete _head;
            _head = nullptr;
        }

        // 链表拷贝构造函数 - 传统写法
        //DList(const DList& list)
        //{
        //    // 创造节点,初始化节点
        //    EmptyInitialize();
        //    for (const T& e : list)
        //    {
        //        PushBack(e);
        //    }
        //}

        // 链表拷贝构造函数 - 现代写法
        DList(const DList& list) {
            // 创造节点,初始化节点
            EmptyInitialize();
            DList temp = DList(list.begin(), list.end());
            Swap(temp);
        }

    // 公有成员函数
    public:
        // 初始化
        void EmptyInitialize(){
            // 创建节点->节点指向自己.
            _head = new Node(T());
            _head->_nextNode = _head;    // 初始化指向自己
            _head->_prevNode = _head;    // 初始化指向自己
            _head->_data = 0;            // 数据初始化为0
            _size = 0;                    // 头节点存放数据个数初始化为0
        }

        // 尾插数据
        void PushBack(const T& val) {
            /*
            // 创建新的节点
            Node* newNode = new Node(val);
            // 找到链表的尾节点
            Node* tailNode = _head->_prevNode;

            // 尾节点->下一个节点 指向新的节点
            tailNode->_nextNode = newNode;
            // 头节点->上一个节点 指向新的节点
            _head->_prevNode = newNode;
            // 新节点->下一个节点 指向头节点
            newNode->_nextNode = _head;
            // 新节点->上一个节点 指向尾节点(更改完成tailNode就不是最后一个节点了)
            newNode->_prevNode = tailNode;
            */

            Insert(end(), val);
            ++_size;
        }

        // 头插数据
        void PushFront(const T& val) {
            /*
            // 创建新的节点
            Node* newNode = new Node(val);
            // 找到链表头节点的下一个节点
            Node* nextNode = _head->_nextNode;

            // 头节点的下一个节点->上一个节点 指向新节点
            nextNode->_prevNode = newNode;
            // 头节点->下一个节点 指向新节点
            _head->_nextNode = newNode;
            // 新节点->下一个节点 指向原头节点的下一个节点
            newNode->_nextNode = nextNode;
            // 新节点->上一个节点 指向头节点
            newNode->_prevNode = _head;
            */

            Insert(begin(), val);
            ++_size;
        }

        // 尾删数据
        void PopBack() {
            /*
            // 判断链表不为空链表
            assert(!Empty());

            // 找到链表最后一个节点
            Node* pTail = _head->_prevNode;
            // 找到链表最有一个节点的上一个节点
            Node* pFirst = pTail->_prevNode;
            // 头节点->上一个节点值 指向链表最后一个节点的上一个节点
            _head->_prevNode = pFirst;
            // 链表最后一个节点->上一个节点的下一个节点 指向头节点
            pFirst->_nextNode = _head;
            // 释放
            delete pTail;
            pTail = nullptr;
            */

            Erase(--end());
            --_size;
        }
        
        // 头删数据
        void PopFront() {
            /*
            // 判断链表不为空链表
            assert(!Empty());
            
            // 找到链表第一个节点 
            Node* pFirst = _head->_nextNode;
            // 找到链表第二个节点
            Node* pSecond = pFirst->_nextNode;
            // 头节点->下一个节点 指向第二个节点
            _head->_nextNode = pSecond;
            // 第二个节点->上一个节点 指向头节点
            pSecond->_prevNode = _head;
            // 释放
            delete pFirst;
            pFirst = nullptr;
            */

            Erase(begin());
            --_size;
        }

        // 指定位置插入数据
        iterator Insert(iterator pos, const T& val) {
            assert(pos != nullptr);
            // 创建新的节点
            Node* newNode = new Node(val);
            // 传递的pos位置是迭代器,先从迭代器中拿到节点.
            Node* pCurNode = pos._node;
            Node* pPosPrevNode = pCurNode->_prevNode;
            // pos上一个节点->下一个节点 指向新的节点
            pPosPrevNode->_nextNode = newNode;
            // pos上一个节点 指向新节点
            pCurNode->_prevNode = newNode;
            // 新节点上一个节点指向 原pos上一个节点
            newNode->_prevNode = pPosPrevNode;
            // 新节点下一个节点指向 原pos节点
            newNode->_nextNode = pCurNode;

            ++_size;
            return iterator(newNode);
        }

        // 指定位置删除数据
        iterator Erase(iterator pos) {
            assert(pos != nullptr);
            assert(!Empty());

            // 获取pos下一个节点
            Node* prevNode = pos._node->_prevNode;
            // 获取pos上一个节点
            Node* nextNode = pos._node->_nextNode;

            // pos上一个节点->下一个节点 指向pos下一个节点
            prevNode->_nextNode = nextNode;
            // pos下一个节点->上一个节点 指向pos上一个节点
            nextNode->_prevNode = prevNode;
            // 释放pos位置节点
            delete pos._node;

            --_size;
            return iterator(nextNode);
        }

        // 判断为空
        bool Empty() {
            return _head->_nextNode == _head;
        }

        // 返回大小
        size_t Size() {
            return _size;
        }

        // 清空
        void Clear() {
            iterator it = begin();
            while (it != end()) {
                it = Erase(it);
            }
        }

        // 交换
        void Swap(DList& list)
        {
            std::swap(_head, list._head);
            std::swap(_size, list._size);
        }

        // 打印测试
        void Print() {
            Node* beginNode = _head->_nextNode;
            while (beginNode != _head) {
                cout << beginNode->_data << " ";
                beginNode = beginNode->_nextNode;
            }
            cout << endl;
        }
    
    // 公有成员函数
    public:
        // 运算符重载:[] - 传统写法
        /*DList& operator=(const DList& list)
        {
            if (this != &list)
            {
                this->Clear();
                for (const auto& v : list)
                {
                    PushBack(v);
                }
        
                return *this;
            }
        }*/

        // 运算符重载:[] - 现代写法
        DList& operator=(DList list) {
            Swap(list);
            return *this;
        }

    };
}

【3.20】vector和list的对比

        vector与list都是STL中非常重要的序列式容器,由于两个容器的底层结构不同,导致其特性以及应用场景不同,其主要不同如下:

vector

list

底 层 结 构

动态顺序表,一段连续空间

带头结点的双向循环链表

随 机 访 问

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

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

插 入 和 删 除

任意位置插入和删除效率低,需要搬移元素,时间复杂 度为O(N),插入时有可能需要增容,增容:开辟新空 间,拷贝元素,释放旧空间,导致效率更低

任意位置插入和删除效率高,不 需要搬移元素,时间复杂度为 O(1)

空 间 利 用 率

底层为连续空间,不容易造成内存碎片,空间利用率 高,缓存利用率高

底层节点动态开辟,小节点容易 造成内存碎片,空间利用率低, 缓存利用率低

迭 代 器

原生态指针

对原生态指针(节点指针)进行封装

迭 代 器 失 效

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

插入元素不会导致迭代器失效, 删除元素时,只会导致当前迭代 器失效,其他迭代器不受影响

使 用 场 景

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

大量插入和删除操作,不关心随 机访问

你可能感兴趣的:(C++,数据结构,c++,数据结构)