vector introduction and basic use

Posted by ggkfc on Tue, 28 Dec 2021 15:30:43 +0100

I vector introduction

vector document

vector is a sequence container that represents a variable size array.

Like arrays, vectors use continuous storage space to store elements. This means that you can use subscripts to access the elements of a vector, which is as efficient as an array. But unlike an array, its size can be changed dynamically, and its size will be automatically processed by the container.

Essentially, vector uses dynamically allocated arrays to store its elements. When new elements are inserted, the array needs to be resized to increase storage space. This is done by allocating a new array and then moving all the elements to the array. In terms of time, this is a relatively expensive task, because the vector does not reallocate the size every time a new element is added to the container.

Vector space allocation strategy: vector will allocate some additional space to accommodate possible growth, because the storage space is larger than the actual storage space. Different libraries use different strategies to balance the use and reallocation of space. However, in any case, the redistribution should be logarithmically increasing interval size, so that when inserting an element at the end, it is completed in constant time complexity.

Therefore, vector occupies more storage space. In order to obtain the ability to manage storage space and grow dynamically in an effective way.

Compared with other dynamic sequence containers (deques, lists and forward_lists), vector is more efficient in accessing elements, and adding and deleting elements at the end is relatively efficient. It is less efficient for other deletion and insertion operations not at the end. It is better than the unified iterator and reference of lists and forward_lists.

II vector usage

(1).constructor

(1). Construct an empty container without elements

vector<int> v1;

(2). Construct n containers with value val

vector<int> v2(10,2); // size is 10 and capacity is 10

(3). Use the elements in the [first,last) interval to construct the container

vector<int> v3(v2.begin(),v2.end());
string s = "hello world";
vector<char> v4(s.begin(),s.end());

(4). copy construction

vector<int> v5(v3);

(2).iterator

iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
reverse_iterator rend();
const_reverse_iterator rend() const;

#include<iostream>
#include<vector>
using namespace std;
void print(const vector<int>& v)
{
	// const_iterator
	vector<int>::const_iterator vit = v.begin();
	while (vit != v.end())
	{
		cout << *vit << " ";
		++vit;
	}
	cout << endl;
}
int main()
{
	vector<int> v1(10, 2);
	const vector<int> v2(10,5);
	
	vector<int>::iterator vit = v1.begin();
	while (vit != v1.end())
	{
		cout << *vit << " ";
		++vit;
	}
	cout << endl;

	print(v2);
	// reverse_iterator
	vector<int>::reverse_iterator vrit = v1.rbegin();
	while (vrit != v1.rend())
	{
		cout << *vrit << " ";
		++vrit;
	}
	cout << endl;
	// const_reverse_iterator
	vector<int>::const_reverse_iterator vrit2 = v2.rbegin();
	while (vrit2 != v2.rend())
	{
		cout << *vrit2 << " ";
		++vrit2;
	}
	cout << endl;
}

(3).capacity

(1). size_t size()const;

Returns the number of valid elements in the container

(2). size_t capacity()const;

Returns the capacity of the current container

#include<iostream>
#include<vector>
using namespace std;
int main()
{
	vector<int> v(10,2);
	cout<<v.size()<<endl; // Returns the number of valid data
	cout<<v.capacity()<<endl;// Number of returned capacity
	return 0;
}

(3).void resize (size_t n, T val = T());

1). When n is smaller than the current size of the container, the size is reduced to n
2). When n is larger than the current size of the container and smaller than the current capacity of the container, the size is expanded to N, and the value is filled with val
3). When n is greater than the current capacity of the container, expand the capacity first, then expand the size of the container by N, and fill the value with val

#include<iostream>
#include<vector>
using namespace std;
int main()
{
	// The test results under vs2019 may be different for different compilers
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5); // size = 5,capacity = 6

	v.resize(2);    // size = 2,capacity = 6
	v.resize(6, 2); // size = 6,capacity = 6
	v.resize(8,3);  // size = 8,capacity = 9
}

(4).void reserve(size_t n);

1). When n is greater than the current capacity of the container, expand the capacity to N or greater.
2). When n is less than the current capacity of the container, nothing is done.

#include<iostream>
#include<vector>
using namespace std;
int main()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5); // size = 5,capacity = 6
	v.reserve(10);  // size = 5,capacity = 10
	v.reserve(6);   // size = 5,capacity = 10
}

(5).bool empty()const;

Judge whether the current container is empty

#include <iostream>
#include <vector>
using namespace std;
int main()
{
	vector<int> v(10, 2);
	cout << v.empty() << endl;
	return 0;
}

(4).Element access

(1).

reference operator[] (size_t n);
const_reference operator[] (size_t n) const;
#include <iostream>
#include <vector>
using namespace std;
int main()
{
	vector<int> v(10, 1);
	//Use "subscript + []" to traverse the container
	for (size_t i = 0; i < v.size(); i++)
	{
		cout << v[i] << " ";
	}
	cout << endl;
	return 0;
}

(2).

reference at (size_type n);
const_reference at (size_type n) const;
#include <iostream>
#include <vector>
using namespace std;
int main()
{
	vector<int> v(10, 1);
	//Use "subscript + []" to traverse the container
	for (size_t i = 0; i < v.size(); i++)
	{
		cout << v.at(i) << " ";
	}
	cout << endl;
	return 0;
}

