[ten thousand words summary] detailed explanation of common algorithms in C++STL (wrong year series)

Posted by colossus_09 on Mon, 29 Nov 2021 15:13:03 +0100

Write in front

  • This little rookie is a sophomore who loves programming. At present, he mainly focuses on learning C + +, data structure and algorithm.
  • I have only started learning algorithms in the last two months (who knows what I did in my freshman year). I have been tortured by algorithms (it's too difficult...). I have brushed more than 200 questions in Li Kou in the past two months, but I can still feel some progress. Continue to maintain it and strive to brush 500 questions at the end of the year! This is my force button Homepage - > My force buckle home page
  • Back to the point, I have summarized the commonly used C + + containers, built-in function objects (imitation functions) and this commonly used built-in algorithms in the current C + + column blog. They are all the contents of C++STL, because this part is not difficult and there are too many contents to remember (that is, I need to see more and use more), so I summarized them first. After that, I will summarize some basic C + + knowledge and C + + object-oriented content, as well as some practical analysis and summary of C + + projects.
  • Welcome friends to pay attention to me and exchange private letters with me to discuss some things about C/C + + and algorithms. Answer everything you know!

The following is the main body. If there is a clerical error, please criticize and point it out.

STL common algorithms

The built-in algorithms in C++STL are mainly in header files < algorithm >, < functional >, < numeric >

  • < algorithm > is the largest of all STL header files. It is also the most commonly used header file with the most algorithms, including algorithms such as comparison, exchange, search, traversal, copy and modification
  • < numeric > is very small and only includes a few template functions that perform simple mathematical operations on the sequence
  • < functional > defines some template classes to declare function objects (imitation functions)

Note: except that the last two arithmetic generation algorithms are in the header file #include < numeric >, other algorithms are in the header file #include < algorithm >

Some commonly used built-in algorithms will be introduced in detail below, and some simple examples will be used to help you understand how to use them.

Max / min

Let's first look at two very simple and convenient algorithms

  • max(a,b) returns the larger number in a,b
  • min(a,b) returns the smaller of a,b

be careful:

  • When using these two functions, variables with the same name (max, min) cannot be used, such as max = max(a, b), min = min(min, b), etc.
  • These two algorithms can only compare the size of two numbers. If you want to compare the size of more than two numbers, you can nest them. For example, find the maximum value of three numbers, m = max(a, max(b, c))

Example:

#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
	int arr[10] = { 5,8,2,4,6,9,7,0,1,3 };
	int m1=arr[0];
	int m2 = arr[0];
	for (int i = 0; i < 10; ++i) {
		m1 = max(m1, arr[i]);
		m2 = min(m2, arr[i]);
	}
	cout << "The maximum value is: " << m1 
		 << "\n The minimum value is: " << m2 << endl;
}

The output is as follows:

Maximum: 9
Minimum value: 0

Header files are basically those in the example. In the following examples, if there is no special header file to include, this part is omitted

Common traversal algorithms

for_each

Function: traverse the container for some custom operation.
This custom operation is a function written by yourself. You decide what you want to do.

Function prototype:
for_each(iterator beg,iterator end,_func); Traversal container

  • The first parameter beg can be passed into the starting iterator of the container or the address of the first element in the array
  • The second parameter end can be passed into the end iterator of the container or the address of the next position of the last element of the array
  • Last parameter_ func can pass in a function or function object

It must be emphasized that for an array, the address of the first element in the array is equivalent to the start iterator, and the address of the next position of the last element in the array is equivalent to the end iterator. In subsequent examples, the parameter part (beg,end) of the container iterator is sometimes passed in the address of the array. Don't be surprised. This is the application of this aspect.

transform

Function: move elements in a container (or array) to another container (or array) according to some custom rules.

Function prototype:
transform(iterator beg1,iterator end1,iterator beg2,_func);

  • beg1 passes in the starting iterator of the original container or the first address of the array
  • end1 passes in the end iterator of the original container or the address of the next position of the last element of the array
  • beg2 passes in the starting iterator of the target container or the first address of the target array
  • _ func passes in a handling rule function or function object

