[Reprinted] C++ containers and iterators

1. Sequential container vector
1.1 What is a container?

In C++, a container is defined as: In data storage, there is an object type that can hold other objects or pointers to other objects. This object type is called a container. Simply understand that a container is an object that holds other objects. Moreover, this "object" also has methods for dealing with "other objects".

The container was proposed with the birth of object-oriented languages, and it is even considered to be the foundation of early object-oriented languages. Now almost all object-oriented languages ​​are accompanied by a container, and C++ is the standard template library (STL).

C++ uses a template-based approach to process containers, and containers in STL provide a variety of data structures.

1.2 Advantages of containers
1) The container class is a good solution to the problem of specific code reuse.

2) It can be expanded by itself. When you don’t know how many objects need to be stored, you don’t know how much memory space should be opened up, and the container does not need to pre-set the space length, only need to create an object and reasonably call the methods it provides, and the rest of the details are done by itself. It applies for memory or releases memory by itself, and executes all commands using the optimal algorithm.

3) The container class automatically applies for and releases memory, so there is no need to perform new and delete operations.

1.3 Classification of general-purpose containers
STL divides general-purpose containers into three categories: sequential containers, associative containers, and container adapters.

1.3.1 Sequential
containers The elements of the vector, list, and deque sequential containers form a linear list with sequential relations, which is an orderable cluster of linear structure. Each element has its own fixed position. After adding or inserting an element, the position moves forward. The position has nothing to do with the element itself, but is related to the operation time and place. It saves the logical sequence of the element operation. For example, if multiple elements are added at once, the relative position of these elements in the container is consistent with the logical order when they are added, that is, the order in which they are added to the container.

vector: Vector
1) Any element can be accessed directly.

2) Linear sequence structure. You can specify a continuous space or not specify the size in advance. The space can be automatically expanded, or it can be operated like an array, that is, it supports the [] operator and vector.at(), so it can be regarded as a dynamic array, which is usually reflected in Append data push_back() and delete the end data pop_back().

3) When the allocated space is not enough, vector will apply for a larger memory block (increased by a multiple of 2), then copy the original data to the new memory block and destroy the objects in the original memory block, and finally release the original Memory space. Therefore, if the amount of data stored by the vector is large, it will consume performance, so the performance is optimal when its size is known in advance.

4) Save space. Because it is continuous storage, there is no waste in the area where the data is stored, but in fact most of the time the storage is not full, so the area that is not actually stored is wasted.

5) The operation efficiency of inserting and deleting internally is low. Since vector is designed with a sequential table structure inside, such operations are basically prohibited, and it is designed to only perform append and delete operations at the back end.

#include <vector>
//初始化
vector<int> vec //默认初始化,空
vector<int> vec2(vec); //使用vec初始化vec2
vector<int> vec3(3); //初始化3个值为0的元素
vector<int> vec4(4, 1); //初始化4个值为1的元素
vector<string> vec5(5,"null"); //初始化5个值为null的元素
vector<string> vec6(6,"hello"); //初始化6个值为hello的元素
 
//常用的操作方法
vec.push_back(3) //向末尾添加元素3
vec.end(); //返回指向末尾下一位置的迭代器
int size = vec.size() //一共的元素个数
bool isEmpty = vec.empty(); //判断是否为空
cout<<vec[0]<<endl; //取得第一个元素
vec.insert(vec.end(),5,3); //从末尾下一位置插入5个值为3的元素
vec.pop_back(); //删除末尾元素
vec.erase(vec.begin(),vec.end());//删除之间的元素,其他元素前移
cout<<(vec==vec2)?true:false; //判断是否相等==、!=、>=、<=...
vector<int>::iterator iter = vec.begin(); //获取迭代器首地址
vector<int>::const_iterator c_iter = vec.begin(); //获取const类型迭代器
vec.clear(); //清空元素
 
//遍历方法
//下标法(vector的特有访问方法,一般容器只能通过迭代器访问)
int length = vec1.size();
for(int i=0;i<length;i++)
{
    
    
   cout<<vec1[i];
}
cout<<endl<<endl;
//迭代器法
vector<int>::const_iterator iterator = vec1.begin();
for(; iterator != vec1.end(); iterator++)
{
    
    
   cout<<*iterator;
}
 
