Effective STL Terms 45

zhaozj2021-02-11  187

STL search algorithm [1]

What you have to look for, and you have a container or you have a range from iterators - the things you have to find are in it. How do you want to complete your search? Your arrows in your arrow have these: count, count_if, find, find_if, binary_search, limited, .... How do you want to make a choice?

simple. You can soon be easy to do. Can be faster, easier, better.

Temporarily, I assume that you have an iterator that specifies the search range. Then, I will consider that you have a container instead of a range.

To select a search policy, you must rely on whether your iterator defines a sequence interval. If so, you can accelerate (usually a log time) search by binary_search, loter_bound, us_bound, and equal_range. If the iterator is not divided, you can only use a linear time algorithm count, count_if, find, and find_if. In the following, I will ignore whether count and find have different _if, just like I ignore binary_search, lors_bound, upper_bound, and equal_range to have a predicate (Predicate). Are you relying on the default search predicate or a specified one yourself, which is the same as the sourcing of the search algorithm.

If you have a disorder interval, your choice is count or Find. They can answer slightly different problems, so they are worth taking care of them. The question is: "Do you have this value, if you have a few copies?" and the problem with the Find is: "Is there, if any, then where is it?"

Suppose something you want to know is, whether there is a specific widget value W in LIST. If you use count, this code looks like this:

List LW; // Widget list

Widget W; // Special Widget value

...

IF (count (lw.begin (), lw.end (), w)) {

... // W in LW

} else {

... // is not

}

Here is a common manual method: use count as an inspection that is existing. Count Returns zero or a positive, so we translate zero to false. If this makes it make more obvious,

IF (count (lw.begin (), lw.end (), w)! = 0) ...

And some programmers write this, but use implicit conversions more common, like the initial example.

Compared with the initial code, use Find to understand, because you must check if the return value of the Find and the List's End iterator is equal:

IF (Find (LW.BEGIN (), LW.END (), W)! = lw.end ()) {

... // found it

} else {

... // did not find

}

If it is to check if there is, the custom method is simple to encode. However, when the search is successful, its efficiency is relatively low, because Find stops after the match is found, and count must continue search until the end of the interval to find other matching values. For most programmers, Find's efficiency is sufficient to prove slightly increased complexity.

Typically, I only know if there is a certain value in the interval is not enough. Instead, you want to get the first one equal to the value of this value. For example, you may want to print this object, you may want to insert what it is in front of it, or you may want to delete it. When you need to know if there is something existing, do you know which object (or which object) has this value, you have to use Find: List :: itemic i = find (lw.begin (), lw.end (), w);

IF (i! = lw.end ()) {

... // Found, I point to the first

} else {

... // could not find it

}

For the sequence intervals, you have other options, and you should use them clearly. Count and find are linear times, but the sequential interval search algorithm (binary_search, limited, usr_range) is the logarithmic time.

Another migration is migrated from the unordered interval to the sequential interval: judge whether or not two values ​​are the same as the equivalent of the use of equal value [2]. That is because count and find algorithms are searched by equal, and binary_search, limited, usr_bound, and equal_range are equivalent.

To test whether there is a value in the sequence interval, use binary_search. Unlike standard C librarism (so it is also the standard C library) BSearch, binary_search only returns a BOOL: Whether this value is found. Binary_search answers this question: "Is it?" Its answer can only be yes. If you need more information than such information, you need a different algorithm.

Here has a binary_search application for the sequence vector:

Vector vw; // Establish a vector, put it in

... // Data,

sort (vw.begin (), vw.end ()); // Sort data

Widget W; // The value to be found

...

