【C++】7-STL-list

【C++】7-list

1. 了解list

list更详细的介绍

  1. list是序列容器,允许在序列内的任意位置进行常量时间的插入和删除,并进行前后两个方向的迭代。
  2. list的底层存储数据的内容是双向链表结构,双向链表中的每个元素存储在互不相关的独立结点中(即存储在不同且不相关的存储位置),且结点中通过指针指向其前一个元素和后一个元素。
  3. list和forward_list相似:主要区别在于forward_list的对象是单链表,只能向前迭代,以使其更简单且高效。
  4. 与其他基本标准序列容器(array、vector和deque)相比,list在容器内的任何位置插入、提取和移动元素(迭代器已经获得)方面通常表现更好,因此在大量使用插入、提取、移动等的算法(如排序算法)中也表现更好。
  5. 与其他序列容器相比,list和forward_lists的主要缺点是它们不支持元素的随机访问(即通过位置直接访问元素),例如,要访问列表中的第六个元素,必须从已知位置(如开始或结束)迭代到该位置,这需要在两者之间的距离上花费线性时间。它们还消耗一些额外的内存来保存与每个元素相关联的链接信息(对于存储类型较小元素的大型list来说,这可能是一个重要因素)。
  6. 带头结点

2. list的使用

与string、vector一样,list也有许多接口,但我们目前只需掌握其中比较常用且重要的接口即可。

(list的接口与vector类似,在vector那一文章中已经做了详细介绍,因此下面只是简要说明)

2.1 构造(constructor)

接口详细信息

函数功能说明
explicit list (const allocator_type& alloc = allocator_type());默认构造
explicit list (size_type n, const value_type& val = value_type(), const allocator_type& alloc = allocator_type());初始化为n个val
template list (InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type());利用迭代器的范围构造
list (const list& x);拷贝构造
operator=构造(赋值)
上述函数可等价于:
list(const value_type& val = value_type());
list(size_t n, const value_type& val = valye_type());
template list(InputIterator first, InputIterator last, const val = value_type());
list(const list& x);
operator=

2.2 迭代器(iterators)

list的迭代器不支持双目运算符+-(后面的modifiers会讲到)

函数功能说明
begin返回指向第一个元素的迭代器
end返回指向最后一个元素的下一个位置的迭代器
rbegin返回指向最后一个元素的下一个位置的迭代器
rend返回指向第一个元素的迭代器
cbegin 返回指向(const 对象的)第一个元素的迭代器。指向的内容不可修改
cend 返回指向(const 对象的)最后一个元素的下一个位置的迭代器。指向的内容不可修改

2.3 容量(capacity)

函数功能说明
empty判断list是否为空
sizelist大小(有效数据/结点个数)

2.4 元素访问(element acess)

函数功能说明
front取得第一个元素(头结点的下一个元素)
back取得最后一个元素(头结点的上一个元素)

2.5 修改(modifiers)

函数功能说明
assign重新分配list的内容
push_front头插
pop_front头删
push_back尾插
pop_back尾删
insert任意位置插入
erase任意位置删除
resize重新设置list大小
clear清空内容
swap交换两个结点的内容

测试用例:

void test_list1()
{
	list<int> It1;
	cout << "Initialize::>>>>>>>>>>>>>>>>>>>>>>>" << endl;
	cout << "list<int> It1" << endl;
	Print(It1);
	// 1. assign
	cout << "assign:>>>>>>>>>>>>>>>>>>>>>>>" << endl;
	It1.assign(3, 10);
	cout << "It1.assign(3, 10)" << endl;
	Print(It1);
	// 2. push
	cout << "push_back和push_front:>>>>>>>>>>>>>>>>>>>>>>>" << endl;
	It1.push_back(1);
	It1.push_back(2);
	It1.push_front(3);
	It1.push_front(4);
	Print(It1);
	// 3. pop
	cout << "pop:>>>>>>>>>>>>>>>>>>>>>>>" << endl;
	It1.pop_back();
	It1.pop_front();
	Print(It1);
	// 4. insert
	// iterator insert (iterator position, const value_type& val);
	// void insert (iterator position, size_type n, const value_type& val);
	cout << "insert:>>>>>>>>>>>>>>>>>>>>>>>" << endl;
	list<int>::iterator it = ++It1.begin();// 迭代器无法使用双目运算符'+'/'-'
	// iterator insert (iterator position, const value_type& val);
	cout << "it = It1.insert(it, 100 + i)" << endl;
	for (size_t i = 0; i < 4; ++i)
	{
		// insert看实际情况选择是否重新接收it
		// it = It1.insert(it, 100 + i);
		It1.insert(it, 100 + i);
	}
	Print(It1);
	// template <class InputIterator> void insert (iterator position, InputIterator first, InputIterator last);
	list<int> It2;
	It2.push_back(10);
	It2.push_back(20);
	It2.push_back(30);
	It2.push_back(40);
	cout << "It1.insert(++It1.begin(), It2.begin(), It2.end())" << endl;
	It1.insert(++It1.begin(), It2.begin(), It2.end());
	Print(It1);	
	// 5. erase
	// iterator erase(iterator position);
	// iterator erase(iterator first, iterator last);// 这里不做介绍
	cout << "erase:>>>>>>>>>>>>>>>>>>>>>>>" << endl;
	it = find(It1.begin(), It1.end(), 100);// 使用算法库里的find()找到数据为100的结点
	for (size_t i = 0; i < 3; ++i)
	{
		it = It1.erase(it);// 删除成功后,返回原被删除的结点的下一个结点的迭代器
	}
	Print(It1);
	// 6. resize
	// void resize (size_type n, value_type val = value_type());
	cout << "resize:>>>>>>>>>>>>>>>>>>>>>>>" << endl;
	It1.resize(4);
	cout << "It1.resize(4)" << endl;
	Print(It1);
	It1.resize(5);
	cout << "It1.resize(5)" << endl;
	Print(It1);
	It1.resize(8, 1000);
	cout << "It1.resize(8, 1000)" << endl;
	Print(It1);
	// 7. swap
	cout << "swap:>>>>>>>>>>>>>>>>>>>>>>>" << endl;
	It1.swap(It2);
	cout << "It1.swap(It2)" << endl;
	cout << "It1:" << endl;
	Print(It1);
	cout << "It2:" << endl;
	Print(It2);
	// 8. clear
	cout << "clear:>>>>>>>>>>>>>>>>>>>>>>>" << endl;
	It1.clear();
	Print(It1);
}

