C++list

Table of contents

1. Introduction and use of list     

1.1 Introduction to list

1.2 Use of list

1.2.1 Construction, destruction and assignment operator overloading

1.2.2 Iterators        

1.2.3 Capacity        

1.2.4 Element access:        

1.2.5 Modifiers

1.2.6 Operations        

1.2.7 Non-member functions      

2. In-depth analysis and simulation implementation of list        

2.1 In-depth analysis of list         

2.1.1 Iterator and const iterator templating

2.1.2 Reverse iterator        

2.2 Simulation implementation of list   

2.3 Comparison between list and vector 



1. Introduction and use of list     

1.1 Introduction to list

Original English document        

list - C++ Reference (cplusplus.com) icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/list/list/?kw=list Chinese introduction (the translation is not accurate, please understand it in conjunction with the document)

1. A list is a sequential container that can be inserted and deleted at any position within a constant range, and the container can be iterated in both directions. 2. The bottom layer of the list is a
doubly linked list structure. Each element in the doubly linked list is stored in an independent independent In the node, the pointer points to the previous element and the next element in the node (for details, please refer to other blogs)
3. List is very similar to forward_list: the main difference is that forward_list is a singly linked list and can only be unidirectional. Iteration is simpler and more efficient
4. Compared with other sequential containers (array, vector, deque), list usually has better execution efficiency for inserting and removing elements at any position. 5. Compared with other sequential
containers, list The biggest flaw with forward_list is that it does not support random access at any position. For example: to access the 6th element of the list, you must iterate from a known position (such as the head or tail) to that position. This process requires linear time overhead.
List of member types

1.2 Use of list

1.2.1 Construction, destruction and assignment operator overloading

Constructor introduction:

(1) empty container constructor (default constructor)        no parameter construction

Constructs an empty container, with no elements.

(2) fill constructor        fill structure

Constructs a container with n elements. Each element is a copy of val.

(3) range constructor        range construction

Constructs a container with as many elements as the range [first,last), with each element constructed from its corresponding element in that range, in the same order.

(4) copy constructor        copy constructor

Constructs a container with a copy of each of the elements in x, in the same order.

注意:所有测试均在  VS2022 下进行
// constructing lists
#include <iostream>
#include <list>

using namespace std;

int main()
{
    // constructors used in the same order as described above:
    list<int> lt1;                              // empty list of ints
    list<int> lt2(4, 100);                      // four ints with value 100
    list<int> lt3(lt2.begin(), lt2.end());      // iterating through second
    list<int> lt4(lt3);                         // a copy of third
    // list<int> lt4 = lt3;
    int array[] = { 16,9,7,49,55,248 };
    list<int> lt5(array, array + sizeof(array) / sizeof(int));

    cout << "The contents of lt5 are: ";
    list<int>::iterator it = lt5.begin();
    while (it != lt5.end())
    {
        cout << *it << ' ';
        it++;
    }
    cout << endl;
    return 0;
}

output:
The contents of lt5 are: 16 9 7 49 55 248

1.2.2 Iterators        

1. begin and end are forward iterators, perform ++ operations on the iterator, and the iterator moves backward
2. rbegin(end) and rend(begin) are reverse iterators, perform ++ operations on the iterator, and iterate To move the iterator forward,
you just need to learn to use these four iterator functions.        

        

1.2.3 Capacity        

Old friends empty and size don’t need to be introduced too much. 

               

1.2.4 Element access:        

int main()
{
    // list front
	list<int> list1;

	list1.push_back(13);
	list1.push_back(14);

	// now front equals 13, and back 14
	list1.front() -= list1.back();

	cout << "list1.front() is now " << list1.front() << endl;

	//
    // list back
	list<int> list2;

	list2.push_back(10);
	while (list2.back() != 0)
	{
		list2.push_back(list2.back() - 1);
	}

	cout << "list2 contains:";
	list<int>::iterator it = list2.begin();
	while (it != list2.end())
	{
		cout << ' ' << *it;
		++it;
	}
	cout << endl;

	return 0;
}

output:
list1.front() is now -1
list2 contains: 10 9 8 7 6 5 4 3 2 1 0

        

1.2.5 Modifiers

int main()
{
	list<int> lt1;
	lt1.push_back(1);
	lt1.push_back(2);
	lt1.push_back(3);
	lt1.push_front(4);
	lt1.push_front(5);
	lt1.push_front(6);
	list<int>::iterator it = lt1.begin();
	it++;
	lt1.insert(it, 10);
	lt1.pop_back();
	lt1.pop_front();
	it++;
	lt1.erase(it);

	for (auto e : lt1)
	{
		cout << e << " ";
	}
	cout << endl;

	return 0;
}

output:
10 5 1 2

Just pay attention to a few functions with relatively complex function interfaces. 

        

1.2.6 Operations        

