why the c++ map changes the content position? - c++

I am trying to understand why the map changes the first element position to the fourth. When I print the map, the first added position goes to the fourth one.
#include <stdio.h>
#include <stdlib.h>
#include <map>
#include <iostream>
typedef std::map<double, unsigned int> A;
double fRand(double fMin, double fMax)
{
double f = (double)rand() / RAND_MAX;
return fMin + f * (fMax - fMin);
}
Here is the main function:
int main ()
{
int i, number;
double c;
number = 5;
A a;
for( i = 0 ; i < number ; i++ )
{
c = fRand(1, 9);
a[c];
std::cout << c<<"\n";
}
for(std::map<double, unsigned int>::const_iterator it = a.begin();
it != a.end(); ++it)
{
std::cout << it->first << " -> "<< it->second <<"\n";
}
return(0);
}
Answer : The first Element went to the fourth position
**7.7215** first
4.15506
7.26479
7.38752
8.29318
4.15506 -> 0
7.26479 -> 0
7.38752 -> 0
**7.7215 -> 0** fourth
8.29318 -> 0

std::map does manage the ordering of the elements according to the keys <, or a custom comparator. Thats actually the main feature of a std::map. If you don't want that then don't use a std::map. If you want to keep the elements in order of insertion you could use a std::vector< std::pair< double, unsigned int>> instead. Just beware of the different complexity guarantees you get from the different containers for eg insertion, lookup, iteration, removal, etc.

std::map stores elements in order of the key. It so happens that you generated numbers in a nearly ascending order.

Related

How to use std::map to retrive a std::vector<int> in c++?

