Trying to update an unordered map using the following code snippet to have only lowercase letters, but it seems to stop after erasing one key-value pair { [33 '!']: 3 } and exits the loop leaving the rest of the map unvisited and prints the partly updated map.
for (auto &i : m)
if (!(i.first >= 'a' && i.first <= 'z'))
m.erase(i.first);
Following debugging images revealed the above
The complete code is herewith:
#include <iostream>
#include <unordered_map>
#include <algorithm>
using namespace std;
int main()
{
string line = "Try! Try! Try! until you succeed";
//getline(cin, line);
unordered_map<char, int> m;
for (int i = 0; line[i]; i++)
{
char lower = (char)tolower(line[i]);
if (m.find(lower) == m.end())
m.insert(make_pair(lower, 1));
else
m[lower]++;
}
for (auto &i : m) //only updates until !
if (!(i.first >= 'a' && i.first <= 'z'))
m.erase(i.first);
cout<<"The freq. map so formed is : \n";
for (auto &i : m)
cout<<i.first<<"\t"<<i.second<<endl;
return 0;
}
/*
OUTPUT :
The freq. map so formed is :
d 1
t 4
r 3
e 2
y 4
l 1
o 1
5
n 1
u 3
i 1
s 1
c 2
*/
Can't seem to understand why it won't loop through the complete unordered map.
Also, not sure if this helps towards a clear picture but, when the standard map is used instead of unordered map it gives an Address Boundary Error at the same instance where the next character of the map needs to be updated like so:
You cannot erase the element of a map while iterating this way. When you erase the iterator, it becomes invalidated, so you need to explicitly increment it before you delete the element.
Try this code instead:
for (auto it = m.begin(); it != m.end();)
if (!((*it).first >= 'a' && (*it).first <= 'z'))
it = m.erase(it);
else
++it;
One of the c++ traps is that iterators are invalidated for most containers when they are modified.
std::unordered_map<Key,T,Hash,KeyEqual,Allocator>::erase - cppreference.com
References and iterators to the erased elements are invalidated. Other iterators and references are not invalidated.
So when you have remove item form m current iterators are become invalid.
Now range base for loop uses iterators underneath.
The beast way to fix it us use std::erase_if algorithm:
std::erase_if(m.begin(), m.end(), [](const auto& i) {
return !(std::islower(i.first));
});
Related
This question already has answers here:
How to keep only duplicates efficiently?
(10 answers)
Closed 2 years ago.
I'm having trouble with writing a program to keep only duplicates,here is what I already wrote :
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> v;
for (int n; std::cin >> n;)
{v.push_back(n); }
std::sort(v.begin(),v.end());
for(std::vector<int>::iterator b = v.begin();b<v.end();++b)
{
if(*b != *(b+1) )
{
v.erase(b);
}
}
for(std::vector<int>::iterator i = v.begin();i < v.end();++i)
{
std::cout<<*i<<" ";
}
}
What I mean by "keep duplicates" is for example
Input: 13 7 2 13 5 2 1 13
Output : 2 13
I apologise if code is not so perfect, I'm complete beginner.I hope you understand my dificulties.
When you erase() from a vector all iterators pointing at the erased element or later in the vector becomes invalidated. Fortunately, erase() returns an iterator to the element that was after the erased element, so you could do:
for(auto b = v.begin(), end=std::prev(v.end()); b != end;) {
if(*b != *(b+1) ) b = v.erase(b);
else ++b;
}
Note that you need to stop iterating one element before end() since you do *(b+1).
The above does however not remove all but one of the repeated elements.
A different approach could be to search for the first element not being part of a repeating sequence and to erase the element if it had no repetitions and to erase all repetitions but one if it had repetitions.
I've used the standard algorithm std::find_if_not in the example below but you can easily replace it with a loop that does that same thing. Just search for the first element not being equal to *b.
#include <algorithm>
for(auto b = v.begin(); b != v.end();) {
// find the first element not being a repetition of *b
auto r = std::find_if_not(b + 1, v.end(), [&b](const auto& v) { return *b==v; });
if(b + 1 == r) b = v.erase(b); // *b had no repetitions, erase b
else b = v.erase(b + 1, r); // erase all repetitions except one element
}
You are trying to dereference an invalid iterator. On the last iteration of your second for loop, *(b+1) will try to dereference this iterator.
Change:
for(std::vector<int>::iterator b = v.begin();b<v.end();++b)
to:
for(std::vector<int>::iterator b = v.begin();b<v.end()-1;++b)
And change your erase to:
v.erase(b+1)
You are trying to erase the iterator in the current iteration.
I have with myself n integers. I am given with starting value(SV) and ending value(EV), I need to delete the values that lie within the range of these values. The starting value as well the ending value may not exist in the set of n integers. This I need to do in O(No. Of elements deleted). Container such as vector of integers is failing since I need to B.Search and get the iterator that is greater than equal to the SV and for EV as well which takes an additional time of Log n. Any approach appreciated.
Edit : I was even thinking of using maps, storing the values as keys, and erasing on the basis of those key values. But the problem again is that the operation lower_bound and upper_bound occur in log time.
If you need keep order in container just use:
set http://www.cplusplus.com/reference/set/set/
or multiset http://www.cplusplus.com/reference/set/multiset/ if values can repeat.
Both have lower_bound and upper_bound functionality.
You can use the erase-remove idiom with std::remove_if to check if each value is between the two bounds.
#include <algorithm>
#include <iostream>
#include <vector>
int main()
{
std::vector<int> values = {1,2,3,4,5,6,7,8,9};
int start = 3;
int stop = 7;
values.erase(std::remove_if(values.begin(),
values.end(),
[start, stop](int i){ return i > start && i < stop; }),
values.end());
for (auto i : values)
{
std::cout << i << " ";
}
}
Output
1 2 3 7 8 9
As Marek R suggested there is std::multiset.
Complexity of the whole exercise is O(log(values.size()+std::distance(first,last)) where that distance is 'number of elements erased'.
It's difficult to see how you can beat that. At the end of it there is always going to be something that increases with the size of the container and log of it is a good deal!
#include <iostream>
#include <set>
void dump(const std::multiset<int>& values);
int main() {
std::multiset<int> values;
values.insert(5);
values.insert(7);
values.insert(9);
values.insert(11);
values.insert(8);
values.insert(8);
values.insert(76);
dump(values);
auto first=values.lower_bound(7);
auto last=values.upper_bound(10);
values.erase(first,last);
dump(values);
return 0;
}
void dump(const std::multiset<int>& values){
auto flag=false;
std::cout<<'{';
for(auto curr : values){
if(flag){
std::cout<<',';
}else{
flag=true;
}
std::cout<< curr;
}
std::cout<<'}'<<std::endl;
}
Is there any STL function which does this?
For vector:
4 4 5 5 6 7
The expected output should be 2,because of one 6 and 7
Would you be kind to help me count them classic if there is no STL function?
I don't think there is an algorithm for that in STL. You can copy into a multimap or use a map of frequencies as suggested, but it does extra work that's not necessary because your array happens to be sorted. Here is a simple algorithm that counts the number of singular elements i.e. elements that appear only once in a sorted sequence.
int previous = v.front();
int current_count = 0;
int total_singular = 0;
for(auto n : v) {
if(previous == n) // check if same as last iteration
current_count++; // count the elements equal to current value
else {
if(current_count == 1) // count only those that have one copy for total
total_singular++;
previous = n;
current_count = 1; // reset counter, because current changed
}
}
if(current_count == 1) // check the last number
total_singular++;
You could also use count_if with a stateful lambda, but I don't think it'll make the algorithm any simpler.
If performance and memory doesn't matter to you, use std::map (or unordered version) for this task:
size_t count(const std::vector<int>& vec){
std::map<int,unsigned int> occurenceMap;
for (auto i : vec){
occurenceMap[i]++;
}
size_t count = 0U;
for (const auto& pair : occurenceMap){
if (pair.second == 1U){
count++;
}
}
return count;
}
with templates, it can be generalize to any container type and any containee type.
Use std::unique to count the unique entries(ct_u) and then user vector count on the original one (ct_o). Difference ct_o-ct_u would give the answer.
P.S.: this will only work if the identical entries are together in the original vector. If not, you may want to sort the vector first.
Using algorithm:
std::size_t count_unique(const std::vector<int>& v)
{
std::size_t count = 0;
for (auto it = v.begin(); it != v.end(); )
{
auto it2 = std::find_if(it + 1, v.end(), [&](int e) { return e != *it; });
count += (it2 - it == 1);
it = it2;
}
return count;
}
Demo
I have a vector of integers. which contains numbers. I want to count the number of occurrences of every number in this vector. So what will be the optimum way to do this. As I am new to Vectors please let me know any optimum method.
You can use a hash table, implemented by std::unordered_map. For example:
#include <unordered_map>
#include <vector>
void count_occurrence(std::unordered_map<int,int>& m, std::vector<int>& v){
for (auto itr = v.begin(); itr != v.end(); ++itr){
++m[*itr];
}
}
//...somewhere else
//you already have std::vector v filled
std::unordered_map<int,int> m;
count_occurrence(m, v);
//print the number of occurrences of 1
std::cout<<m[1]<<std::endl;
You could sort the elements of the vector
Iterate through vector
Store the current integer as x
Compare current index to previous index.
If they are equal, increment another variable as f
If they are unequal, begin the cycle again
This of course is by no means a step by step instruction, but it contains enough direction to get you going
To find the mode of a number of integers stored in an array/list/vector/etc. where v is the DS and num is the number of integers.
You may use the following technique:
Its simple and sober.
i = 0;
int m = 0, mode, c = 0, nc = 0;
while(i<num)
{
c = 0;
nc = v[i];
c++;
i++;
while(nc == v[i])
{
c++;
i++;
}
if(m < c)
{
m = c;
mode = nc;
}
}
Hey here is a trick question asked in class today, I was wondering if there is a way to find a unique number in a array, The usual method is to use two for loops and get the unique number which does not match with all the others I am using std::vectors for my array in C++ and was wondering if find could spot the unique number as I wouldn't know where the unique number is in the array.
Assuming that we know that the vector has at least three
elements (because otherwise, the question doesn't make sense),
just look for an element different from the first. If it
happens to be the second, of course, we have to check the third
to see whether it was the first or the second which is unique,
which means a little extra code, but roughly:
std::vector<int>::const_iterator
findUniqueEntry( std::vector<int>::const_iterator begin,
std::vector<int>::const_iterator end )
{
std::vector<int>::const_iterator result
= std::find_if(
next( begin ), end, []( int value) { return value != *begin );
if ( result == next( begin ) && *result == *next( result ) ) {
-- result;
}
return result;
}
(Not tested, but you get the idea.)
As others have said, sorting is one option. Then your unique value(s) will have a different value on either side.
Here's another option that solves it, using std::find, in O(n^2) time(one iteration of the vector, but each iteration iterates through the whole vector, minus one element.) - sorting not required.
vector<int> findUniques(vector<int> values)
{
vector<int> uniqueValues;
vector<int>::iterator begin = values.begin();
vector<int>::iterator end = values.end();
vector<int>::iterator current;
for(current = begin ; current != end ; current++)
{
int val = *current;
bool foundBefore = false;
bool foundAfter = false;
if (std::find(begin, current, val) != current)
{
foundBefore = true;
}
else if (std::find(current + 1, end, val) != end)
{
foundAfter = true;
}
if(!foundBefore && !foundAfter)
uniqueValues.push_back(val);
}
return uniqueValues;
}
Basically what is happening here, is that I am running ::find on the elements in the vector before my current element, and also running ::find on the elements after my current element. Since my current element already has the value stored in 'val'(ie, it's in the vector once already), if I find it before or after the current value, then it is not a unique value.
This should find all values in the vector that are not unique, regardless of how many unique values there are.
Here's some test code to run it and see:
void printUniques(vector<int> uniques)
{
vector<int>::iterator it;
for(it = uniques.begin() ; it < uniques.end() ; it++)
{
cout << "Unique value: " << *it << endl;
}
}
void WaitForKey()
{
system("pause");
}
int main()
{
vector<int> values;
for(int i = 0 ; i < 10 ; i++)
{
values.push_back(i);
}
/*for(int i = 2 ; i < 10 ; i++)
{
values.push_back(i);
}*/
printUniques(findUniques(values));
WaitForKey();
return -13;
}
As an added bonus:
Here's a version that uses a map, does not use std::find, and gets the job done in O(nlogn) time - n for the for loop, and log(n) for map::find(), which uses a red-black tree.
map<int,bool> mapValues(vector<int> values)
{
map<int, bool> uniques;
for(unsigned int i = 0 ; i < values.size() ; i++)
{
uniques[values[i]] = (uniques.find(values[i]) == uniques.end());
}
return uniques;
}
void printUniques(map<int, bool> uniques)
{
cout << endl;
map<int, bool>::iterator it;
for(it = uniques.begin() ; it != uniques.end() ; it++)
{
if(it->second)
cout << "Unique value: " << it->first << endl;
}
}
And an explanation. Iterate over all elements in the vector<int>. If the current member is not in the map, set its value to true. If it is in the map, set the value to false. Afterwards, all values that have the value true are unique, and all values with false have one or more duplicates.
If you have more than two values (one of which has to be unique), you can do it in O(n) in time and space by iterating a first time through the array and filling a map that has as a key the value, and value the number of occurences of the key.
Then you just have to iterate through the map in order to find a value of 1. That would be a unique number.
This example uses a map to count number occurences. Unique number will be seen only one time:
#include <iostream>
#include <map>
#include <vector>
int main ()
{
std::map<int,int> mymap;
std::map<int,int>::iterator mit;
std::vector<int> v;
std::vector<int> myunique;
v.push_back(10); v.push_back(10);
v.push_back(20); v.push_back(30);
v.push_back(40); v.push_back(30);
std::vector<int>::iterator vit;
// count occurence of all numbers
for(vit=v.begin();vit!=v.end();++vit)
{
int number = *vit;
mit = mymap.find(number);
if( mit == mymap.end() )
{
// there's no record in map for your number yet
mymap[number]=1; // we have seen it for the first time
} else {
mit->second++; // thiw one will not be unique
}
}
// find the unique ones
for(mit=mymap.begin();mit!=mymap.end();++mit)
{
if( mit->second == 1 ) // this was seen only one time
{
myunique.push_back(mit->first);
}
}
// print out unique numbers
for(vit=myunique.begin();vit!=myunique.end();++vit)
std::cout << *vit << std::endl;
return 0;
}
Unique numbers in this example are 20 and 40. There's no need for the list to be ordered for this algorithm.
Do you mean to find a number in a vector which appears only once? The nested loop if the easy solution. I don't think std::find or std::find_if is very useful here. Another option is to sort the vector so that you only need to find two consecutive numbers that are different. It seems overkill, but it is actually O(nlogn) instead of O(n^2) as the nested loop:
void findUnique(const std::vector<int>& v, std::vector<int> &unique)
{
if(v.size() <= 1)
{
unique = v;
return;
}
unique.clear();
vector<int> w = v;
std::sort(w.begin(), w.end());
if(w[0] != w[1]) unique.push_back(w[0]);
for(size_t i = 1; i < w.size(); ++i)
if(w[i-1] != w[i]) unique.push_back(w[i]);
// unique contains the numbers that are not repeated
}
Assuming you are given an array size>=3 which contains one instance of value A, and all other values are B, then you can do this with a single for loop.
int find_odd(int* array, int length) {
// In the first three elements, we are guaranteed to have 2 common ones.
int common=array[0];
if (array[1]!=common && array[2]!=common)
// The second and third elements are the common one, and the one we thought was not.
return common;
// Now search for the oddball.
for (int i=0; i<length; i++)
if (array[i]!=common) return array[i];
}
EDIT:
K what if more than 2 in an array of 5 are different? – super
Ah... that is a different problem. So you have an array of size n, which contains the common element c more than once, and all other elements exactly once. The goal is to find the set of non-common (i.e. unique) elements right?
Then you need to look at Sylvain's answer above. I think he was answering a different question, but it would work for this. At the end, you will have a hash map full of the counts of each value. Loop through the hash map, and every time you see a value of 1, you will know the key is a unique value in the input array.