int main()
{
	list<int> lt1;
	lt1.push_back(1);
	lt1.push_back(3);
	lt1.push_back(2);
	lt1.push_back(5);
	lt1.push_back(4);
	
	list<int> lt2;
	list<int>::iterator it2 = lt2.begin();
	lt2.splice(it2, lt1);	// 此时lt1已经为空了
	for (auto e : lt2)
	{
		cout << e << " ";
	}
	cout << endl;

	lt2.reverse();
	for (auto e : lt2)
	{
		cout << e << " ";
	}
	cout << endl;

	lt2.sort();
	for (auto e : lt2)
	{
		cout << e << " ";
	}
	cout << endl;

	lt2.remove(5);
	for (auto e : lt2)
	{
		cout << e << " ";
	}
	cout << endl;

	lt2.push_back(4);
	lt2.push_back(4);
	lt2.push_back(4);
	lt2.unique();
	for (auto e : lt2)
	{
		cout << e << " ";
	}
	cout << endl;

	list<int> lt3;
	lt3.push_back(9);
	lt3.push_back(8);
	lt3.push_back(7);
	lt3.push_back(6);
	lt2.sort(), lt3.sort();
	// 注意先排序再合并
	lt3.merge(lt2);
	// 此时lt2已经为空了
	for (auto e : lt3)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

output:
1 3 2 5 4
4 5 2 3 1
1 2 3 4 5
1 2 3 4
1 2 3 4
1 2 3 4 6 7 8 9

         

1.2.7 Non-member functions      

        


2. In-depth analysis and simulation implementation of list        

2.1 In-depth analysis of list         

2.1.1 Iterator and const iterator templating

    template<class T, class Ref, class Ptr>
    //普通正向迭代器 <T, T&, T*>
    //const正向迭代器 <T, const T&, const T*>
	struct __list_iterator
	{
		typedef list_node<T> Node;
		typedef __list_iterator<T, Ref, Ptr> itor;
		Node* _node;

		__list_iterator(Node* node)
			:_node(node)
		{}

		itor& operator++()
		{
			_node = _node->_next;
			return *this;
		}

		itor& operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		itor operator++(int)    // 前置++
		{
			itor tmp(*this);
			_node = _node->_next;

			return tmp;
		}

		itor operator--(int)    // 前置--
		{
			itor tmp(*this);
			_node = _node->_prev;

			return tmp;
		}

		Ref operator*()    // 引用
		{
			return _node->_data;
		}

		Ptr operator->()    // 指针
		{
			return &_node->_data;
		}

		bool operator==(const itor& it)
		{
			return _node == it._node;
		}

		bool operator!=(const itor& it)
		{
			return _node != it._node;
		}
	};

        

2.1.2 Reverse iterator        

template<class Iterator>
class ReverseListIterator
{
    // 注意:此处typename的作用是明确告诉编译器,Ref是Iterator类中的类型,而不是静态成员变量
    // 否则编译器编译时就不知道Ref是Iterator中的类型还是静态成员变量
    // 因为静态成员变量也是按照 类名::静态成员变量名 的方式访问的
public:
    typedef typename Iterator::Ref Ref;
    typedef typename Iterator::Ptr Ptr;
    typedef ReverseListIterator<Iterator> Self;
public:
//
// 构造
    ReverseListIterator(Iterator it)
        : _it(it)
    {}
//
// 具有指针类似行为
    Ref operator*()
    {
        Iterator temp(_it);
        --temp;
        return *temp;
    }

    Ptr operator->()
    { 
        return &(operator*());
    }
//
// 迭代器支持移动
    Self& operator++()
    {
        --_it;
        return *this;
    }

    Self operator++(int)
    {
        Self temp(*this);
        --_it;
        return temp;
    }

    Self& operator--()
    {
        ++_it;
        return *this;
    }

    Self operator--(int)
    {
        Self temp(*this);
        ++_it;
        return temp;
    }
//
// 迭代器支持比较
    bool operator!=(const Self& l)const
    { 
        return _it != l._it;
    }

    bool operator==(const Self& l)const
    { 
        return _it != l._it;
    }
    Iterator _it;
};

        

2.2 Simulation implementation of list   

namespace mylist
{
	template<class T>
	struct list_node
	{
		T _data;
		list_node<T>* _prev;
		list_node<T>* _next;

		list_node(const T& x = T())
			:_data(x)
			,_prev(nullptr)
			,_next(nullptr)
		{}
	};
    
    // 迭代器 与 const迭代器
	//template<class T>
	template<class T, class Ref, class Ptr>
	struct __list_iterator
	{
		typedef list_node<T> Node;
		typedef __list_iterator<T, Ref, Ptr> itor;
		Node* _node;

		__list_iterator(Node* node)
			:_node(node)
		{}

		itor& operator++()
		{
			_node = _node->_next;
			return *this;
		}

		itor& operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		itor operator++(int)    // 前置++
		{
			itor tmp(*this);
			_node = _node->_next;

			return tmp;
		}

		itor operator--(int)    // 前置--
		{
			itor tmp(*this);
			_node = _node->_prev;

			return tmp;
		}

		Ref operator*()
		{
			return _node->_data;
		}

		Ptr operator->()
		{
			return &_node->_data;
		}

		bool operator==(const itor& it)
		{
			return _node == it._node;
		}

		bool operator!=(const itor& it)
		{
			return _node != it._node;
		}
	};

	// 反向迭代器
	template<class Iterator, class Ref, class Ptr>
	struct Reverse_iterator
	{
		Iterator _it;
		typedef Reverse_iterator<Iterator, Ref, Ptr> Self;

		Reverse_iterator(Iterator it)
			:_it(it)
		{}

		Ref operator*()
		{
			Iterator tmp = _it;
			return *(--tmp);
		}

		Ptr operator->()
		{
			return &(operator*());
		}

		Self& operator++()
		{
			--_it;
			return *this;
		}

		Self& operator--()
		{
			++_it;
			return *this;
		}

		bool operator!=(const Self& s)
		{
			return _it != s._it;
		}
	};

	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; 
		typedef Reverse_iterator<iterator, T&, T*> reverse_iterator;
		typedef Reverse_iterator<const_iterator, const T&, const <T*>const_reverse_iterator;

		iterator begin()
		{
			return _head->_next;
		}

		iterator end()
		{
			return _head;
		}

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

		const_iterator end() const
		{
			return const_iterator(_head);
		}

		const_reverse_iterator rbegin() const
		{
			// list_node<int>*
			return const_reverse_iterator(end());
		}

		const_reverse_iterator rend() const
		{
			return const_reverse_iterator(begin());
		}

		reverse_iterator rbegin()
		{
			return reverse_iterator(end());
		}

		reverse_iterator rend()
		{
			return reverse_iterator(begin());
		}

		void empty_init()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;

			_size = 0;
		}

		list()
		{
			empty_init();
		}

		// list(list<T>& lt)
		list(const list<T>& lt)
		{
			empty_init();
			for (auto e : lt)
			{
				push_back(e);
			}
		}

		/*list<int>& operator=(const list<int>& lt)
		{
			if (this != &lt)
			{
				clear();
				for (auto e : lt)
				{
					push_back(e);
				}
			}
			return *this;
		}*/

		void swap(list<T>& lt)
		{
			std::swap(_head, lt._head);
			std::swap(_size, lt._size);
		}

		list<int>& operator=(list<int> lt)
		{
			swap(lt);
			return *this;
		}

		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
			_size = 0;
		}

		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}

		void push_back(const T& x)
		{
			insert(end(), x);
		}

		void push_front(const T& x)
		{
			insert(begin(), x);
		}

		void pop_back()
		{
			erase(--end());
		}

		void pop_front()
		{
			erase(begin());
		}

		T& front()
		{
			return _head->_next->_data;
		}

		const T& front()const
		{
			return _head->_next->_data;
		}

		T& back()
		{
			return _head->_prev->_data;
		}

		const T& back()const
		{
			return _head->_prev->_data;
		}

		size_t size() const
		{
			return _size;
		}

		bool empty() const
		{
			return _size == 0;
		}

		iterator insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;
			Node* newnode = new Node(x);
			Node* prev = cur->_prev;

			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;

			++_size;
			return iterator(newnode);
		}

		iterator erase(iterator pos)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;

			delete cur;
			prev->_next = next;
			next->_prev = prev;
			--_size;
			return iterator(next);
		}

	private:
		Node* _head;
		size_t _size;
	};
}

           

