How to delete elements:
- Remove all objects with specific values in a container:
If the container is a vector, string, or deque, use erase-Remove.
C. Erase (remove (C. Begin (), C. End (), 1963), C. End (); // deletes all 1963
If the container is list, use list: Remove.
If the container is a standard associated container, use its erase member function.
- Remove all objects in a container that meet a specific criterion:
If the container is a vector, string, or deque, use a erase-remove_if.
C. erase (remove_if (C. begin (), C. end (), badvalue), C. end (); // delete all elements that make bool badvalue (t) return true
If the container is list, use list: remove_if.
If the container is a standard associated container, use remove_copy_if and swap, or write a loop to traverse the container elements. When you pass the iterator to erase, remember to increment it later. (Because deletion invalidates all iterator pointing to the deleted element, it must be incrementally added)
Assoccontainer <int> C; // C is now a standard associated container
Assoccontainer <int> goodvalues; // temporary container used to hold non-Deleted Values
// Copy undeleted values from C to goodvalues
Remove_copy_if (C. Begin (), C. End (), inserter (goodvalues, goodvalues. End (), badvalue );
C. Swap (goodvalues );Assoccontainer <int> C;
...
For (assoccontainer <int>: iterator I = C. Begin (); I! = C. End ();/** // * nothing */)...{
If (badvalue (* I) c. Erase (I ++ );
Else ++ I;
}
- Do something in the loop (except Delete objects ):
If the container is a standard sequence container, write a loop to traverse the container elements. Whenever erase is called, remember to use its return value to update your iterator.
For (seqcontainer <int >:: iterator I = C. Begin (); I! = C. End ();)...{
If (badvalue (* I ))...{
// Do what you want to do, such as log
I = C. Erase (I); // assign the returned values of erase to I to keep I valid.
}
Else ++ I;
}
If the container is a standard associated container, write a loop to traverse the container elements. When you pass the iterator to erase, remember to increment it later. (This is no different from the write loop method in the second case)
Uh .. You mean list? You can use the sequence container or associated container method here.
How to select a Sorting Algorithm
- If you want to perform full sorting on the vector, String, deque, or array, you can use sort or stable_sort.
- If you have a vector, String, deque, or array, you only need to sort the first n elements. partial_sort should be used. Bool qualitycompare (const widget & LHS, const widget & RHs); // The returned LHS quality is better than RHS Quality
// Put the best 20 elements (in order) at the front end of Widgets
Partial_sort (Widgets. Begin (), Widgets. Begin () + 20, Widgets. End (), qualitycompare );
- If you have a vector, String, deque, or array, You need to identify the nth element or you need to identify the first n elements without knowing their order, you should pay attention to and call nth_element. // Put the best 20 elements on the front end of widgets, regardless of the order between them
Nth_element (Widgets. Begin (), Widgets. Begin () + 19, Widgets. End (), qualitycompare );
- If you want to separate the elements or arrays of the standard sequence container to meet or not meet a certain standard, you probably need to find partition or stable_partition. // Move all widgets that meet hasacceptablequality to the front-end of widgets,
// Return an iterator pointing to the first unsatisfied widget
Vector <widget>: iterator goodend = partition (Widgets. Begin (), Widgets. End (), hasacceptablequality );
- If your data is in list, you can directly use partition and stable_partition. You can use sort of list to replace sort and stable_sort. If you need the effect provided by partial_sort or nth_element, you must indirectly complete this task.
The sort, stable_sort, partial_sort, and nth_element algorithms must randomly access the iterator. Therefore, they can only be used for vector, String, deque, and array. It does not make sense for sorting elements of standard associated containers, because such containers use their comparison functions to maintain order at any time. The only container that we may but cannot use sort, stable_sort, partial_sort, or nth_element is list. List provides some compensation by providing the sort member function. (Interestingly, list: Sort provides stable sorting .) Therefore, if you want to sort a list, you can, but if you want to perform partial_sort or nth_element on the objects in the list, you must indirectly complete it. An indirect method is to copy an element to a container that supports Random Access to the iterator, and then apply the required algorithm to it. Another method is to create a list: iterator container, use an algorithm for that container, and then access the list element through the iterator. The third method is to use the information of the sorted iterator container to iteratively join the list elements to the location you want them to be. As you can see, there are many options.
Partition and stable_partition are different from sort, stable_sort, partial_sort, and nth_element. They only need two-way iterators. Therefore, you can use partition and stable_partition on any standard sequence iterator.
"But what is the performance ?", You want to know. This is an excellent problem. Generally, algorithms that do more work take longer than those that do less, while those that must be sorted stably take longer than those that ignore stability. We can sort the algorithms we discuss in this article as follows, and the algorithms that require less resources (time and space) are listed before the need for more:
1. Partition |
4. partial_sort |
2. stable_partition |
5. Sort |
3. nth_element |
6. stable_sort |
How to select search algorithms
The following table shows everything. (What details do you want? Oh, it is better to directly read objective STL item45 .)
What you want to know |
Algorithm Used |
Member functions used |
In unordered intervals |
In the ordered Interval |
On set or map |
On Multiset or multimap |
Does the expected value exist? |
Find |
Binary_search |
Count |
Find |
Does the expected value exist? If so, where is the first object equal to this value? |
Find |
Performance_range |
Find |
Find or lower_bound (see below) |
Where is the first object not before the expected value? |
Find_if |
Lower_bound |
Lower_bound |
Lower_bound |
Where is the first object after the expected value? |
Find_if |
Upper_bound |
Upper_bound |
Upper_bound |
How many objects are equal to the expected value? |
Count |
Pai_range, and then distance |
Count |
Count |
Where are all objects equal to the expected value? |
Find (iteration) |
Performance_range |
Performance_range |
Performance_range |
The table above summarizes how to operate the ordered interval. The occurrence frequency of the interval _ range may be surprising. When searching, this frequency increases because of the importance of equivalence detection. For lower_bound and upper_bound, it is easy to retreat in equal detection, but for pai_range, it is natural to detect only equal values. In the ordered interval of the second row, pai_range beat find for another reason: pai_range takes logarithm time, while find takes linear time.
For Multiset and multimap, when you are searching for the row of the first object that is equal to a specific value, this table lists the find and lower_bound algorithms as candidates. Find is a common choice for this task, and you may have noticed that in the set and map columns, this is only find. However, for multi containers, if not only one value exists, find does not guarantee that it can identify the first element in the container that is equal to the given value; it only recognizes one of these elements. If you really need to find the first element equal to the given value, you should use lower_bound, and you must manually perform the equivalence check on the second part, valid STL Article 19 helps you confirm that you have found the value you are looking. (You can use interval _range to avoid manual equivalence detection, but it takes much more to call interval _range than to call lower_bound .)
It is easy to choose from Count, find, binary_search, lower_bound, upper_bound, and pai_range. When you call it, selecting an algorithm or a member function can give you the behavior and performance you need, and it is the least effort. Follow this advice (or refer to the table) and you will not be confused.
Algorithm requiring ordered intervals
Here is a table of algorithms that can only operate on ordered data:
Binary_search |
Lower_bound |
Upper_bound |
Performance_range |
Set_union |
Set_intersection |
Set_difference |
Set_effecric_difference |
Merge |
Inplace_merge |
Includes |
|
In addition, the following algorithms are generally used for ordered intervals, although they are not required:
Unique and unique_copy provide well-defined behaviors even in unordered intervals. But let's see how the standard describes unique's behavior (the italic is a minefield ):
FromContinuousRemove all elements except the first element from the group.
In other words, if you want unique to remove all repeated values from an interval (that is, to make all values in the interval "unique"), you must first ensure that all repeated values are followed by one. What have you guessed? That is one of the things completed by sorting. In fact, unique is generally used to remove all repeated values from the range, so you almost always need to ensure that the range you pass to unique (or unique_copy) is ordered. (UNIX developers will find that there is a striking similarity between STL unique and Unix uniq. I think this similarity is by no coincidence .)
By the way, unique removes elements from a range in the same way as remove, that is, it only differentiates elements that are not excluded. If you do not know what that means, immediately turn to valid STL clauses 32 and 33. It is always time to understand the importance of removing and similar remove algorithms (including unique) behavior. Basic understanding is not enough. If you don't know what they do, you will be in trouble.
Always let the comparison function return false for equal values
(Oh, let me first say that the comparison function here is not a judgment function ...)
In short, implement "<" or ">" instead of "<=" or "> = ".
Unless your comparison functions always return false for equal values, you will break all the standard associated containers regardless of whether they allow storage of duplicates.
Technically, the comparison functions used to sort associated containers must define a strict weak ordering on the objects they compare )". (The comparison functions passed to algorithms such as sort also have the same restrictions ). If you are interested in the details of strict weak ordinal meanings, you can find them in many comprehensive STL reference books, such as the C ++ standard library by josutis. austern's generic programming and the STL, and sgi stl websites. I have never found this detail so important, but a requirement for strict weak ordering directly points to this provision. That requirement is that all functions that define strict weak sequentions must return false when two copies of the same value are passed in.
Be cautious with the strange analysis of c ++
Suppose you have an int file, and you want to copy the int to a list. This looks like a reasonable method:
Ifstream datafile ("ints. dat ");
// Warning! This is not what you think
List <int> data (istream_iterator <int> (datafile), istream_iterator <int> ());
The idea here is to pass an istream_iterator to the list range constructor, So copy the int from the file to the list.
This code can be compiled, but it does not do anything at runtime. It does not read any data from the file. It does not even create a list. That's because the second sentence does not declare the list, and it does not call the constructor. Actually, what it does is ......
Fighting, this declares a function data whose return type is list <int>. This function data has two parameters:
- The first parameter is datafile. Its type is istream_iterator <int>. The parentheses around datafile are redundant and ignored.
- The second parameter has no name. It is a pointer to a function that has no parameters and returns istream_iterator <int>.
Strange? But this complies with a general rule in C ++-almost anything may be analyzed as a function declaration. If you have been programming in C ++ for a while, you should have encountered the appearance of another rule. How many times have you seen this error?
Class widget... {...}; // assume that the widget has a default constructor.
Widget W (); // Well ......
This does not sound a widget called W. It declares a function called w without parameters and returns the widget. Learn to recognize thisFailure (faux pas)It is a real ceremony to become a C ++ programmer.
All of this is interesting (in its own twist), but it does not help us tell what we want to say, that is, you should use the content of a file to initialize a list <int> object. Now that we know what we must overcome, we can easily express it. It is invalid to enclose a real parameter with parentheses, but it is legal to enclose a function call with parentheses. Therefore, by adding a pair of parentheses, we force the compiler to look at things in our way:
List <int> data (istream_iterator <int> (datafile), istream_iterator <int> ());
Unfortunately, not all compilers know about it. A better solution is to step back from using the anonymous istream_iterator object in the Data declaration and just give those iterator names. The following code works everywhere:
Ifstream datafile ("ints. dat ");
Istream_iterator <int> databegin (datafile );
Istream_iterator <int> dataend;
List <int> data (databegin, dataend );
This is today. For more information, it is better to directly look at objective STL... ^_^.