//赋值与swap
vector<string> vs1(3); // vs1有3个元素
vector<string> vs2(5); // vs2有5个元素
vs1.swap(vs2); //执行后,vs1中5个元素,而vs2则存3个元素

list: Double-linked list
1) Linear linked list structure.

2) Its data is composed of several nodes, and each node includes an information block (that is, the actual stored data), a predecessor pointer and a trailing pointer. There is no need to allocate a specified memory size and can be scaled arbitrarily, so it is stored in a non-contiguous memory space, and the ordered elements are linked by pointers. Therefore, it also occupies more memory than vector.

3) According to its structure, the performance of random retrieval is very poor. Vector is to find the address of the element directly, and it needs to be searched in order from the beginning, so it is very time-consuming to retrieve the later elements. That is, the [] operator and .at() are not supported.

4) Since each node of the list saves its position in the linked list, inserting or deleting an element only affects up to three elements, so it can quickly insert and delete operations at any node.

#include <list>
//初始化
list<int> lst1; //创建空list
list<int> lst2(3); //创建含有三个元素的list
list<int> lst3(3,2); //创建含有三个元素的值为2的list
list<int> lst4(lst2); //使用lst2初始化lst4
 
//常用的操作方法
lst1.assign(lst2.begin(),lst2.end()); //分配值
lst1.push_back(10); //添加值
lst1.pop_back(); //删除末尾值
lst1.begin(); //返回首值的迭代器
lst1.end(); //返回末尾位置下一位的迭代器
lst1.clear();//清空值
bool isEmpty1 = lst1.empty(); //判断为空
lst1.erase(lst1.begin(),lst1.end()); //删除元素
lst1.front(); //返回第一个元素的引用
lst1.back(); //返回最后一个元素的引用
lst1.insert(lst1.begin(),3,2); //从指定位置插入3个值为2的元素
lst1.rbegin(); //返回第一个元素的前向指针
lst1.remove(2); //相同的元素全部删除
lst1.reverse(); //反转
lst1.size(); //含有元素个数
lst1.sort(); //排序
lst1.unique(); //删除相邻重复元素
 
//遍历方法
//迭代器法
for(list<int>::const_iterator iter = lst1.begin();iter != lst1.end();iter++)
{
    
    
   cout<<*iter;
}
cout<<endl;
 
//赋值与swap
list<string> sl1,sl2;
for(int i=0;i<10;i++)sl2.push_back("a");
sl1.assign(10, "A"); //s1被重新赋值,拥有十个元素,都为A

deque: Double-ended queue
(https://blog.csdn.net/weixin_42462202/article/details/87537503)
1) It is an optimized basis for adding and deleting operations on both ends of the sequence, and faster random access Sequence container.

2) Use multiple consecutive storage blocks to store objects, and keep track of these blocks and their order in a mapping structure. Since there is no need to reallocate space, it is more efficient than vector when appending elements. In fact, there is a map pointer inside.

3) Support random access, that is, support [] operator and .at(), but the performance is not as good as vector.

4) Random insertion and deletion can be performed internally, but the performance is not as good as the list.
1.3.2 Associative container set, multiset, map, multimap
associative container is a binary tree structure, it is sorted according to the characteristics of the elements, the iterator can get the elements "sequentially" according to the characteristics of the elements. It saves data in the form of key-values, that is, associating keywords and values ​​to save, and the sequential container can only save one type (it can be considered that it only saves keywords, or it can only save values). Specifically, it uses a more efficient special balanced search binary tree-red-black tree structure (https://blog.csdn.net/asdfsadfasdfsa/article/details/86500552).

Collection set:

1) Quick search, no duplicate values ​​are allowed.

2) Arranged in a certain order, each element in the set is called an instance in the set.

3) The internal organization is organized by a linked list, so it is faster than vector when inserting, but it is slower than vector when searching and appending at the end.

set<int> mySet(arr,arr+n); //使用arr[0]到arr[n-1]初始化set
begin();            // 返回指向第一个元素的迭代器
end();              // 返回指向迭代器的最末尾处(即最后一个元素的下一个位置)
clear();            // 清除所有元素
count();            // 返回某个值元素的个数
 