Here is a simple example to practice the use of these two traversal algorithms:

void printArr(int val)//Custom actions: printing
{
	cout << val << " ";
}
int targetArr(int val)//Handling rule function, add 100 to the original data
{
	return val + 100;
}
int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };//Original array
	int target[15];//Target array. The length of the target array must not be less than the original array
	transform(arr, arr + 10, target, targetArr);
	cout << "Print original array:";
	for_each(arr, arr + 10, printArr);
	cout << "\n Target array print:";
	for_each(target, target + 10, printArr);
}

The output is as follows:

Original array printing: 0 1 2 3 4 5 6 7 8 9
Target array printing: 100 101 102 103 104 105 106 107 108 109

Common search algorithms

Introduction:

  • Find find element
  • find_if find elements by criteria
  • adjacent_find finds adjacent duplicate elements
  • binary_find binary search method to find elements
  • count counts the number of elements
  • count_if counts the number of elements by condition

find

Function: find elements by value

Function prototype:

find(iterator beg,iterator end,value);

  • beg start iterator

  • End end iterator

  • value is the element to be searched. If it is a user-defined data type, such as an object of a class or an element of a structure type, the = = operator needs to be overloaded when searching

  • The iterator that returns the specified location was found, and the return end iterator was not found

Here are two examples to practice the use of find

For the use of built-in data types:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
	vector<int> v;
	for (int i = 0; i < 10; ++i) {
		v.push_back(i);
	}
	auto it = find(v.begin(), v.end(), 5);//Find element 5
	if (it != v.end()) {
		cout << "Find element" << *it << endl;
	}
	else {
		cout << "Element not found";
	}
}

The output is as follows:

Element 5 found

For the use of custom data types:

class Person
{
public:
	string m_name;
	int m_age;
	Person(string name,int age):m_name(name),m_age(age){}
	//Overload = = operator, otherwise find does not know what rules to compare
	bool operator==(const Person& p)
	{
		if (m_name == p.m_name && m_age == p.m_age) {
			return true;
		}
		return false;
	}
};
int main()
{
	vector<Person> v;
	v.push_back(Person("Zhang San", 20));
	v.push_back(Person("Li Si", 25));
	v.push_back(Person("Wang Wu", 30));
	v.push_back(Person("Zhao Liu", 40));

	Person p("Wang Wu",30);

	auto it = find(v.begin(), v.end(), p);//Find element 5
	if (it != v.end()) {
		cout << "Find this person:" << it->m_name << it->m_age << endl;
	}
	else {
		cout << "This person was not found";
	}
}

Enter as follows:

Find this person: Wang wu30

find_if

Function: find elements according to specified rules

Function prototype:

find_if(iterator beg,iterator end,_Pred);

  • beg start iterator
  • End end iterator
  • _ Pred is called a predicate (a function or imitation function whose return value is bool type is called a predicate), which is used here as a search rule. The concept of predicate is very important and will be seen frequently later
  • The iterator that returns the specified location was found, and the return end iterator was not found

Example:

bool greaterFive(int val)//Find rules
{
	return val > 5;
}
int main()
{
	vector<int> v;
	for (int i = 0; i < 10; ++i) {
		v.push_back(i);
	}
	auto it = find_if(v.begin(), v.end(), greaterFive);
	if (it != v.end()) {
		cout << "Found elements greater than 5:" << *it << endl;
	}
	else {
		cout << "not found" << endl;
	}
}

The output is as follows:

Found element greater than 5: 6

adjacent_find

Function: find adjacent and repeated elements

Function prototype:

adjacent_find(iterator beg,iterator end);

  • beg start iterator
  • End end iterator
  • The iterator that returns the position of the first element of the adjacent element was found, and the iterator that returns the end of the container was not found

Example:

int main()
{
	vector<int> v;
	v.push_back(2);
	v.push_back(5);
	v.push_back(2);
	v.push_back(3);
	v.push_back(3);
	auto it = adjacent_find(v.begin(), v.end());
	if (it != v.end()) {
		cout << "Adjacent repeating elements are:" << *it << endl;
	}
	else {
		cout << "There are no adjacent duplicate elements" << endl;
	}
}

