I have a long vector of strings. Example:
std::string data;
data.push_back("abc");
data.push_back("bcd");
//...
data.push_back("zze");
data.push_back("zzz");
The strings in the vector are alphabetically sorted.
I understand I can use std::find to find the existence and position of a string in the std::vector. But since my vector is alphabetically sorted, is there an easy to implement and more effective method to check the existence of a value in the vector?
If the container is sorted, you might use std::binary_search:
Checks if an element equivalent to value appears within the range [first, last).
std::string some_value = "xyz";
const auto found =
std::binary_search(std::cbegin(data), std::cend(data), some_value);
So found is true if some_value is found and false in the other case.
If you are interested in getting an iterator which points to the element you are looking for, then consider using std::lower_bound:
const auto it =
std::lower_bound(std::cbegin(data), std::cend(data), some_value);
if (it != std::cend(data)) {
// element is found, it points to this element
}
Related
I came across this c++ code for counting frequency in a vector.
std::map<std::string, int> countMap;
// Iterate over the vector and store the frequency of each element in map
for (auto & elem : vecOfStrings)
{
auto result = countMap.insert(std::pair<std::string, int>(elem, 1));
if (result.second == false)
result.first->second++;
}
from https://thispointer.com/c-how-to-find-duplicates-in-a-vector/. I want to ask what does
result.second == false mean?
Since std::map and the other non-multi associative containers only store unique items there is a chance that when you insert something into it it wont actually insert since it may already be present. insert therefore returns a std::pair<iterator, bool> where the bool will be true if the insert succeeded and false otherwise.
I would like to point out you can get rid of the if statement in the loop. Because of how operator[] of a map works the loop can be replaced with
for (const auto & elem : vecOfStrings) // also added const here since we don't need to modify elem
{
++countMap[elem];
}
And now if elem exists then you increment the value and if it doesn't you added elem to the map and increment its value.
std::map::insert returns a std::pair<iterator, bool>.
pair.first is an iterator to the newly inserted element OR the element that was already in the map and prevented the insertion.
pair.second tells whether or not the insertion happened.
result.second == false is detecting the case where nothing was inserted into the map due to a key collision.
Note that with C++17, this can be written to be a bit more clear:
auto [itr, inserted] = countMap.insert({elem, 1});
if (!inserted) {
itr->second++;
}
From cppreference:
Returns a pair consisting of an iterator to the inserted element (or
to the element that prevented the insertion) and a bool denoting
whether the insertion took place.
result.first gives you the iterator to the element, while result.second tells you whether the element was actually inserted or did already exist.
std::map::insert returns a pair where the second value indicates whether any insertion actually happened. If the value is false, this means no value was inserted into the map because a value with the same key already exists.
However, the code shouldn’t be written like this: comparing against boolean literals is a nonsensical operation. Instead you’d write
if (not result.second)
// or
if (! result.second)
std::map::insert returns a pair of iterator and a bool. The bool indicates whether the insertion actually took place. The code you listed seems to increment the mapped int if key collision happens on insert.
We have STL (Standard Template Library) multiset, we want to implement a binary search that will give us the first less-or-equal element compared to some value x
From this post: lower_bound == upper_bound, we see that we can use the standar lower_bound and upper_bound to find greater values compared to x, what about finding smaller or equal.
Is it possible to do such thing?
Just use the upper_bound member function of the multiset. upper_bound will return the first element that is grater than the value you are searching for. That means the iterator before that will be the first element that is equal to or smaller than the value. So in
int main()
{
std::multiset<int> ms = {1,1,2,2,3,3,4,4,5,5};
auto end = ms.upper_bound(3);
for (auto it = ms.begin(); it != end; ++it)
std::cout << *it;
}
It will print 112233 since that is all the element less than or equal to 3.
Of course you will need to make sure upper_bound does not return begin() which would mean there are no elements less than or equal to what you are searching for.
I read the description for C++ upper_bound() function and lower_bound() function. It is interesting for me that upper_bound() only returns the first iterator with value > val (or return last iterator in the range [first, last) if val not found).
The implementation is different from lower_bound(), while it returns the first iterator NOT SMALLER than val, so it allows the return pointer equivalent to val.
I am just curious to know what is the purpose to design upper_bound() in this way that upper_bound() must NOT return an iterator with value equivalent to val?
For example:
vector<int> a = {1, 2, 3, 4};
auto i = lower_bound(a.begin(), a.end(), 2); // i is iterator at 2;
auto j = upper_bound(a.begin(), a.end(), 2); // j is iterator at 3;
http://www.cplusplus.com/reference/algorithm/lower_bound/
In C++, iterators usually work in pairs. The first iterator points to the first element to consider, and the last iterator points to one-past-the-last element to consider. This is to make looping easy:
for(it cur=first; cur!=last; cur++)
As such, lower_bound and upper bound together, form a "range of all elements equal to the item you searched for. The same range that std::equal_range returns.
upper_bound is useful in cases like std::multimap where values are stored in sorted order. It lets you traverse within a range where starting position can be decided by lower_bound, and end-before-this-position is decided by upper_bound.
for (iter_type it = myMap.lower_bound("key_val"); it != myMap.upper_bound("key_val"); it++)
This for loop would traverse till the point key == key_val.
How to get the last element of an std::unordered_map?
myMap.rbegin() and --myMap.end() are not possible.
There is no "last element" in a container that is unordered.
You might want an ordered container, e.g. std::map and access the last element with mymap.rbegin()->first (Also see this post)
EDIT:
To check if your iterator is going to hit the end, simply increment it (and possibly save it in a temporary) and check it against mymap.end(), or, even cleaner : if (std::next(it) == last)
In your comments, it appears your goal is to determine if you are on the last element when iterating forward. This is a far easier problem to solve than finding the last element:
template<class Range, class Iterator>
bool is_last_element_of( Range const& r, Iterator&& it ) {
using std::end;
if (it == end(r)) return false;
if (std::next(std::forward<Iterator>(it)) == end(r)) return true;
return false;
}
the above should work on any iterable Range (including arrays, std containers, or custom containers).
We check if we are end (in which case, we aren't the last element, and advancing would be illegal).
If we aren't end, we see if std::next of us is end. If so, we are the last element.
Otherwise, we are not.
This will not work on iterators that do not support multiple passes.
You cant. by definition, the element is not stored based on some sort of order. the key is hashed first and that's why O(1) search is possible. if you wanna check whether a key exists in the unordered_map or not, u can use this code:
std::unordered_map dico;
if(dico.count(key)!=0){
//code here
}
std::unordered_map::iterator last_elem;
for (std::unordered_map::iterator iter = myMap.begin(); iter != myMap.end(); iter++)
last_elem = iter;
// use last_elem, which now points to the last element in the map
This will give you the last element in whatever order the map gives them to you.
Edit: You need to use std::unordered_map<YourKeyType, YourValueType> instead of just std::unordered_map. I just wrote it like this because you did not provide the type in your question.
Alternatively, as suggested by vsoftco (thanks), you could declare both last_elem and iter as decltype(myMap)::iterator.
(If you're compiling with the MSVC++ compiler, then you will need to add typedef decltype(myMap) map_type; and then instead of decltype(myMap)::iterator use map_type::iterator.)
.end() is an iterator to the "element past the last element". That's why you compare it like this when you loop through a map:
for (auto it = myMap.begin(); it != myMap.end(); ++it) // '!=' operator here makes it possible to only work with valid elements
{
}
So you want the "last" element (whatever that may be, because it's not really guaranteed to be the last in an unordered map, since it ultimately depends on how the key was hashed and in which "bucket" it ends up in). Then you need: --myMap.end()
More specifically, .end() is a function, that returns an iterator, same as .begin() returns an iterator. Since there is no .rbegin() in an std::unordered_map, you have to use -- (the decrement operator):
auto it = --myMap.end();
To access the key you use it->first, to access the value you use it->second.
The accepted answer seems wrong. Unordered_map does have the last element even though the key-value pair is not stored in sorted order. Since the iterator of unorered_map is forwar_iterator(LegacyForwardIterator), the cost to find the last element is O(n). Yakk - Adam gave the correct answer. Essentially, you have to iterator the container from begin to end. At each iteration, you have to check whether the next element is end(); if yes then you are at the last element.
You cannot call prev(it) or --it. There will be no syntax error, but you will have a runtime error (more likely segmentation fault) when using the prev(it) or --it. Maybe next version of compiler can tell you that you have an logic error.
It may not be the best solution, performance-wise, but in C++11 and later, I use a combination of std::next() and size() to jump all elements from the beginning of the map, as shown below:
std::unordered_map<int,std::string> mapX;
...
if (mapX.size() > 0) {
std::unordered_map<int,std::string>::iterator itLast =
std::next(mapX.begin(), mapX.size() - 1);
...
assumend I have a (filled) list
std::list<std::pair<int,otherobject>> myList;
and want to find() the first element within this list, where int has a specific value - how can I do that?
To explain it a bit further:
I want to append these pairs to the list with an int that identifies otherobject but is not unique. The order where these int/otherobject pairs arrive has to be kept.
When an int is found during access to elements of this list the first occurence of that int has to be given back (and removed).
Thanks!
I think I'd use the standard find_if algorithm:
auto pos = std::find_if(myList.begin(), myList.end(),
[value](std::pair<int, otherobject> const &b) {
return b.first == value;
});
That gives an iterator to the element with the required value -- from there, you can copy the value, delete the value, etc., just like with any other iterator.
According to your need the better option would be to use a multimap.
In you case it would give :
std::multimap<int, otherobject> myMultiMap;
Then when looking for otherobjects linked to a int ( myInt) you'll do :
std::pair<std::multimap<int, otherobject>::iterator, std::multimap<int, otherobject>::iterator> result = myMultiMap.equal_range(myInt);
for (std::multimap<int,otherobject>::iterator iter=result.first; iter!=result.second; ++iter)
{
std::cout << it->second;
}
This is a STL container so you'll easilly find online documentation.