empty();            // 如果集合为空,返回true
 
equal_range();      //返回集合中与给定值相等的上下限的两个迭代器
 
erase()–删除集合中的元素
 
find()–返回一个指向被查找到元素的迭代器
 
get_allocator()–返回集合的分配器
 
insert()–在集合中插入元素
 
lower_bound()–返回指向大于(或等于)某值的第一个元素的迭代器
 
key_comp()–返回一个用于元素间值比较的函数
 
max_size()–返回集合能容纳的元素的最大限值
 
rbegin()–返回指向集合中最后一个元素的反向迭代器
 
rend()–返回指向集合中第一个元素的反向迭代器
 
size()–集合中元素的数目
 
swap()–交换两个集合变量
 
upper_bound()–返回大于某个值元素的迭代器
 
value_comp()–返回一个用于比较元素间的值的函数

//遍历
for (std::set<int>::iterator it=myset.begin(); it!=myset.end(); ++it)
    std::cout << ' ' << *it;

map:

1) Provide a one-to-one data storage capability with a "key-value" relationship. The keys are arranged in a certain order and cannot be repeated (set can also be seen as a special map form without keys but values).

2) Linked list storage inherits the advantages and disadvantages of linked lists.

3) One-to-many mapping, quick search based on keywords.

 begin()          返回指向map头部的迭代器

   clear()          删除所有元素

   count()          返回指定元素出现的次数

    empty()          如果map为空则返回true

    end()          返回指向map末尾的迭代器

   equal_range()          返回特殊条目的迭代器对

    erase()          删除一个元素

   find()          按键查找一个元素,若不存在,则返回iter与end函数的值相同

   get_allocator()         返回map的配置器

    insert()          插入元素

   key_comp()          返回比较元素key的函数

   lower_bound()         返回键值>=给定元素的第一个位置

   max_size()          返回可以容纳的最大元素个数

   rbegin()          返回一个指向map尾部的逆向迭代器

    rend()          返回一个指向map头部的逆向迭代器

   size()          返回map中元素的个数

   swap()          交换两个map

    upper_bound()          返回键值>给定元素的第一个位置

   value_comp()          返回比较元素value的函数

multiset and multimap: the element is not required to be unique, the others are the same as above.

Features of associative containers:

1) The structural principle of the red-black tree.

2) Set and map ensure the uniqueness of elements, and mulset and mulmap extend this attribute to allow elements to be non-unique.

3) The elements are an ordered set, which are arranged in ascending order when inserted by default.

4) Insert and delete operations are faster than vector and slower than list. Because the vector is stored sequentially, and the associative container is stored in a chain; and the same is a chain structure, the list is linear, and the associative container is a sorted binary tree structure, so the elements need to be reordered each time, and the element changes involved are more many.

5) The retrieval operation of elements is slower than vector and much faster than list. Vector is sequential continuous storage, which is the fastest speed; while lists need to be searched one by one, the search time is proportional to the size of the container, and the complexity of associative container search is log(n). Therefore, the larger the container, the better the associative container relative to the list. Reflect its superiority.

6) The use of set is different from sequential containers in that although the query is slower than vector, it is stronger than list.

7) The function of map is irreplaceable in use. It saves the data of the "key-value" relationship, and this key-value relationship uses an array-like approach. Arrays use numeric subscripts to index the positions of elements, while map uses character keywords to index the positions of elements. In use, map also provides a way to manipulate arrays, that is, it can retrieve data by subscript. In STL, only vector and map can manipulate elements in an array-like manner.

1.3.3 Container adapter stack, queue, priority_queue
This is a relatively abstract concept. The C++ explanation is: an adapter is a mechanism that makes the behavior of one thing similar to the behavior of another thing. A container adapter is a mechanism that allows an existing container type to use another different abstract type of work to achieve a mechanism. In fact, only interface conversion has occurred. It can be understood as the container of the container, but it does not depend on the specific standard container type. It can be understood as the template of the container, or it can be understood as the interface of the container. Therefore, the adapter itself cannot save the element, but "it saves a container, and the container saves the element", so it implements the saving function by calling another container.