The output is as follows:

Adjacent repeating elements are: 3

binary_find

Function: find the specified element value and return true; otherwise, return false

characteristic:

  • The search speed is much faster than the general search algorithm, because the time complexity of the general search algorithm is O(n), while the time complexity of the binary search algorithm is O(log(n))
  • However, binary search has its limitations. It can not be used to find unordered sequences, that is, it can only use binary search to find the elements specified in ascending or descending sorting sequences

Function prototype:

bool binary_search(iterator beg,iterator end,value);

  • beg start iterator
  • End end iterator
  • It can only be used in ordered sequences. If it is used in unordered sequences, the result returned by the function will be inaccurate

Example:

int main()
{
	vector<int> v;
	for (int i = 0; i < 10; ++i) {
		v.push_back(i);
	}
	bool flag = binary_search(v.begin(), v.end(), 5);
	if (flag == 1) {
		cout << "The specified element was found" << endl;
	}
	else {
		cout << "not found" << endl;
	}
}

The output is as follows:

The specified element was found

count

Function: count the occurrence times of the same element

Function prototype:
count(iterator beg,iterator end,value);

  • Count the number of element value
  • beg start iterator
  • End end iterator

Example:

Let's focus on the use of count in custom data types

class Person
{
public:
	string m_name;
	int m_age;
	Person(string name,int age):m_name(name),m_age(age) {}
	bool operator==(const Person& p)//Overload = =, as rule of statistical element
	{
		if (m_age == p.m_age) {
			return true;//Elements with the same statistical age
		}
		return false;
	}
};
int main()
{
	vector<Person> v;
	v.push_back(Person("Zhang San", 30));
	v.push_back(Person("Li Si", 25));
	v.push_back(Person("Wang Wu", 35));
	v.push_back(Person("Zhao Liu", 25));
	v.push_back(Person("Xiong Da", 25));
	v.push_back(Person("Xiong er", 20));

	Person p("Bald head strength", 25);
	int num = count(v.begin(), v.end(), p);
	cout << "Number of organisms aged 25:" << num << endl;
}

The output is as follows:

Number of organisms aged 25: 3

count_if

Function: count the number of elements according to specified conditions

Function prototype:

count_if(iteraotr beg,iterator end,_Pred);

  • beg start iterator
  • End end iterator
  • _ Pred predicate, used here as a condition for statistics

Example:

bool greater5(int val)
{
	return val > 5;
}
int main()
{
	vector<int> v;
	for (int i = 0; i < 10; ++i) {
		v.push_back(i);
	}
	int num = count_if(v.begin(), v.end(), greater5);
	cout << "The number of elements greater than 5 is:" << num << endl;
}

The output is as follows:

The number of elements greater than 5 is 4

Common sorting algorithms

Introduction:

  • sort sorts the elements in the container
  • random_shuffle randomly adjusts the order of elements within the specified range
  • merge container elements are merged and stored in another container
  • reverse reverses the elements of the specified range

sort

Function: sort the elements in the container or array

characteristic:

  • sort is the most commonly used algorithm in C++STL. There is no one!
  • Sort sort is the fastest sort, with a time complexity of O(n*log(n)), because it is mainly realized by quick sort + insert sort + heap sort.
  • When the amount of sequence data is large, the fast sorting algorithm is used to merge and sort by segments. Once the amount of segmented data is less than a certain threshold (16), insert sorting is used to avoid excessive additional load caused by recursive calls of fast scheduling. If the recursion level is too deep, heap sorting will be used instead.

Function prototype:

sort(iterator beg,iterator end,_Pred);

  • By default, the elements in the container are sorted in ascending order (from small to large)
  • beg start iterator
  • End end iterator
  • _ For Pred predicate, this parameter can not be filled in (ascending sort by default). If you want to sort according to other rules, such as descending sort, this parameter must be filled in
  • This algorithm can only be used by random access iterators, such as ordinary arrays, vector containers, deque containers, and so on

The following example will put the previous algorithm for_each, transform, and sort are used together