输出结果:

image-20230716223955214

3. list进阶 – 模拟实现

以上内容可以帮你了解怎么使用list,但如果想要熟练使用,还需要了解list的底层实现。

// 反向迭代器类
// 这是一个容器适配器
// 关于反向迭代器的底层原理,可以看【C++】stack_queue文章
template<class Iterator, class Ref, class Ptr>
class ReverseIterator
{
	typedef ReverseIterator<Iterator, Ref, Ptr> Self;

public:
	ReverseIterator(Iterator it)
		:_iterator(it)
	{}

	Ref operator*()
	{
		Iterator tmp(_iterator);

		return *(--tmp);
	}
	Ptr operator->()
	{
		return &(operator*());
	}
	// 前置
	Self& operator++()
	{
		--_iterator;

		return *this;
	}
	Self operator++(int)
	{
		Self tmp(_iterator);
		--tmp;

		return tmp;
	}
	// 前置
	Self& operator--()
	{
		++_iterator;

		return *this;
	}
	Self operator--(int)
	{
		Self tmp(_iterator);
		++tmp;

		return tmp;
	}
	bool operator==(const Self& s) const
	{
		return _iterator == s._iterator;
	}
	bool operator!=(const Self& s) const
	{
		return _iterator != s._iterator;
	}
private:
	Iterator _iterator;
};

/
namespace wzw
{
    // List的结点类
    template<class T>
    struct ListNode
    {
        // 提供默认构造
        ListNode(const T& val = T())
            :_pPre(nullptr)
            ,_pNext(nullptr)
            ,_val(val)
        {}

        ListNode<T>* _pPre;
        ListNode<T>* _pNext;
        T _val;
    };

    // List的迭代器类
    template<class T, class Ref, class Ptr>// Ref相当于T&,Ptr相当于T*
    class ListIterator
    {
        typedef ListNode<T>* PNode;// 结点指针
        typedef ListIterator<T, Ref, Ptr> Self;// 方便使用,同时提高代码的可维护性
    public:
        // 构造
        ListIterator(PNode pNode = nullptr)
            :_pNode(pNode)
        {}
        // 拷贝构造
        ListIterator(const Self& l)
            :_pNode(l._pNode)
        {}

        Ref operator*()
        {
            return _pNode->_val;
        }
        Ptr operator->()
        {
            return &_pNode->_val;
        }
        // 前置++
        Self& operator++()
        {
            _pNode = _pNode->_pNext;

            return *this;
        }
        // 后置++
        Self operator++(int)
        {
            Self tmp(*this);

            _pNode = _pNode->_pNext;

            return tmp;
        }
        // 前置--
        Self& operator--()
        {
            _pNode = _pNode->_pPre;

            return *this;
        }
        // 后置--
        Self operator--(int)
        {
            Self tmp(*this);

            _pNode = _pNode->_pPre;

            return tmp;
        }
        // 迭代器是否相等取决去迭代器指向的结点的地址
        bool operator!=(const Self& l) const
        {
            return !(*this == l);
        }
        bool operator==(const Self& l) const
        {
            return _pNode == l._pNode;
        }
    public:
        PNode _pNode;
    };

    //list类
    template<class T>
    class list
    {
        typedef ListNode<T> Node;
        typedef Node* PNode;
    public:
        typedef ListIterator<T, T&, T*> iterator;
        typedef ListIterator<T, const T&, const T*> const_iterator;
        