What I'm trying to do is to somehow replicate in c++ the structure and functionality of a Perl Hash. For those not familiar with Perl, in such language a key can be used to point not only to a variable but other more complicated structures like arrays or vector or even other hashes. What I have tried so far is to create a vector of size 10 which is going to be my mapped_type
size_t size = 10;
std::vector<int> v1(size);
... Code that fills the v1...
and then create the map with v1 and fill it with values.
std::map<unsigned int, v1> x;
std::map<unsigned int,std::vector<int>>::iterator p=x.find(key);
if(p==m.end()) m[key]=v1;
Later, I plan to loop through all the keys and retrieve the vectors associated with those keys
for (std::map<unsigned int, std::vector<int>>::iterator p=x.begin(); p!=x.end(); ++p) {
...Do something with the p...
}
but of course these two last piece of code does not work at all.
I have successfully created other iterators like
std::map<unsigned int, unsigned int> x;
std::map<unsigned int, unsigned int>::iterator p=x.find(key);
if(p==m.end()) m[key]=1;
for (std::map<unsigned int, unsigned int>::iterator p=x.begin(); p!=x.end(); ++p) {
...Do something with the p...
}
but the mapped type is just a variable containing a single number or character. I want to be able to call and work with a complete vector using map (or any other C++ functionality). Is there a way to do this?
The problem is with the line:
std::map<unsigned int, v1> x;
You can't use v1 as the mapped type as v1 is not a type, it is an instance of a type. You can either write the type explicitly:
std::map<unsigned int, std::vector<int>> x;
Or use decltype:
std::map<unsigned int, decltype(v1)> x;
Also, if you want a hash map then you should use std::unordered_map rather than std::map, which is actually a red-black tree
With C++11 you might do something like this:
#include <iostream>
#include <map>
#include <vector>
int main() {
// type helpers
using MyVec = std::vector<int>;
using MyMap = std::map<unsigned int, MyVec>;
// create v1
MyVec v1 { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
// Or MyVec v1; v1.resize(10);
// create the map
MyMap x;
unsigned int key = 123;
auto it = x.find(key);
if (it == x.end())
x[key] = v1; // causes a COPY of the entire vector
for (const auto& idx: x) {
// idx.first is the key
// idx.second is the vector
std::cout << idx.first << ": ";
for (auto val: idx.second) {
// ^ no &, makes copies of values but they're ints so it's ok.
std::cout << val << " ";
}
std::cout << "\n";
}
}
Live demo: http://ideone.com/1r13zB
This give some clues as to how to iterate through a map. Using p->second gives you complete access to the int vector, and p->first gives you complete access to the map's key.
std::map<unsigned int, v1> x; // v1 -> typo?
std::map<unsigned int, std::vector<int> > x; // does this work?
I'm not sure if you're copying your code directly or providing pseudo code, but when you nest template arguments you have to insert a space into the final right angle brackets since >> is a C++ operator. I would imagine you would have found this out by getting a build error though.
I often use similar (nested) structures and i know this approach work, so consider the following, if this answers your question.
Your example is a map with int keys and vector values.
std::map< unsigned int, vector<int> > myMap;
// fill some vectors and put them in the myMap
for (int i = 0 ; i < 10 ; i++){
vector<int> myValueHolder;
for (int j = 0 ; j < 10 ; j++){
myValueHolder.push_back(10*i + j);
}
myMap[i] = myValueHolder;
}
// vectors are filled and pushed into the map with int keys
// retrieve vectors
for (std::map<unsigned int, vector<int> >::iterator iter = myMap.begin() ; iter != myMap.end() ; iter++ ){
unsigned int currentKey = iter->first; // first gets the key in the
vector<int> currentValue = iter->second; // second get the value
cout << "key is: " << currentKey << endl << "values are:" << endl;;
for (unsigned i = 0 ; i < currentValue.size() ; i++){ cout << currentValue.at(i) << endl; }
}
// or using any of the keys
vector<int> someRandomVectorFromTheMap = myMap[5];
for (unsigned i = 0 ; i < someRandomVectorFromTheMap.size() ; i++){ cout << someRandomVectorFromTheMap.at(i) << endl; }
I tested this and works as expected, hope answers what you ask. And i compiled it with this flag
COMPILE_AS_Cpp0X = $(-std=c++14) in case you wonder if it's 14 compatible
EDIT: I forgot to mention that what you wrote suggests you are not very familiar with the maps and their iterator methods first and second. This methods are very useful when iterating over map elements. Also usage of .find()
vector<int> someVectorTofind = myMap.find(8)
OR if you want to check if such key exists
vector<int> holder;
if (myMap.find(15) != myMap.end()) { holder = myMap.at(15)};
Check more map and vector examples is my suggestion
If you try to define the map with type of v1, you need decltype()
#include <string>
#include <vector>
#include <map>
int main( void )
{
size_t size = 10;
std::vector<int> v1(size);
std::map<unsigned int, decltype(v1) > x;
return 0;
}
Now I'm unsure what you're trying to do with a map<unsigned int, vector int> but the most likely case is that you really want a multimap<unsigned int, int>. With this you could do something like:
while(it != x.cend()) {
auto end = x.upper_bound(it->first);
cout << it->first << "\n\t";
for_each(it, end, [](const auto& i){ cout << i.second << '\t'; });
cout << endl;
it = end;
}
Live Example
Which would print your key on one line and an indented tab delineated list of the values associated with the key. There are lots of other clever things you can do with multimap, you can read about the functionality here: http://en.cppreference.com/w/cpp/container/multimap
One thing about your question, you allude to Perl's weak typing. Where you can assign an int to a variable and then turn around and assign a number to it. That's not possible in C++, because it's strongly typed: https://en.wikipedia.org/wiki/Strong_and_weak_typing
What you can do, and what I do in my example, is to assign a char's value to an the int value of the multimap. This assignment will cast the integral value of the character to an int. If you want the character back all you'll need to do is static_cast it back to a char.

Element at index in a std::set?

I've stumbled upon this problem: I can't seem to select the item at the index' position in a normal std::set. Is this a bug in STD?
Below a simple example:
#include <iostream>
#include <set>
int main()
{
std::set<int> my_set;
my_set.insert(0x4A);
my_set.insert(0x4F);
my_set.insert(0x4B);
my_set.insert(0x45);
for (std::set<int>::iterator it=my_set.begin(); it!=my_set.end(); ++it)
std::cout << ' ' << char(*it); // ups the ordering
//int x = my_set[0]; // this causes a crash!
}
Anything I can do to fix the issue?
It doesn't cause a crash, it just doesn't compile. set doesn't have access by index.
You can get the nth element like this:
std::set<int>::iterator it = my_set.begin();
std::advance(it, n);
int x = *it;
Assuming my_set.size() > n, of course. You should be aware that this operation takes time approximately proportional to n. In C++11 there's a nicer way of writing it:
int x = *std::next(my_set.begin(), n);
Again, you have to know that n is in bounds first.
Try this you will be able to use set in another way namely ordered_set
This is very much used in CP
Hope this is diff from all and will help you/someone!
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
#define ordered_set tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>
Now you can use
order_of_key (k) : Number of items strictly smaller than k .
find_by_order(k) : K-th element in a set (counting from zero). //This is what you need
[https://www.geeksforgeeks.org/ordered-set-gnu-c-pbds/][1]
A usual implementation of std::set is to use binary search trees, notably self-balancing binary search trees such as red-black trees
They don't give you constant time access to the n-th element. However, you seems to want the first. So try in C++11:
auto it = my_set.begin();
int first=0;
if (it != my_set.end()) first = *it;
There is no way you can access it in constant time.
But you can reach to any element in O(n) time.
E.g.
std::set<int>::iterator it;
it=my_set.begin();
advance(it,n);
cout<<*it;
I don't think std::set has any methods of doing this in better than O(n) time, but I recently made this data structure using a set and a Binary Index Tree that can do most things the std::set can do, but it can also get the index of an element in O(log n) time, as well as the element at a specific index in O((log n) * (log n)) time:
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <math.h>
#include <vector>
#include <queue>
#include <bitset>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
using namespace std;
typedef pair<int, int> pii;
typedef pair<pii, int> piii;
typedef long long ll;
typedef pair<ll, ll> pll;
#define max(n, m) ((n>m)?n:m)
#define min(n, m) ((n<m)?n:m)
#define f first
#define s second
struct ss
{
// binary index tree (to mark elements)
int bit[1000010]; // set this number to the max you will use
// set (to store the numbers in order)
set<int> nums;
// the maximum element in the set (NOTE: this data structure works with marking in the BIT array, but you can make this better by using an unordered set to store all values that could appear inside of the set, but this will increase runtime by a high constant factor)
int mx;
// constructor
ss(int maxEl)
{
mx = maxEl + 5;
}
int sum(int arr[], int idx)
{
int ans = 0;
idx ++;
if(idx > mx + 5) return -1;
while(idx > 0)
{
ans += arr[idx];
idx -= idx & (-idx);
}
return ans;
}
void update(int arr[], int idx, int val, int size)
{
idx ++;
while(idx <= size)
{
arr[idx] += val;
idx += idx & (-idx);
}
}
int bs(int l, int r, int idx)
{
int mid = (l + r) / 2;
if(l == r) return mid + 1;
if(l == r - 1)
{
if(sum(bit, r) == idx) return r + 1;
return r;
}
if(sum(bit, mid) <= idx) return bs(mid, r, idx);
return bs(l, mid - 1, idx);
}
// regular set functions
set<int>::iterator find(int num) { return nums.find(num); }
set<int>::iterator lower_bound(int num) { return nums.lower_bound(num); }
set<int>::iterator upper_bound(int num) { return nums.upper_bound(num); }
int size() { return (int)nums.size(); }
set<int>::iterator begin() { return nums.begin(); }
set<int>::iterator end() { return nums.end(); }
bool empty() { return nums.empty(); }
// slightly modified insert and erase functions to also mark stuff in BIT (still O(log n) though)
void insert(int num)
{
if(nums.find(num) == nums.end())
update(bit, num, 1, mx); // marks the element in the BIT if it doesn't already exist
nums.insert(num);
}
void erase(int num)
{
if(nums.find(num) != nums.end())
update(bit, num, -1, mx); // unmarks the element in the BIT if it exists in the set
nums.erase(num);
}
// gets index (0-indexed) of a specific element in O(log n), returns -1 if element not in set
int idx(int num)
{
if(nums.find(num) == nums.end())
return -1;
return sum(bit, num - 1);
}
// gets the iterator of the element at a specific index (0-indexed) in O((log n) * (log n)), returns end of set if idx is invalid
set<int>::iterator at(int idx)
{
if(idx < 0 || idx >= nums.size())
return nums.end();
return nums.find(bs(0, mx, idx));
}
};
int main()
{
ss test = ss(1000);
test.insert(1);
test.insert(3);
test.insert(5);
test.insert(1);
test.insert(9);
test.insert(1000);
cout << *test.at(1) << "\n";
test.erase(3);
cout << *test.at(1) << "\n";
cout << test.idx(1) << "\n";
cout << *test.at(-1) << "\n";
}
This set does have some flaws since it marks elements in the Binary Indexed Tree, so the elements cannot be negative or really big without some extra modifications, but it can still be helpful in some cases. Also, using an std::map or some other type of map could make the set work with negative numbers, big numbers, as well as other data types, but this would increase the runtime by a factor of O(log n) and I think you would have to know all the elements that could appear in the set beforehand so that you can store them in the correct order inside of the map.
EDIT: I just realized there is already a policy-based data structure called ordered-set that has the same functions as a set but can do the two operations (get element at index and get index of element) in O(log n). Read more here: https://www.geeksforgeeks.org/ordered-set-gnu-c-pbds/. This might not work in all compilers though
This is not a bug in the STD. There is no random access in a std::set. If you need random access by index, you can use std::vector
Sometimes there's a good reason for needing a set you can index into. I had to implement this functionality recently to support a legacy API which has functions to return the number of items, and the item at an index, so that the caller can enumerate the items.
My way of solving the problem is to use std::vector, and use std::equal_range to find and insert or delete items in the set. For example, inserting a new item into the set looks like this:
std:vector<std::string> my_set;
...
std::string new_item("test");
auto range = std::equal_range(my_set.begin(),my_set.end(),new_item);
if (range.first == range.second)
my_set.insert(range.first,new_item);
Deleting is very similar: use equal_range to find the item, and if range.first is not equal to range.second, delete that range.
i believe the most optimal way, especially if this indexing happens in a loop, is to convert to a vector.
auto my_vect = std::vector(my_set.begin(), my_set.end()); // O[n]
int output = my_vect[n]; // O[1]
std::set<int> my_set;
my_set.insert(0x4A);
my_set.insert(0x4F);
my_set.insert(0x4B);
my_set.insert(0x45);
int arr[my_set.size()];
set<int>::iterator it = my_set.begin();
for (int i = 0; i < my_set.size(); i++) {
arr[i] = *it;
it++;
}
cout << arr[0];
Edit: Edited code. You can't access set using index but the above method would provide an "index" i if you want to copy the elements from set into an array, provided you have created an array of sufficient size before hand.

C++ design: Given a list of scores of students, get the frequence of scores in order

I have a C++ interview question:
Given a list of scores of students, get the frequence of scores in order.
[use c++ container map]
My idea: put the list of scores into a map with score as key and frequency as value. Before add a key, search it. If a key is new, add it and set its freq as 1. If not, update its freq by ++1. O(nlgn)
Then, reverse key and value in a new map, in which set freq as key and its score as value. O(nlgn) because map do sorting itself.
memory: O(n)
It is not very efficient because I have to use 2 maps and do sorting 2 times.
Any comments or ideas are welcome.
Thanks
My code
#include <iostream>
#include <map>
#include <algorithm>
#include <time.h>
using namespace std;
const int M =10;
int A[M] ;
bool myFunc(pair<int, int> p1 , pair<double, int> p2)
{
//return p1.second > p2.second;
}
int scoreMap(int *A, const int& N)
{
if (A == NULL)
return 1;
map<int, int> map1;
map<int, int>::iterator itr;
int j = 0 ;
while(j < N)
{
int myKey = (A[j]) ;
itr = map1.find(myKey);
if (itr == map1.end())
{
map1.insert(pair<int, int>(myKey, 1));
}
else
{
++(itr->second);
}
++j;
}
// print map1
cout << "socre \t freq " << endl;
for(itr = map1.begin(); itr != map1.end(); ++itr )
{
cout << itr->first << "\t" << itr->second << endl;
}
// use multimap
multimap<int, int> map2;
multimap<int, int>::iterator itr2;
for (itr = map1.begin() ; itr != map1.end() ; ++itr )
{
map2.insert(pair<int, int>((*itr).second, (*itr).first)) ;
}
// print map2
cout << "after sort " << endl;
cout << "freq \t socre " << endl;
for(itr2 = map2.begin(); itr2 != map2.end(); ++itr2 )
{
cout << (double)(itr2->first)/N << "\t" << itr2->second << endl;
}
return 0;
}
int main()
{
int N = 10; int range=10;
for (int i = 0 ; i < M ; ++i)
{
srand(time(NULL)+rand());
//A[i] = rand()%range + (double)rand()/INT_MAX;
A[i] = rand()%range ; // + (double)rand()/INT_MAX;
// sleep(1);
}
scoreMap(A, M);
return 0;
}
// EOF
time O(nlgn), space O(n), are there more efficient solutions ?
thanks
Assume the score is a narrow integer range (1-100)
The score accumulation is stored in an array[score-range] of pairs, with your ++[score] idea.
The extraction of the frequencies is made by moving down the score list in an iterative fashion. O(N+M) N score range + M number of results/scores. Sort the result.
Sample pseudo:
const size_t MAX_SCORE = 100; // Min is assumed 0.
void scoreFrequencies(int [] scores, size_t N){
pair<int,int> score_counts[MAX_SCORES];
for(size_t i = 0; i < N; i++){
score_counts[i].first++;
score_counts[i].second = i;
}
sort( score_counts, score_counts+MAX_SCORES );
for(size_t score_decreasing = MAX_SCORES-1;
score_counts[score_decreasing].first!=0 && score_decreasing >=0;
score_decreasing--)
cout<< (score_counts.second) <<": " <<
( score_counts.first*1.0/N ) << endl;
}
As can be seen, by this method, the ordering of score_counts before sort( ... ) is unnecessary, so a map would not help.
Rather than use a map, simply define a histogram of all the scores using an array-type of std::pair<int, int>. One member will be the scores, and the other the frequency. Initially the scores will be the same value as the array-index they are at, but you should only do this initialization when you try to access each specific score index, otherwise you will end up initializing a bunch of scores that don't exist. Then sort the array based on frequency of the scores after you've filled in the histogram for the scores themselves. Since the scores in the histogram basically act like very simple hash-lookup, the overall time should be very fast (... O(1) for each score-lookup and associated frequency increment, and O(n log n) for sorting).
Here's a little bit of code to help explain:
std::pair<int, int> scores[SCORE_RANGE] = {0}; //zero-out the entire array
//...iterate through your score data
for (int i=0; i < SCORE_DATA_SIZE; i++)
{
int score_val = raw_score_data[i];
if (scores[score_val].first == 0)
{
scores[score_val].first = score_val;
}
scores[score_val].second++;
}
//now sort your scores array based on the frequency which is stored in the second
//member of the std::pair structure
My idea: put the list of scores into a map with score as key and frequency as value.
So far, so good,
Before add a key, search it. If a key is new, add it and set its freq as 1. If not, update its freq by ++1. O(nlgn)
Checking for the existing of a map entry is unneeded and wasteful. The entry will spring into existence (with a default value) the fist time it is referenced. Just do this:
++scoreMap[score];
This doesn't change your big-O, but it does make it go faster.
Then, reverse key and value in a new map, in which set freq as key and its score as value. O(nlgn) because map do sorting itself.
You can't use a new map, because the score won't necessarily be unique. But you could use set<pair>.
In honor of Ubuntu 11.10 (and g++ 4.6!) installing correctly on my PC, here is how one could use lambdas to solve this:
map<int, int> scoreMap;
for_each(istream_iterator<int>(cin), istream_iterator<int>(),
[&scoreMap] (int i) { ++scoreMap[i]; } );
set<pair<int,int>> freqMap;
for_each(scoreMap.begin(), scoreMap.end(),
[&freqMap] (const pair<int,int>& p) {
freqMap.insert(make_pair(p.second, p.first));
} );
for_each(freqMap.rbegin(), freqMap.rend(),
[] (const pair<int,int>& p) {
cout << p.second << "/" << p.first << "\n";
} );

C++ sort on vector using function object

I'm trying to sort a vector v1 using another vector v2. I can't wrap my head around this error:
terminate called after throwing an instance of 'std::out_of_range'
what(): vector::_M_range_check
Abort trap
while running this code:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Comp
{
public:
Comp(vector<double>& inVec): _V(inVec) {}
bool operator()(int i, int j) {return (_V.at(i)<_V.at(j));}
private:
vector<double> _V;
};
int main(int argc, char** argv)
{
double x1[] = {90.0, 100.0, 80.0};
double x2[] = {9.0, 3.0, 1.0};
vector<double> v1(x1,x1+3);
vector<double> v2(x2,x2+3);
sort(v1.begin(), v1.end(), Comp(v2)); // sort v1 according to v2
for(unsigned int i=0; i<v1.size(); i++)
{
cout << v1.at(i) << " " << v2.at(i) << endl;
}
return 0;
}
v1 and v2 are of the same size. Why the out_of_range error?
Thanks in advance for any pointers.
I believe that your problem is in this line:
bool operator()(int i, int j) {return (_V.at(i)<_V.at(j));}
The problem is that when the std::sort algorithm uses a custom callback, it passes in the actual values stored in the vector at particular locations, not the indices of those locations within the vector. As a result, when you call
sort(v1.begin(), v1.end(), Comp(v2)); // sort v1 according to v2
The Comp comparator you've written will be getting passed as parameters the values stored in the v1 vector and will then try indexing at those positions into the v2 vector. Since the values in v1 are larger than the size of v2, the call to _V.at(i) will cause an out_of_range exception to be thrown.
If you want to sort the two ranges with respect to one another, you'll need to adopt a different approach. I'm not aware of a straightforward way of doing this, but I'll let you know if I think of one.
Size of v1 is just 3, but you're using each value of v2 as index of v1. And as v2 has one value 9 which is greater than the size of v1, that is what gives std::out_of_range error in here:
bool operator()(int i, int j) {return (_V.at(i)<_V.at(j));}
std::vector::at function gives std::out_of_range exception of the index passed to it as argument is greater than the size of vector. That is, the index must be less than vector::size().
Ok, now you're probably aware of the fact, that i and j are actual values held in vector rather than indices. There is a good reason for that: sorting is all about values, not indexes. Note you're passing iterators to sort method, so there is no way it can extract index for you. Of course, you could get index relative to first iterator, but there is no reason for doing this.
However, let's be insane for awhile and imagine you would get indices rather than values in your comparator. Assume that your code does what you want and let's think about following scenario:
v1 = {20,10}; v2 = {2,1}
I secretly assume you want the following output:
v1 = {10, 20}
right? Now imagine I'm a sorting function you're calling and I do following steps:
v2[0] < v2[1] is false, so swap(&v1[0], &v1[1])
It's sorted, isn't it? But wait, I'm a crazy sorting function, so I want to make sure it's sorted, so I do the following:
v2[0] < v2[1] is false, swap(&v1[0], &v1[1])
And again:
v2[0] < v2[1] is false, swap(&v1[0], &v1[1])
and again, again, again...
Can you see a problem? Sorting function has some requirements and for sure you're breaking fundamental one.
I suspect you need completely different container (maybe std::map with keys from vec1 and values from vec2) or at least something like vector< pair<double, double> >, so you can easily sort by either first or second value. If not, consider creating vector with values in range [0, v2.size()), sorting it using your comparator (values are equal to indices, so will be all right) and then print correct values from v1. This code works fine:
vector<size_t> indices;
for(size_t i =0; i < v1.size(); ++i)
{
indices.push_back(i);
}
// yes, it works using your original comparator
sort(indices.begin(), indices.end(), Comp(v2));
for(size_t i =0; i < indices.size(); ++i)
{
cout << v1.at(indices[i]) << " " << v2.at(indices[i]) << endl;
}
Like said in other answers, the problem is that the sort algorithm passes the actual values to compare rather than indices.
Here is how you can solve it:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<double, double> Zipped; // Represent an element of two lists
// "zipped" together
// Compare the second values of two pairs
bool compareSeconds ( Zipped i, Zipped j )
{
return i.second < j.second;
}
int main ( int argc, char **argv )
{
double x1[] = { 90, 100, 80 };
double x2[] = { 9, 3, 1 };
vector<double> v1(x1, x1 + 3);
vector<double> v2(x2, x2 + 3);
vector<Zipped> zipped(v1.size()); // This will be the zipped form of v1
// and v2
for ( int i = 0; i < zipped.size(); ++i )
{
zipped[i] = Zipped(v1[i], v2[i]);
}
sort(zipped.begin(), zipped.end(), &compareSeconds);
for ( int i = 0; i < zipped.size(); ++i )
{
cout << zipped[i].first << " " << zipped[i].second << endl;
}
for ( int i = 0; i < v1.size(); ++i )
{
v1[i] = zipped[i].first;
}
// At this point, v1 is sorted according to v2
return 0;
}

Finding Frequency of numbers in a given group of numbers

Suppose we have a vector/array in C++ and we wish to count which of these N elements has maximum repetitive occurrences and output the highest count. Which algorithm is best suited for this job.
example:
int a = { 2, 456, 34, 3456, 2, 435, 2, 456, 2}
the output is 4 because 2 occurs 4 times. That is the maximum number of times 2 occurs.
Sort the array and then do a quick pass to count each number. The algorithm has O(N*logN) complexity.
Alternatively, create a hash table, using the number as the key. Store in the hashtable a counter for each element you've keyed. You'll be able to count all elements in one pass; however, the complexity of the algorithm now depends on the complexity of your hasing function.
Optimized for space:
Quicksort (for example) then iterate over the items, keeping track of largest count only.
At best O(N log N).
Optimized for speed:
Iterate over all elements, keeping track of the separate counts.
This algorithm will always be O(n).
If you have the RAM and your values are not too large, use counting sort.
A possible C++ implementation that makes use of STL could be:
#include <iostream>
#include <algorithm>
#include <map>
// functor
struct maxoccur
{
int _M_val;
int _M_rep;
maxoccur()
: _M_val(0),
_M_rep(0)
{}
void operator()(const std::pair<int,int> &e)
{
std::cout << "pair: " << e.first << " " << e.second << std::endl;
if ( _M_rep < e.second ) {
_M_val = e.first;
_M_rep = e.second;
}
}
};
int
main(int argc, char *argv[])
{
int a[] = {2,456,34,3456,2,435,2,456,2};
std::map<int,int> m;
// load the map
for(unsigned int i=0; i< sizeof(a)/sizeof(a[0]); i++)
m [a[i]]++;
// find the max occurence...
maxoccur ret = std::for_each(m.begin(), m.end(), maxoccur());
std::cout << "value:" << ret._M_val << " max repetition:" << ret._M_rep << std::endl;
return 0;
}
a bit of pseudo-code:
//split string into array firts
strsplit(numbers) //PHP function name to split a string into it's components
i=0
while( i < count(array))
{
if(isset(list[array[i]]))
{
list[array[i]]['count'] = list + 1
}
else
{
list[i]['count'] = 1
list[i]['number']
}
i=i+1
}
usort(list) //usort is a php function that sorts an array by its value not its key, Im assuming that you have something in c++ that does this
print list[0]['number'] //Should contain the most used number
The hash algorithm (build count[i] = #occurrences(i) in basically linear time) is very practical, but is theoretically not strictly O(n) because there could be hash collisions during the process.
An interesting special case of this question is the majority algorithm, where you want to find an element which is present in at least n/2 of the array entries, if any such element exists.
Here is a quick explanation, and a more detailed explanation of how to do this in linear time, without any sort of hash trickiness.
If the range of elements is large compared with the number of elements, I would, as others have said, just sort and scan. This is time n*log n and no additional space (maybe log n additional).
THe problem with the counting sort is that, if the range of values is large, it can take more time to initialize the count array than to sort.
Here's my complete, tested, version, using a std::tr1::unordered_map.
I make this approximately O(n). Firstly it iterates through the n input values to insert/update the counts in the unordered_map, then it does a partial_sort_copy which is O(n). 2*O(n) ~= O(n).
#include <unordered_map>
#include <vector>
#include <algorithm>
#include <iostream>
namespace {
// Only used in most_frequent but can't be a local class because of the member template
struct second_greater {
// Need to compare two (slightly) different types of pairs
template <typename PairA, typename PairB>
bool operator() (const PairA& a, const PairB& b) const
{ return a.second > b.second; }
};
}
template <typename Iter>
std::pair<typename std::iterator_traits<Iter>::value_type, unsigned int>
most_frequent(Iter begin, Iter end)
{
typedef typename std::iterator_traits<Iter>::value_type value_type;
typedef std::pair<value_type, unsigned int> result_type;
std::tr1::unordered_map<value_type, unsigned int> counts;
for(; begin != end; ++begin)
// This is safe because new entries in the map are defined to be initialized to 0 for
// built-in numeric types - no need to initialize them first
++ counts[*begin];
// Only need the top one at this point (could easily expand to top-n)
std::vector<result_type> top(1);
std::partial_sort_copy(counts.begin(), counts.end(),
top.begin(), top.end(), second_greater());
return top.front();
}
int main(int argc, char* argv[])
{
int a[] = { 2, 456, 34, 3456, 2, 435, 2, 456, 2 };
std::pair<int, unsigned int> m = most_frequent(a, a + (sizeof(a) / sizeof(a[0])));
std::cout << "most common = " << m.first << " (" << m.second << " instances)" << std::endl;
assert(m.first == 2);
assert(m.second == 4);
return 0;
}
It wil be in O(n)............ but the thing is the large no. of array can take another array with same size............
for(i=0;i
mar=count[o];
index=o;
for(i=0;i
then the output will be......... the element index is occured for max no. of times in this array........
here a[] is the data array where we need to search the max occurance of certain no. in an array.......
count[] having the count of each element..........
Note : we alrdy knw the range of datas will be in array..
say for eg. the datas in that array ranges from 1 to 100....... then have the count array of 100 elements to keep track, if its occured increament the indexed value by one........
Now, in the year 2022 we have
namespace aliases
more modern containers like std::unordered_map
CTAD (Class Template Argument Deduction)
range based for loops
using statment
the std::ranges library
more modern algorithms
projections
structured bindings
With that we can now write:
#include <iostream>
#include <vector>
#include <unordered_map>
#include <algorithm>
namespace rng = std::ranges;
int main() {
// Demo data
std::vector data{ 2, 456, 34, 3456, 2, 435, 2, 456, 2 };
// Count values
using Counter = std::unordered_map<decltype (data)::value_type, std::size_t> ;
Counter counter{}; for (const auto& d : data) counter[d]++;
// Get max
const auto& [value, count] = *rng::max_element(counter, {}, &Counter::value_type::second);
// Show output
std::cout << '\n' << value << " found " << count << " times\n";
}