void myPrint(int val)
{
	cout << val << " ";
}
int targetVector(int val)
{
	return val;
}
bool myComepare(int v1,int v2)
{
	return v1 > v2;
}
int main()
{
	int a[10] = { 1,4,7,2,5,8,3,6,9 };//Out of order array
	vector<int> v(10);
	transform(a, a + 10, v.begin(), targetVector);

	cout << "Print array before sorting a: ";
	for_each(a, a + 10, myPrint);
	cout << "\n Print container before sorting v: ";
	for_each(v.begin(), v.end(), myPrint);

	sort(a, a + 10);//Default ascending sort
	cout << "\n Print array after ascending sort a: ";
	for_each(a, a + 10, myPrint);

	sort(v.begin(), v.end(), myComepare);//Descending sort
	cout << "\n Print containers after descending sort v: ";
	for_each(v.begin(), v.end(), myPrint);
}

The output is as follows:

Print array before sorting a: 1 4 7 2 5 8 3 6 9 0
Print container before sorting v: 1 4 7 2 5 8 3 6 9 0
Print array after ascending sort a: 0 1 2 3 4 5 6 7 8 9
Print containers after descending sort v: 9 8 7 6 5 4 3 2 1 0

random_shuffle

Function: randomly adjust the order of elements within the specified range

Function prototype:

random_shuffle(iterator beg,iterator end);

  • beg start iterator
  • End end iterator
  • The elements in the specified range [beg,end) are randomly scrambled

Note: before using random_shuffle, you'd better sow the random number seed srand((unsigned)time(NULL)), otherwise the random result will be the same every time. The srand function is in the header file #include < CTime >.

Example:

void myPrint(int val)
{
	cout << val << " ";
}
int main()
{
	int a[10] = { 0,1,2,3,4,5,6,7,8,9 };
	srand((unsigned)time(NULL));//Random number seed
	random_shuffle(a, a + 10);
	cout << "After random disruption:";
	for_each(a, a + 10, myPrint);
}

Print as follows (the results printed by each person may be different because they are random):

After random disruption: 0 5 4 6 7 1 8 3 9 2

merge

Function: merge two ordered containers into another target container. After merging, the target container is still ordered

Function prototype:

merge(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator target_beg);

  • beg1 first container or array start iterator
  • end1 first container or array end iterator
  • beg2 first container or array start iterator
  • end2 first container or array end iterator
  • target_beg target container or array start iterator

be careful:

  • Two containers must be in order and in the same order, not in ascending and descending order
  • The size of the target container must be specified in advance, and its size is not less than the sum of the sizes of the merged two containers

Example:

void myPrint(int val)
{
	cout << val << " ";
}
int main()
{
	vector<int> v;
	int a[10] = { 0,1,2,3,4,5,6,7,8,9 };
	
	for (int i = 0; i < 10; ++i) {
		v.push_back(i + 5);
	}
	vector<int> target(10 + v.size());
	cout << "Print a: ";
	for_each(a,a+10, myPrint);
	cout << "\n Print print v2: ";
	for_each(v.begin(), v.end(), myPrint);

	merge(a, a + 10, v.begin(), v.end(), target.begin());
	cout << "\n Print merged container target: ";
	for_each(target.begin(), target.end(), myPrint);
}

Print as follows:

Print a: 0 1 2 3 4 5 6 7 8 9
Print v2: 5 6 7 8 9 10 11 12 13 14
Print merged container target: 0 1 2 3 4 5 6 6 7 8 9 10 11 12 13 14

reverse

Function: invert the elements in the container

Function prototype:

reverse(iterator beg,iterator end);

  • beg start iterator
  • End end iterator

Example:

int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	reverse(arr, arr + 10);
	cout << "Output after inversion:";
	for (int i = 0; i < 10; ++i) {
		cout << arr[i] << " ";
	}
}

The output is as follows:

Output after inversion: 9 8 7 6 5 4 3 2 1 0

Common replacement algorithms

Introduction:

  • swap exchanges elements of two containers, or just two elements
  • replace replaces the old element of the specified range in the container with a new element
  • replace_if the element is replaced by the condition, and the element that meets the condition is replaced by the specified element