The difference between operator [] and at()

The difference between operator [] and at()
The difference between operator [] and at()

(5).Modifiers

(1).void push_back (const T& val);
(2).void pop_back();

Through push_ The back function tail inserts the container, pop_ The back function deletes the tail of the container.

#include <iostream>
#include <vector>
using namespace std;
int main()
{
	vector<int> v;
	v.push_back(1); //Trailing element 1
	v.push_back(2); //Trailing element 2
	v.push_back(3); //Trailing element 3
	v.push_back(4); //Trailing element 4

	v.pop_back(); //Tail deletion element
	v.pop_back(); //Tail deletion element
	v.pop_back(); //Tail deletion element
	v.pop_back(); //Tail deletion element
	return 0;
}

(3). insert and erase

iterator insert (iterator position, const value_type& val);
void insert (iterator position, size_type n, const value_type& val);
template <class InputIterator>
void insert (iterator position, InputIterator first, InputIterator last);
iterator erase (iterator position);
iterator erase (iterator first, iterator last);
#include <iostream>
#include <vector>
using namespace std;
int main()
{
	vector<int> v1;
	v1.insert(v1.begin(),1);
	v1.insert(v1.end(),5,2);
	print(v1);
	vector<int> v2;
	v2.insert(v2.begin(),v1.begin(),v1.end());
	print(v2);

	v2.erase(v2.begin());
	v2.erase(v2.begin(),v2.begin() + 2);
	print(v2);
}

(4). void swap (vector& x);

The swap function can exchange the data space of two containers to realize the exchange of two containers

#include <iostream>
#include <vector>
using namespace std;
int main()
{
	vector<int> v1(10,1);
	vector<int> v2(10,2);
	v1.swap(v2);
	print(v1);
	print(v2);
}

The above is the way to insert or delete elements by position. If you want to insert or delete elements by value (insert or delete elements at a specific value position), you need to use the find function.

find function:

template <class InputIterator, class T>
   InputIterator find (InputIterator first, InputIterator last, const T& val);

The find function has three parameters. The first two parameters determine an iterator interval (left closed and right open), and the third parameter determines the value to be searched.
The find function looks for the first matching element in the given iterator interval and returns its iterator. If it is not found, it returns the given second parameter.

#include <iostream>
#include <vector>
using namespace std;
int main()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	
	vector<int>::iterator pos = find(v.begin(),v.end(),3);
	if (pos != v.end())
	{
		v.insert(pos,30);
	}
	pos = find(v.begin(), v.end(), 3);
	v.erase(pos);
}

(5). void clear();

Make the size of the container 0

III vector iterator failure

The main function of the iterator is to enable the algorithm not to care about the underlying data structure. The underlying layer is actually a pointer or encapsulates the pointer. For example, the iterator of vector is the original ecological pointer T *. Therefore, the failure of the iterator is actually the destruction of the space pointed to by the corresponding pointer at the bottom of the iterator, and the use of a freed space will cause the program to crash (that is, if you continue to use the failed iterator, the program may crash).

Example 1: meaning change of iterator
(errors are reported in vs2019, but not in g + +, because different compilers handle different programs, but the programs are wrong)
The original intention was to delete 3, but since we inserted 30 and pos pointed to 30, the meaning of the iterator changed.
Solution: reposition the pos

#include <iostream>
#include <vector>
using namespace std;
int main()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);//  size = 5,capacity = 6
	
	vector<int>::iterator pos = find(v.begin(),v.end(),3);
	if (pos != v.end())
	{
		v.insert(pos,30);
	}
	print(v);
	// Solution
	//pos = find(v.begin(),v.end(),3);
	v.erase(pos);
	print(v);
}

Example 2: iterators become wild pointers
(both vs2019 and g + + report errors)

Before inserting, the container is full, and then inserting elements will increase the capacity (open a new space, copy data, and release the original space). After releasing the original space, the pos becomes a wild pointer

Solution: reposition the pos

#include <iostream>
#include <vector>
using namespace std;
int main()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.push_back(6); // size = 6,capacity = 6
	
	vector<int>::iterator pos = find(v.begin(),v.end(),3);
	if (pos != v.end())
	{
		 v.insert(pos,30);
	}
	print(v);
	// Solution
	// pos = find(v.begin(),v.end(),3);
	v.erase(pos);
}

Example 3: delete even numbers
(vs2019) no matter whether the last number is an odd number or an even number, the program will crash because the vit iterator has failed after erase(). The + + operation on the failed iterator will cause the program to crash. If the last number under g + + is an even number, it will crash because an out of bounds access is made during erase(), and the last number under g + + is an odd number and will not crash, But if there are consecutive even numbers, the program running result is still wrong)

Solution: using the return value of erase(), erase() returns the next valid location of the deleted element

#include <iostream>
#include <vector>
using namespace std;
int main()
{
	// Delete even
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.push_back(6);
	v.push_back(7);

	vector<int>::iterator vit = v.begin();
	while (vit != v.end())
	{
		if (*vit % 2 == 0)
			v.erase(vit);
		++vit;
	}
	
	// Solution
	vector<int>::iterator vit = v.begin();
	while (vit != v.end())
	{
		if (*vit % 2 == 0)
			vit = v.erase(vit);
		else
			++vit;
	}
}

Topics: C++ STL vector