The vectors and arrays described in the previous blog, which have a contiguous memory space and have a constant starting address, are good for random access, but because of the continuous space, the insertion and deletion in the middle causes the memory block to be copied and moved, In addition, when there is not enough memory space, you need to reapply a large memory for the memory copy. To overcome these flaws, the STL defines another container list, which has an O (1) time complexity for data insertion and deletion, and no more frequent copy transfers in memory. Below, together to see the list of the source code to achieve it. List Overview
The list and vector are all STL sequential containers, the only difference being that the vector is a contiguous memory space, and the list is a discrete memory space, and the list needs only to configure or release an element space each time it inserts and deletes, compared to the vector. , the list can always do constant time for insertions and deletions anywhere. However, because the list does not support random addressing because of the discontinuous memory space, the ruler has a short length, and the choice of the container in the program depends on the construction complexity and the access behavior of the element. the node of the list
The node structure of the list is as follows:
Template <class t>
struct __list_node
{
typedef void* void_pointer;
Void_pointer Next; Type is void*, can also be set as __list_node<t>*
void_pointer prev;
T data;
};
From the node structure, it can be seen that the list is a two-way list, its structure as shown in the following figure:
iterator for list
In Vector, because it is continuous storage space, support random access, so its iterator can be directly replaced by ordinary pointers. But it won't work in the list. The list must have the ability to point to the nodes of the list, and to be able to perform the proper increment, decrement, value, and member access operations.
The
List is a two-way list, and the iterator must have the ability to move forward and back, so the list iterator is a bidirectional iterator. If the iterator fails after inserting and deleting operations in the vector, the list has an important property that the insert and join operations do not cause the original list iterator to fail. Also, when you delete a node, only the iterator that points to the deleted element is invalidated, and the other iterators are unaffected. Let's look at the source of the list iterator.
Template<class T, Class Ref, class ptr> struct __list_iterator {typedef __list_iterator<t, T&, t*> iterator;
Support iterator_traits typedef __list_iterator<t, const T&, const t*> const_iterator;
typedef __list_iterator<t, REF, ptr> self; The following are some types of typedef bidirectional_iterator_tag Iterator_category defined to support iterator_traits;
The iterator type of list is bidirectional iterator typedef T VALUE_TYPE;
typedef PTR Pointer;
typedef REF Reference;
typedef size_t SIZE_TYPE;
typedef ptrdiff_t DIFFERENCE_TYPE;
typedef __list_node<t>* LINK_TYPE;
This is the resource pointer link_type node that the iterator actually manages; Iterator constructor __list_iterator (Link_type x): node (x) {} __list_iterator () {} __list_iterator (const iterator& x): N
Ode (X.node) {}//In the STL algorithm requires iterators to provide support for bool operator== (const self& x) Const {return node = = X.node;} BOOL Operator!= (const self& x) Const {REturn node!= x.node;
}//Overload operator *, returns the actual maintained data reference operator* () const {return (*node). Data}
Member invocation operator pointer operator-> () const {return & (operator* ());}
Prefix from self& operator++ () {node = (Link_type) ((*node). Next);
return *this;
}//suffix is added, it needs to produce a copy of itself first, then it will operate on itself, and finally return the copy self operator++ (int) {self tmp = *this;
++*this;
return TMP;
} self& operator--() {node = (Link_type) ((*node). prev);
return *this;
Self operator--(int) {self tmp = *this;
--*this;
return TMP; }
}
The iterator of list implements the operation of ==,!=,++,–, value and member invocation, because it is stored in the discontinuous memory space, so the p+n operation of vector is not supported. data structure of list
List of data structure of a list of node data structure is defined separately, SGI list is not only a two-way linked list, but also a circular two-way linked list, so it only needs a pointer, you can fully represent a linked list.
Template <class T, class Alloc = alloc>
class list
{
protected:
typedef void* Void_pointer;
typedef __list_node<t> List_node;
This provides STL standard allocator interface
typedef simple_alloc<list_node, alloc> list_node_allocator;
The head node of the linked list does not store the data
link_type node;
//.... The following is also a list of action functions
}
List Constructor
The list provides an empty constructor, as follows:
List () {empty_initialize ();}
For the establishment of an empty list
void Empty_initialize ()
{
node = Get_node ();
node->next = node; The front node points to itself
Node->prev = node; The back node points to itself
}
In addition, the list provides a constructor with a parameter that supports the following initialization operations:
List<int> myList (5,1); Initializes 5 1-linked lists, {1,1,1,1,1}
The source code of its constructor is as follows:
With parameter constructor
list (size_type N, const t& value) {fill_initialize (n, value);}
Creates a linked list
void Fill_initialize (size_type n, const t& value)
{
empty_initialize () with value of n nodes. First create an empty list insert
(begin (), n, value);//insert N Value of node
}
//insert n x at specified location
void Insert (iterator pos , int n, const t& x)
{
Insert (pos, (size_type) n, x);
}
Insert n
Template <class T, class alloc>
void List<t, Alloc>::insert (iterator) before position Position, Size_type N, const t& x)
{for
(; n > 0;--n)
Insert (position, x);
Okay, here's the real insert operation
//very simple bidirectional linked list insert operation
iterator Insert (iterator position, const t& x)
{
Link_type TMP = Create_node (x);
Tmp->next = Position.node;
Tmp->prev = position.node->prev;
(Link_type (position.node->prev))->next = tmp;
Position.node->prev = tmp;
return tmp;
}
The STL list provides a number of constructors, which I've listed here as an example. Other action functions for list Get_node
This function is used to configure a node.
Configures a node and returns
Link_type Get_node () {return
list_node_allocator::allocate ();
}
Put_node
This function is used to free a node.
Releases the specified node, without destructors, to the global destroy,
void Put_node (Link_type p) {
list_node_allocator::d eallocate (P);
}
Create_node
This function is used to configure and construct a node and initialize its value
Configures a node and initializes its value to x
link_type create_node (const t& x)
{
Link_type p = get_node ();
Construct (&p->data, x); global function return
p;
}
Destory_node
This function is used to deconstruct a node.
Destructors the node element and frees the memory
void Destroy_node (Link_type p)
{
destroy (&p->data); Global function
Put_node (p);
}
Insert
This function is used to insert a node in the development location (this function is mentioned above, repeat here, the main insertion of the list is given to this function), the function is an overloaded function, which has many forms.
Okay, here's the real insert operation
//very simple bidirectional linked list insert operation
iterator Insert (iterator position, const t& x)
{
Link_type tmp = Create_node (x);
Tmp->next = Position.node;
Tmp->prev = position.node->prev;
(Link_type (position.node->prev))->next = tmp;
Position.node->prev = tmp;
return tmp;
}
It also has the following multiple forms of overloaded functions
//inserting elements within [first,last] intervals
template <class T, class alloc> template <class inputiterator>
void List<t, Alloc>::insert (iterator position,
inputiterator-A, Inputiterator Last)
{
for (; I!= last; ++first)
Insert (position, *first);
}
Inserts an element at the position position, and the element calls the type default constructor
iterator insert (iterator position) {return insert (position, T ());
push_back
At the end of the insert element, with the above insert function, Push_back is easier to implement.
Insert node
void push_back (const t& x) {Insert (end (), X) on the linked list
Pop_front
Inserts a node
void Push_front (const t& x) {insert (Begin (), X) at the front end of the list;
Earse
Remove the element that the iterator refers to
Erase
The operation of the specified node iterator erase (iterator position)
{
//bidirectional linked list removing nodes
link_type next_node = Link_type ( Position.node->next);
Link_type Prev_node = Link_type (Position.node->prev);
Prev_node->next = Next_node;
Next_node->prev = Prev_node;
Destroy_node (Position.node);
Return iterator (Next_node);
}
The above function also has an overloaded version that removes all nodes in the interval/
/erase node
template <class T, class alloc>
list<t, Alloc>::iterator list<t, Alloc>::erase (iterator-I, iterator last) {while (before-
!= last) Erase ( first++);
return to last;
}
Pop_front
Remove the head node elements, with the above erase function, it is very convenient to implement.
Deletes the first node void Pop_front () {erase () of the linked list (
begin ());}
Pop_back
Remove the last element in a linked list
Deletes the last node of the linked list
void Pop_back ()
{
iterator tmp = end ();
Erase (--TMP);
}
Clear
Clears all nodes in a linked list, that is, a single purge
Destroy all nodes, place the linked list empty
template <class T, class alloc>
void List<t, Alloc>::clear ()
{
link_ Type cur = (link_type) node->next;
while (cur!= node) {//Traverse each node
link_type tmp = cur;
Cur = (link_type) cur->next;
Destroy_node (TMP);
}
Node->next = node;//after removal Note to keep the linked list is a cyclic list
Node->prev = node;
Remove
Remove a node with value in the list
Removes all nodes
//Time complexity O (n)
template <class T, class alloc>
void List<t, Alloc>::remove (const t&) for a specific value Amp Value)
{
iterator-i = begin ();
Iterator last = end ();
while (the!= last) {//guarantee that the list is not empty
iterator next = A;
++next;
if (*first = = value) erase (a); Erase the node
, the next;
Transfer
Migrates elements in a contiguous range to a specified location. (non-public interface)
void transfer (iterator position, iterator a, iterator last)
{
if (position!=)
{
(*) (LINK_ Type ((*last.node). prev)). Next = Position.node;
(* (Link_type (*first.node). prev)). Next = Last.node;
(* (Link_type (*position.node). prev)). Next = First.node;
Link_type tmp = Link_type ((*position.node). prev);
(*position.node). Prev = (*last.node). prev;
(*last.node). Prev = (*first.node). prev;
(*first.node). prev = tmp;
}
}
Here, borrow Mr. Houtie's "STL Source Analysis" in a picture to illustrate this process.
Splice
The join function provided by list is splice, and the above transfer is a non-public function. There are several versions of the splice function:
Move the list x before position
void splice (iterator position, list& x)
{
if (!x.empty ())
Transfer (position , X.begin (), X.end ()); Just call the transfer function
}
//Move the I-pointed content in the list to position before
void splice (iterator position, list& iterator i)
{
iterator j = i;
++j;
if (position = i | | | position = = j) return;
Transfer (position, I, j);
}
Move [First, last} elements before position
void splice (iterator position, List&, iterator first, iterator last)
{ C17/>if (!= last)
transfer (position, I, last);
}
Merge
This function is used to merge two lists, where the two lists must be well sequenced.
Suppose the current container and X are ordered to ensure that the two containers are merged and still ordered
template <class T, class alloc>
void List<t, Alloc>::merge (List<t, alloc>& x)
{
iterator first1 = begin ();
Iterator Last1 = end ();
Iterator First2 = X.begin ();
Iterator last2 = X.end ();
while (First1!= last1 && first2!= last2)
if (*first2 < *first1) {
iterator next = first2;
Transfer (First1, First2, ++next); After migrating the FIRST2 node to first1
first2 = next;
else
++first1;
if (first2!= last2) transfer (Last1, First2, last2); If the first2 still has the remaining, direct join again linked List 1 tail
}
Reverse
This function is used to reverse the linked list, which is specifically implemented as follows:
Invert the list to
template <class T, class alloc>
void List<t, Alloc>::reverse ()
{
if (node-> Next = node | | Link_type (node->next)->next = = node) return;
Iterator-A-begin ();
++first;
while (the!= end ()) {
iterator old = i; Remove a node
++first;
Transfer (begin (), old, and a); Insert after begin ()}
Sort
This function sorts the list in ascending order, which is implemented as follows:
Sorted in ascending order
template <class T, class alloc>
void List<t, Alloc>::sort ()
{
if (Node->next = = Node | | Link_type (node->next)->next = = node) return;
List<t, alloc> carry;
List<t, alloc> counter[64];
int fill = 0;
while (!empty ()) {
//takes a node from the list
Carry.splice (Carry.begin (), *this, Begin ());
int i = 0;
Merge the new elements in the carry with the results in counter while
(I < fill &&!counter[i].empty ()) {
Counter[i].merge ( carry);
Carry.swap (counter[i++]);
The merged results are deposited in the counter[i]
Carry.swap (counter[i]);
has reached 2*fill,fill 1
if (i = = fill) ++fill;
}
Merges all elements in the counter for
(int i = 1; i < fill; ++i) Counter[i].merge (counter[i-1));
Swap the counter linked list and this list for swaps
(Counter[fill-1]);
Exchange this list and list x
void swap (list<t, alloc>& x) {
Swap (node, x.node);
}
Here is an example to illustrate this process: (To list 5,3,6,4,7,9,1,2,8)
Carry each time you take a number from an array and then merge it into the counter array, the algorithm can only sort 2 of 64 of the number of times.
"STL Source Analysis" Written here is a quick sort, in fact I think should be merge sort. PostScript
List chain provides a lot of operation functions, read the source feel that they have reviewed the data structure of the commonly used linked list operations, but also mastered the STL linked list of commonly used functions. Benefit.. In addition, sort does not know is Mr. Houtie's clerical error or what, how does not look like is the quick sort. Finally, we have any doubts can be the bottom of the message.
Reference: Mr. Houtie's "STL Source Analysis" C + + STL Source Analysis