2.3 Comparison between list and vector 

                                              vector                                                         list

underlying structure Dynamic sequence list, a continuous space Bidirectional circular linked list with head node
random access Supports random access, and the efficiency of accessing a certain element is O(1) Random access is not supported, and
the efficiency of accessing an element is O(N)
Insertion and deletion Inserting and deleting at any position is inefficient and requires moving elements. The time complexity
is O(N). Capacity expansion may be required during insertion. Capacity expansion: open up new space
, copy elements, and release old space, resulting in lower efficiency.
Insertion and deletion at any position is highly efficient, no
elements need to be moved, and the time complexity is
O(1)
Space utilization The bottom layer is continuous space, which is not easy to cause memory fragmentation, has
high space utilization and high cache utilization.
The underlying nodes are dynamically opened, and small nodes are prone
to memory fragmentation, low space utilization, and
low cache utilization.
Iterator Original ecological pointer Encapsulate the original ecological pointer (node ​​pointer)
Iterator invalid When inserting elements, all iterators must be reassigned, because inserting
elements may cause re-expansion, causing the original iterators to become invalid.
When deleting, the current iterator needs to be reassigned, otherwise it will become invalid.
Inserting an element will not cause the iterator to become invalid.
When an element is deleted, it will only cause the current iterator
to become invalid. Other iterators will not be affected.
scenes to be used Need efficient storage, support random access, do not care about insertion and deletion efficiency Lots of insert and delete operations, don't care about random
access

        


        

Guess you like

Origin blog.csdn.net/weixin_72501602/article/details/132649853