The three adapters provided in STL can be implemented by a sequential container. By default, stack and queue are implemented based on the deque container, and priority_queue is implemented based on the vector container. You can also specify a specific implementation container when you create it. However, due to the characteristics of adapters, not all sequential containers can implement these adapters.

Stack: Last in, first out. The associative container can be any kind of sequential container. Because sequential containers can provide stack operation requirements: push_back, pop_back, back.

Queue: first in, last out. Associative container must provide pop_front operation, so vector is not applicable.

Priority priority_queue: The highest priority element is always processed first. It needs to provide random access function, so list is not applicable.

Second, iterator iterator
iterator iterator
is to express the concept of the position of an element in the container, it is a data type that inspects the elements in the container and traverses the elements. C++ tends to use iterators instead of subscripts for operations, because the Standard Library (STL) defines an iterator type for each standard container, and only a few containers support subscript operations to access container elements.
Insert picture description here
Therefore, each container defines its own iterator type. Take vector as an example:

//定义和初始化
vector<int>::iterator iter; //定义一个名为iter的变量
 
vector<int> ivec;
vector<int>::iterator iter1=ivec.bengin(); //将迭代器iter1初始化为指向ivec容器的第一个元素
vector<int>::iterator iter2=ivec.end(); //将迭代器iter2初始化为指向ivec容器的最后一个元素的下一个位置
 
//常用操作
*iter //对iter进行解引用,返回迭代器iter指向的元素的引用
iter->men //对iter进行解引用,获取指定元素中名为men的成员。等效于(*iter).men
++iter //给iter加1,使其指向容器的下一个元素
iter++
--iter //给iter减1,使其指向容器的前一个元素
iter--
iter1==iter2 //比较两个迭代器是否相等,当它们指向同一个容器的同一个元素或者都指向同同一个容器的超出末端的下一个位置时,它们相等
iter1!=iter2
//用迭代器来遍历ivec容器,把其每个元素重置为0
for(vector<int>::iterator iter=ivec.begin();iter!=ivec.end();++iter)
    *iter=0;
 
//只有vector和queue容器提供迭代器算数运算和除!=和==之外的关系运算
iter+n //在迭代器上加(减)整数n,将产生指向容器中钱前面(后面)第n个元素的迭代器。新计算出来的迭代器必须指向容器中的元素或超出容器末端的下一个元素
iter-n
iter1+=iter2 //将iter1加上或减去iter2的运算结果赋给iter1。两个迭代器必须指向容器中的元素或超出容器末端的下一个元素
iter1-=iter2
iter1-iter2 //两个迭代器的减法,得出两个迭代器的距离。两个迭代器必须指向容器中的元素或超出容器末端的下一个元素
>,>=,<,<= //元素靠后的迭代器大于靠前的迭代器。两个迭代器必须指向容器中的元素或超出容器末端的下一个元素
vector<int>::iterator mid=v.begin()+v.size()/2; //初始化mid迭代器,使其指向v中最靠近正中间的元素

Iterator const_iterator
Each type of container also defines a type called const_iterator. This type of iterator can only perform read operations, not write operations.

for(vector<int>::const_iterator iter=ivec.begin();iter!=ivec.end();++iter)
     cout<<*iter<<endl;   //合法,读取容器中元素值
     *iter=0;             //不合法,不能进行写操作

And const iterator is different from const_iterator. The former must be initialized when the iterator is declared, and its value cannot be modified once it is initialized.

vector<int> ivec(10);
const vector<int>::iterator iter=ivec.begin();
*iter=0;    //合法,可以改变其指向的元素的值
++iter;    //不合法,无法改变其指向的位置

Since operations such as deleting or moving elements will modify the internal state of the container, it will invalidate the iterator that originally pointed to the moved element, and may also invalidate other iterators at the same time. An invalid iterator is meaningless, and using an invalid iterator can cause serious runtime errors, so be sure to pay special attention to which operations will cause the iterator to fail.

Guess you like

Origin blog.csdn.net/qq_43530773/article/details/114776805