swap

Function: exchange elements in two containers, or only two elements

Function prototype:

swap(container c1,container c2);

  • c1 container 1 or element 1
  • c2 container 2 or element 2

Note: c1 and c2 must be containers or elements of the same type. Otherwise, the compiler will report an error.

Example:

void myPrint(int val)
{
	cout << val << " ";
}
int main()
{
	int a=1, b=2;
	vector<int> v1,v2;
	for (int i = 0; i < 10; ++i) {
		v1.push_back(i);     //v1: 0,1,2,3,4,5,6,7,8,9
		v2.push_back(9 - i); //v2: 9,8,7,6,5,4,3,2,1,0
	}
	swap(a, b);
	cout << "a = " << a << ", b = " << b << endl;
	swap(v1, v2);
	cout << "output v1: ";
	for_each(v1.begin(), v1.end(), myPrint);
	cout << "\n output v2: ";
	for_each(v2.begin(), v2.end(), myPrint);
}

The output is as follows:

a = 2, b = 1
Output v1: 9 8 7 6 5 4 3 2 1 0
Output v2: 0 1 2 3 4 5 6 7 8 9

replace

Function: replace the old element of the specified range in the container with a new element

Function prototype:

replace(iterator beg,iterator end,oldvalue,newvalue);

  • beg start iterator
  • End end iterator
  • Replace all the elements oldvalue in the container [beg,end) with newvalue

Example:

int main()
{
	int arr[10] = { 6,3,1,4,8,6,6,7,6,5 };
	replace(arr + 1, arr + 10, 6, 60);
	cout << "After replacement:";
	for (int i = 0; i < 10; ++i) {
		cout << arr[i] << " ";
	}
}

Enter as follows:

After replacement: 6 3 1 4 8 60 60 7 60 5

replace_if

Function: replace elements according to conditions, and replace qualified elements with specified elements

Function prototype:

replace_if(iterator beg,iterator end,_Pred,newvalue);

  • beg start iterator
  • End end iterator
  • _Pred is a predicate, replacing the element satisfying the predicate condition with newvalue

Example:

bool lessFive(int val)//Predicate if the number is less than 5
{
	return val < 5;
}
int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	replace_if(arr, arr + 10, lessFive, 0);
	cout << "After replacement:";
	for (int i = 0; i < 10; ++i) {
		cout << arr[i] << " ";
	}
}

The output is as follows:

After replacement: 0 0 0 0 5 6 7 8 9

Common set algorithm

Introduction:

  • set_intersection finding the intersection of two containers
  • set_union finding the union of two containers
  • set_difference finding the difference set of two containers

set_intersection

Function: find the intersection of elements in two containers and pass the intersection to another container

Function prototype:

set_intersection(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator target_beg)

  • beg1 first container start iterator
  • end1 first container end iterator
  • beg2 first container start iterator
  • end2 first container end iterator
  • target_beg the starting iterator of the target container
  • The function returns the iterator at the next address of the last element of the target container

Note: the two containers for intersection must be ordered and in the same order (ascending or descending), otherwise an error will be reported.

Example:

int main()
{
	vector<int> v1, v2;
	for (int i = 0; i < 10; ++i) {
		v1.push_back(i);//v1: 0,1,2,3,4,5,6,7,8,9
		v2.push_back(i + 5);      //v2: 5,6,7,8,9,10,11,12,13,14
	}
	vector<int> target;
	//Set target container size
	target.resize(min(v1.size(), v2.size()));

	auto target_end = set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), target.begin());
	cout << "Print the destination container where the intersection is stored:";
	for (auto it = target.begin(); it != target_end; ++it) {
		cout << *it << " ";
	}
}

The output is as follows:

Print destination container for storing intersection: 5 6 7 8 9

set_ union

Function: find the union of elements in two containers and pass the union to another container

Function prototype:

set_union(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator target_beg)

  • beg1 first container start iterator
  • end1 first container end iterator
  • beg2 first container start iterator
  • end2 first container end iterator
  • target_ The starting iterator of the beg target container
  • The function returns the iterator at the next address of the last element of the target container