IF (binary_search (vw.begin (), vw.end (), w) {

... // W in VW

} else {

... // is not

}

If you have a sequential area and your problem is: "Is it it, if, where?" You need equal_range, but you might want to use Lower_Bound. I will discuss Equal_Range soon, but first, let's see how to locate a certain value in the interval.

When you use Lower_Bound to find a value, it returns an iterator, which points to the first copy of this value (if found) or to the location where you can insert this value (if not found). So Lower_Bound answered this question: "Is it? If so, where is it? If not, it will be?" And Find Value. But unlike Find, you can't just detect if the return value of Lower_Bound is equal to the END iterator. Instead, you must detect that the object indicated by Lower_Bound is not the value you need.

Many programmers use Lower_Bound:

Vector :: item i = limited (vw.begin (), vw.end (), w); if (i! = vw.end () && * i == w) {// guarantee i pointing one Object;

// also guarantees that this object has the correct value.

// This is a bug!

... // Find this value, I point to

// The first object equal to this value

} else {

... // did not find

}

In most cases this is a good, but it is not true. Look at the code that you find the value you need:

IF (i! = vw.end () && * i == w) ...

This is an equal test, but LOWER_BOUND search is equivalent. In most cases, equivalent testing and equal testing have the same result, but it is not difficult to see if the results of notification, equivalent and equivalent results are not difficult to see. In this case, the above code is wrong.

To complete it, you must detect whether the value of the object that Lower_Bound returns to the object is equivalent to the value you want to find. You can do it manually, but you can do more, because you must confirm the same comparison function as Lower_Bound use. In general, it can be a free function (or function object). If you pass a comparison function to Lower_Bound, you must confirm that the same comparison function is used with your handwritten equivalent detection code. This means that if you change your comparison function you pass to Lower_Bound, you have to make a modification of your equivalent test. Keep a comparison function synchronization is not a rocket launch, but it is another thing to remember, and I think you have a lot of things that you need to remember.

There is a simple method here: using Equal_Range. Equal_Range returns a pair of iterators, the first one equal to LOWER_BOUND returns, the second equal to UPPER_BOUND (i.e., equivalent to the next one of the ends of the end of the range). Therefore, Equal_Range returns an iterator that is divided into intervals with the value of the value you want to search. A very good algorithm, isn't it? (Of course, you may call Equivalent_Range will be better, but it is also very good to be Equal_Range.)

There are two important places for the return value of Equal_Range. First, if the two iterators are the same, it means that the interval of the object is empty; this is only found. This result is an answer to "it is?" This problem with Equal_Range. You can use it:

Vector VW;

...

sort (vw.begin (), vw.end ());

Typedef Vector :: item vwiter; // Convenient Typedef

Typedef Pair vwiterpair;

Vwiterpair p = equal_range (vw.begin (), vw.end (), w);

IF (p.first! = p.second) {// If equal_range does not return

// empty range ...

... // Description found, P.First point

// First and P.second

// Point to the next next one

} else {

... // Nothing found, P.First and

// p. Second point to search value

} // Insert location

This code is only equivalent, so it is always correct. The second thing to note is that equal_range returned is two iterators, which are Distance equal to the number of objects in the interval, that is, equivalent to the object to be viewed. As a result, Equal_Range is not preferentially completed the task of the sequential interval, and the count is completed. For example, if you want to find the Widget equivalent to W in VW, then print out how much such widget exists, you can do this:

Vwiterpair p = equal_range (vw.begin (), vw.end (), w);

COUT << "there" << distance (p.first, p.second)

<< "Elements in vw equivalent to w.";

So far, what we discussed is assumed that we have to search for a value in a range, but sometimes we are more interested in finding a location in the range. For example, suppose we have a TimeStamp class and a TimeStamp's Vector, which is sorted in front of the old TimeStamp in front:

Class TimeStamp {...};

Bool Operator <(const timestamp & lhs, // Return to Time LHS

Const TimeStamp & RHS); / / Whether in front of RHS

Vector vt; // Establish VECTOR, fill data,

... // Sort, make old time

sort (vt.begin (), vt.end ()); // in front of the front

Now, we have a special TimeStamp - Agelimit, and we remove all the old TimeStamp from the Agelimit from the VT. In this case, we don't need to search for TimeStamp in VT, because there may be no elements equivalent to this precise value.

Instead, we need to find a location in VT: the first element is not more older than Agelimit. This is simple, because Lower_Bound will give us an answer:

TimeStamp Agelimit;

...

Vt.rase (vt.begin (), limited to Troubled all from VT

Vt.end (), // Row value in Agelimit

Agelimit); // front object

If our needs have changed slightly, we want to exclude all TimeStamp as old as Agelimit, which is the first location of the first smaller TimeStamp than Agelimit. This is a task for Upper_Bound specials:

vt.ras (vt.begin (), Upper_bound (vt.begin (), // excluded from VT

Vt.end (), // Row in front of the value of Agelimit

Agelimit); // or equivalent object

If you want to insert something into a sequence interval, and the insertion position of the object is where it should be in an ordered equivalent relationship, Upper_bound is also useful. For example, you may have a List of a sequential Person object, and the object is sorted by Name:

Class Person {

PUBLIC:

...

CONST STRING & NAME () Const;

...

}

Struct Personnameless:

Public Binary_Function {Bool Operator () (Const Person & lh, Const Person & rhs) Const

{

Return lhs.name ()

}

}

List lp;

...

lp.sort (persuNameless ()); // Sort by PersonnameLESS LP

To keep List is still our desired order (according to Name, the name of the insert is still arranged in order), we can use Upper_Bound to specify the insertion position:

Person newperson;

...

lp.insert (Upper_bound (lp.begin (), // Row in NewPerson in LP

Lp.end (), // before or equivalent

NewPerson, //'s last one

Personnameless ()), // behind the object

NewPerson); // Insert NewPerson

This work is very good and very convenient, but it is very important to not be misleading - this usage of Upper_Bound makes us magic in a LIST to find insertion positions in a log time. We didn't. Because we are using List, find a linear time, but it only uses a few comparisons.

I have been here, I only consider that we have a pair of iterators that define the search range. Usually we have a container instead of a range. In this case, we must distinguish between sequences and associated containers. For standard sequence containers (Vector, String, Deque, and List), you should follow my recommendations I proposed in this Territory, use the container's Begin and End iterators to divide the interval.

This situation is different for standard related containers (SET, MULTISET, MAP, and MULTIMAP) because they provide searches for members functions, which are often better than using STL algorithms [3]. Fortunately, the member function usually has the same name and the corresponding algorithm, so the previous discussion recommends the algorithm count, find, equal_range, limited to the associated container, you can simply use the same name for the same name when searching the associated container. Instead.

Binary_search calls are different, because this algorithm does not provide the equal member function. To test if there is a value in SET or MAP, use count's customary method to detect a member:

Set S; // Establish Set, put into data

...

Widget W; // W is still saving the value to search

...

IF (S.count (w)) {

... // Present and W Equivalent Value

} else {

... // There is no such value

}

To test if a value exists in Multiset or MultiMap, Find is often better than Count, because once found some individual objects equal to the expected value, Find can stop, and count, in the most abundant case, must detect the container Every object.

However, counts are reliable to associated container counts. Special, it is better than calling Equal_Range and then apply to the result iterator. First, it is clearer: Count means "count." Second, it is simpler; you don't have to create a pair of iterators and then make it the composition (translation: just first and second) to Distance. Third, it may be faster.

To give us what we take into account in this Territor, where is our starting? The table below has everything. The algorithm you want to know is existent in the demand interval in the sequential interval in the sequential interval in the sequential interval in the set or map of MULTIMAP exists on MultiSet or MultiMap? Findbinary_SearchCountFind does not exist? If so, where is the first object equal to this value? FINDEQUAL_RANGEFINDFIND OR LOWER_BOUND (See Article) Where is the number of objects that do not equal to the expected value? Find_iflower_boundlower_boundlower_bound Where is the number of objects equal to the expected value? How many objects are added for Find_ifupper_boundupper_boundupper_bound equal to the expected value? Where is the owner of the countqual_rangecountcount equal to the expected value? Find (iteration) Equal_Rangeeequal_Rangeequal_Range

The above table summarizes how to operate the sequence interval, and the frequency of Equal_Range may be surprising. When searching, this frequency rose because of the importance of equivalent detection. For LOWER_BOUND and UPPER_BOUND, it is easy to retreat in equal detection, but for equal_range, only the equivalent is natural. In the second line of sequence intervals, equal_range defeated Find because of a reason: Equal_Range spent log hours, while Find spends linear time.

For MultiSet and MultiMap, this table lists the Find and Lower_Bound two algorithms as candidates for the first line of the first object equal to a particular value. It has been a usual selection for this task, and you may have already noticed that this only is Find in the SET and MAP. But for the MULTI container, if not only one value exists, Find does not guarantee the first element of the container to give a given value in the container; it only recognizes one of these elements. If you really need to find the first element of a given value, you should use Lower_Bound, and you must manually do an equivalent test for the second part. (You can use Equal_Range to avoid manual equivalence detection, but call Equal_Range more much more than call Lower_Bound.)

It is easy to make a selection in count, find, binary_search, limited, ket, usper_bound, and equal_range. When you call, select the algorithm or the member function can give you the behavior and performance you need, and it is the least work. Do it according to this recommendation (or refer to that form), you will not be confused.

[1] Scott Meyers, Effective Stl: 50 Specific Ways to Improve Your Use of the Standard Template Library, Addison-Wesley, 2001, ISBN 0-201-74962-9. This article is written based on the clause 45 of Effective STL.

[2] If in some sort order, one is not in another one, then two object equivalents. Usually, the equivalent value is equal, but not always. For example, string "STL" and "STL" are equivalent in case in case insects, but they are obviously not equal. To know the details of the equivalence and equality, please refer to Terms 19.

[3] For this requirement, you can find reasons in terms 44.

转载请注明原文地址:https://www.9cbs.com/read-4779.html

New Post(0)