The difference and use of vector and list

To understand vector, list, deque. Let's take a look at the STL first.
STL is the abbreviation of Standard Template Library, and the Chinese name is Standard Template Library. Fundamentally, the STL is a collection of containers and algorithms. STL can be divided into six parts: containers, iterators, allocators, adapters, algorithms, and functors. Pointers are encapsulated as iterators, where vector and list are so-called containers.
When we implement linked lists, stacks, queues or arrays, we often write some repetitive or similar code, and we also need to consider various possible problems. The introduction of STL greatly improves the reusability of the code. When we implement these codes, we can flexibly apply them as long as we introduce header files.
The use of
continuous storage structure of vector: vector is an array of objects that can achieve dynamic growth. It supports efficient access to the array and delete and insert operations at the end of the array. It is relatively difficult to delete and insert in the middle and at the head, and a large amount of data needs to be moved. . The biggest difference between it and the array is that the vector does not need the programmer to consider the capacity issue. The library itself has realized the dynamic growth of the capacity, and the array needs the programmer to manually write the expansion function to expand the capacity.
Simulation Implementation of Vector

template <class T>
class Vector
{
public:
    typedef T* Iterator;
    typedef const T* Iterator;

    Vector()
        :_start(NULL)
        ,_finish(NULL)
        ,_endOfStorage(NULL)
    {}

    void template<class T>
    PushBack(const T& x)
    {
        Iterator end = End();
        Insert(end, x);
    }

    void Insert(Iterator& pos, const T& x)
    {
        size_t n = pos - _start;
        if (_finish == _endOfStorage)
        {
            size_t len = Capacity() == 0 ? 3 :    Capacity()*2;
            Expand(len);
        }

        pos = _start+n;
        for (Iterator end = End(); end != pos; --end)
        {
            *end = *(end-1);
        }

        *pos = x;
        ++_finish;
    }

    Iterator End()
    {
        return _finish;
    }

    Iterator Begin()
    {
        return _start;
    }


    void Resize(size_t n, const T& val = T())//用Resize扩容时需要初始化空间,并且可以缩小容量
    {
        if (n < Size())
        {
            _finish = _start+n;
        }
        else
        {
            Reserve(n);
            size_t len = n-Size();
            for (size_t i = 0; i < len; ++i)
            {
                PushBack(val);
            }
        }
    }

    void Reserve(size_t n)//不用初始化空间,直接增容
    {
        Expand(n);
    }

    inline size_t Size()
    {
        return _finish-_start;
    }

    inline size_t Capacity()
    {
        return _endOfStorage-_start;
    }

    void Expand(size_t n)
    {
        const size_t size = Size();
        const size_t capacity = Capacity();
        if (n > capacity)
        {
            T* tmp = new T[n];
            for (size_t i = 0; i < size; ++i)
            {
                tmp[i] = _start[i];
            }
            delete[] _start;

            _start = tmp;
            _finish = _start+size;
            _endOfStorage = _start+n;
        }
    }

    T& operator[](size_t pos)
    {
        assert(pos < Size());
        return _start[pos];
    }

    const T& operator[](size_t pos) const
    {
        assert(pos < Size());
        return _start[pos];
    }

protected:
    Iterator _start;  //指向第一个元素所在节点
    Iterator _finish;  //指向最后一个元素所在节点的下一个节点
    Iterator _endOfStorage; //可用内存空间的末尾节点
};

Use of list
Non-contiguous storage structure: list is a double-linked list structure that supports bidirectional traversal of the linked list. Each node includes three pieces of information: the element itself, the node that points to the previous element (prev), and the node that points to the next element (next). Therefore, the list can efficiently perform operations such as access, insertion and deletion to any position of the data element. The overhead is relatively high due to the maintenance of additional pointers.
The difference between vector and list
*The random access efficiency of vector is high, but the data needs to be moved during insertion and deletion (excluding the tail), which is not easy to operate.
*List access needs to traverse the entire linked list, and its random access efficiency is low. But it is more convenient to insert and delete data, just change the pointer.
*list is unidirectional, vector is bidirectional.
The iterator in *vector is invalid after use, and the iterator of list can continue to be used after use.
Mock implementation of List

template<class T>
class List
{
    typedef __ListNode<T> Node;
public:
    typedef __ListIterator<T, T&, T*> Iterator;
    typedef __ListIterator<T, const T&, const T*> ConstIterator;

    Iterator Begin()
    {
        return _head->_next;
    }

    Iterator End()
    {
        return _head;
    }


    ConstIterator Begin() const 
    {
        return _head->_next;
    }

    ConstIterator End() const
    {
        return _head;
    }

    List()
    {
        _head = new Node(T());
        _head->_next = _head;
        _head->_prev = _head;
    }

    // l2(l1)
    List(const List& l)
    {
        _head = new Node(T());
        _head->_next = _head;
        _head->_prev = _head;
        ConstIterator it = l.Begin();
        while (it != l.End())
        {
            PushBack(*it);
            ++it;
        }
    }

    ~List()
    {
        Clear();

        delete _head;
        _head = NULL;
    }

    void Clear()
    {
        Iterator it = Begin();
        while (it != End())
        {
            Node* del = it._node;
            ++it;
            delete del;
        }

        _head->_next = _head;
        _head->_prev = _head;
    }

    void PushBack(const T& x)
    {
        Insert(End(), x);
    }

    void PushFront(const T& x)
    {
        Insert(Begin(), x);
    }

    void PopBack()
    {
        Erase(--End());
    }

    void PopFront()
    {
        Erase(Begin());
    }

    void Insert(Iterator pos, const T& x)
    {
        Node* cur = pos._node;
        Node* prev = cur->_prev;
        Node* tmp = new Node(x);
        prev->_next = tmp;
        tmp->_prev = prev;
        tmp->_next = cur;
        cur->_prev = prev;
    }

        Iterator Erase(Iterator& pos)
    {
        assert(pos != End());

        Node* prev = (pos._node)->_prev;
        Node* next = (pos._node)->_next;
        prev->_next = next;
        next->_prev = prev;

        delete pos._node;

        pos._node = prev;

                return Iterator(next);
    }

protected:
    Node* _head;
};

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326518436&siteId=291194637