        typedef ReverseIterator<iterator, T&, T*> reverse_iterator;
        typedef ReverseIterator<const_iterator, const T&, const T*> const_reverse_iterator;
    public:
        // List的构造
        // explicit list (const allocator_type& alloc = allocator_type());
        list()
        {
            // 空链表有一个头结点
            CreateHead();
        }
        // explicit list (size_type n, const value_type& val = value_type(),
        // const allocator_type& alloc = allocator_type());
        list(int n, const T& value = T())
        {
            CreateHead();

            for (size_t i = 0; i < n; ++i)
            {
                push_back(value);
            }
        }
        // template <class InputIterator>
        // list(InputIterator first, InputIterator last,const allocator_type& alloc = allocator_type());
        template <class InputIterator>
        list(InputIterator first, InputIterator last)
        {
            CreateHead();

            while (first != last)
            {
                push_back(*first);
                ++first;
            }
        }
        // list (const list& x);
        // list(const list& l)也是正确的
        list(const list<T>& l)
        {
            CreateHead();

            list<T> tmp(l.begin(), l.end());

            swap(tmp);
        }
        // list& operator= (const list& x);
        // 此写法是优化过的写法 -- 只是写着更简洁了,不考虑效率
        list<T>& operator=(list<T> l)
        {
            clear();

            swap(l);

            return *this;
        }
        ~list()
        {
            clear();

            delete _pHead;
            _pHead = nullptr;
        }

        // List Iterator
        iterator begin()
        {
            return iterator(_pHead->_pNext);
        }
        iterator end()
        {
            return iterator(_pHead);
        }
        const_iterator begin() const
        {
            return const_iterator(_pHead->_pNext);
        }
        const_iterator end() const
        {
            return const_iterator(_pHead);
        }
        
        // 一般来说,rbegin()应该返回指向最后一个元素的迭代器end()-1,而非end(),这里之所以返回的是end(),而非end()-1,只是为了美观(正向和反向保持对称美)
        reverse_iterator rbegin()
        {
            return reverse_iterator(end());
        }
        reverse_iterator rend()
        {
            return reverse_iterator(begin());
        }
        const_reverse_iterator rbegin() const
        {
            return reverse_iterator(end());
        }
        const_reverse_iterator rend() const
        {
            return reverse_iterator(begin());
        }

        // List Capacity
        size_t size() const // 频繁调用,代价较大
        {
            size_t size = 0;

            const_iterator it = begin();
            while (it != end())
            {
                ++size;
                ++it;
            }

            return size;
        }
        bool empty() const
        {
            return begin() == end();
        }

        // List Access
        T& front()
        {
            return _pHead->_pNext->_val;
        }
        const T& front() const
        {
            return _pHead->_pNext->_val;
        }
        T& back()
        {
            return _pHead->_pPre->_val;
        }
        const T& back() const
        {
            return _pHead->_pPre->_val;
        }

        // List Modify
        void push_back(const T& val) 
        { 
            insert(end(), val);
        }
        void pop_back() 
        {
            erase(--end());
        }
        void push_front(const T& val)
        {
            insert(begin(), val);
        }
        void pop_front()
        {
            erase(begin());
        }
        // iterator insert (iterator position, const value_type& val);
        // 在pos位置前插入值为val的节点
        iterator insert(iterator pos, const T& val)
        {
            PNode newNode = new Node(val);
            PNode cur = pos._pNode;
            PNode pre = cur->_pPre;

            newNode->_pNext = cur;
            newNode->_pPre = pre;
            pre->_pNext = newNode;
            cur->_pPre = newNode;

            return iterator(newNode);
        }
        // iterator erase (iterator position);
        // 删除pos位置的节点,返回该节点的下一个位置
        iterator erase(iterator pos)
        {
            assert(!empty());

            PNode cur = pos._pNode;
            PNode pre = cur->_pPre;
            PNode next = cur->_pNext;

            pre->_pNext = next;
            next->_pPre = pre;

            delete cur;
            cur = nullptr;

            return iterator(next);
        }
        void clear()
        {
            while (begin() != end())
            {
                pop_back();
            }
        }
        // void swap (list& x);
        void swap(list<T>& l)
        {
            std::swap(_pHead, l._pHead);
        }
    private:
        void CreateHead()
        {
            _pHead = new Node();
            _pHead->_pNext = _pHead;
            _pHead->_pPre = _pHead;
        }
        PNode _pHead;
    };
}

4. vector和list的比较

vector

优点:

  1. 支持元素下标的随机访问

  2. 尾插尾删效率较高

  3. cpu高速缓存命中率高

    即当获得数组首元素的地址时,将首元素存入高速缓冲区,同时将首元素紧接着后面的一些元素也存入高速缓冲区。之后若要访问这些被存入高速缓冲区的元素时,便可直接从缓冲区访问,而不必从内存访问,速度更快。

缺点:

  1. 前面部分的插入删除数据效率低,需要挪动数据,时间复杂度O(N)
  2. 扩容耗费时间(大可能是异地扩容),还可能存在空间浪费

list

优点:

  1. 空间按需申请/释放,无需扩容
  2. 任意位置插入数据效率高,时间复杂度O(1)

缺点:

  1. 不支持元素的下标随机访问
  2. cpu高速缓存命中率低(底层数据存储物理空间非连续)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丨归凡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值