Note: the two containers of the union must be ordered and in the same order (ascending or descending), otherwise an error will be reported.

Example:

int main()
{
	vector<int> v1, v2;
	for (int i = 0; i < 10; ++i) {
		v1.push_back(i);//v1: 0,1,2,3,4,5,6,7,8,9
		v2.push_back(i+5);        //v2: 5,6,7,8,9,10,11,12,13,14
	}
	vector<int> target;
	//Set target container size
	target.resize(v1.size()+ v2.size());

	auto target_end = set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), target.begin());
	cout << "Destination container for print save Union:";
	for (auto it = target.begin(); it != target_end; ++it) {
		cout << *it << " ";
	}
}

The output is as follows:

Target container for print save Union: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14

set_ difference

Function: find the difference set of elements in two containers and pass the difference set to another container
The so-called difference set: the difference set of container v1 relative to v2 is that the elements in v1 remove the remaining elements in the intersection of v1 and v2

Function prototype:

set_difference(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator target_beg)

  • beg1 first container start iterator
  • end1 first container end iterator
  • beg2 first container start iterator
  • end2 first container end iterator
  • target_ The starting iterator of the beg target container
  • The function returns the iterator at the next address of the last element of the target container

Note: the two containers of the union must be ordered and in the same order (ascending or descending), otherwise an error will be reported.

Example:

int main()
{
	vector<int> v1, v2;
	for (int i = 0; i < 10; ++i) {
		v1.push_back(i);//v1: 0,1,2,3,4,5,6,7,8,9
		v2.push_back(i + 5);      //v2: 5,6,7,8,9,10,11,12,13,14
	}
	vector<int> target;
	//Set target container size
	target.resize(max(v1.size(), v2.size()));

	auto target_end = set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), target.begin());
	cout << "Print v1 and v2 Difference set of:";
	for (auto it = target.begin(); it != target_end; ++it) {
		cout << *it << " ";
	}
	target_end = set_difference(v2.begin(), v2.end(), v1.begin(), v1.end(), target.begin());
	cout << "\n Print v2 and v1 Difference set of:";
	for (auto it = target.begin(); it != target_end; ++it) {
		cout << *it << " ";
	}
}

The output is as follows:

Print the difference between v1 and v2: 0 1 2 3 4
Print the difference between v2 and v1: 10 11 12 13 14

Common arithmetic generation algorithms

Note: all the algorithms described earlier in this article are in the header file #include < algorithm >, and now we will introduce two algorithms in #include < numeric >

Introduction:

  • Calculate calculates the cumulative sum of container elements
  • fill add element to container

accumulate

Function: calculate the cumulative sum of elements in the container

Function prototype:

accumulate(iterator beg,iterator end,value);

  • beg start iterator
  • End end iterator
  • Value is the starting value, if you don't need to set it to 0
  • Function returns the cumulative sum of elements in the container
#include<iostream>
#include<vector>
#include<numeric>
using namespace std;
int main()
{
	vector<int> v;
	for (int i = 0; i <= 100; i++) {
		v.push_back(i);
	}
	int sum = accumulate(v.begin(), v.end(), 0);
	cout << "The elements and in the container are:" << sum << endl;
}

The output is as follows:

The sum of elements in the container is: 5050

fill

Function: fills the container with specified elements

Function prototype:

fill(iterator beg,iterator end,value);

  • beg start iterator
  • End end iterator
  • Fill the container with value

Example:

#include<iostream>
#include<vector>
#include<numeric>
using namespace std;
int main()
{
	vector<int> v(10);
	fill(v.begin(), v.end(), 1);
	for (int i = 0; i < 10; ++i) {
		cout << v[i] << " ";
	}
}

The output is as follows:

1 1 1 1 1 1 1 1 1 1

Conclusion

This is the end of this article, with a total of more than 12000 words. Your praise, comments, attention and collection are the greatest support for me. Thank you! (whispering Mimi, this article may be the most detailed summary of common C++STL algorithms in Station C).

Topics: C++ Algorithm Back-end STL