C++ Loop through first K elements of unordered_map - c++

I have an unordered_map that stores counts of integers. I want to loop through the map, but instead of fetching all the entries, I only wish to get the first K.It is guaranteed that map has more than K entries.
I'm running into issues when I do the following:
unordered_map<int, int> u_map;
// Logic to populate the map
for(auto it=u_map.begin(); it!=u_map.begin()+2; it++)
cout<<it->first<<" "<<it->second<<endl;
The expression u_map.begin()+2 is causing the issue.
So is it possible to get only the first K entries of a map using for_each loop in C++?

If you can use C++20, then views::take would be a choice.
#include <unordered_map>
#include <ranges>
#include <iostream>
int main() {
std::unordered_map<int, int> u_map;
for (auto [key, value] : u_map | std::views::take(2))
std::cout << key << " " << value << "\n";
}
Alternative for pre-C++20, using std::next:
std::unordered_map<int, int> u_map;
auto end = std::next(u_map.begin(), 2);
for (auto it = u_map.begin(); it != end; ++it)
std::cout << it->first << " " << it->second << "\n";

I only wish to get the first K
Note from std::unordered_map documentation
an unordered_map object makes no guarantees on which specific element is considered its first element.
This essentially means that there is no guarantee that you will iterate over the elements in the inserted order.
For iterating over the elements of the map you can use:
int count = 0;
for (auto& it: u_map) {
/* some code here like you can keep a count variable that will check if it
reaches the number K and then break the loop. But remember that it is
**not** guaranteed that the elements you will get will be in inserted order.*/
if(count < K)
{
cout<<it.first<<" "<<it.second<<endl;
}
else
{
break;
}
++count;
}
Working example
#include <iostream>
#include <unordered_map>
using namespace std;
int main()
{
std::unordered_map<std::string, std::string> u_map = {
{"RED","#FF0000"},
{"GREEN","#00FF00"},
{"BLUE","#0000FF"},{"PURPLE","#0F00FF"},{"WHITE","#0000RF"},{"ORANGE","#F000FF"}
};
int K = 3;
int count = 0;
for (auto& it: u_map)
{
if(count < K)
{
cout<<it.first<<" "<<it.second<<endl;
}
else
{
break;
}
++count;
}
return 0;
}

Related

Ordering in unordered_map in C++

So I have an array as :
arr[] = {5, 2,4,2,3,5,1};
How can I insert them in this order with the number of times they occur in unordered_map?
#include<bits/stdc++.h>
using namespace std;
void three_freq(int arr[], int n){
unordered_map<int, int> m;
for(int i=0;i<n;i++){
m[arr[i]]++;
}
for(auto itr = m.begin(); itr != m.end(); itr++){
cout<<itr->first<<":"<<itr->second<<"\n";
}
}
int main(){
int arr[] = {5, 2,4,2,3,5,1};
int n = sizeof(arr)/ sizeof(arr[0]);
three_freq(arr, n);
return 0;
}
Using the code above I am getting output as :
1:1
3:1
4:1
5:2
2:2
But I want the output to be in same order as the element occur in array.
Example:
5:2
2:2
4:1
3:1
1:1
If you don't care about efficiency (that much), then you can just change the for loop which is printing the output.
for(int i=0; m.size(); i++) {
auto it = m.find(arr[i]);
if (it != m.end()) {
cout<<arr[i]<<":"<<it->second<<"\n";
m.erase(it);
}
}
The quite efficient way is traversing the original array and resetting the counters after print.
for (int i = 0; i < n; ++i) {
if (m[a[i]]) != 0) {
std::cout <<arr[i] << ":" << m[a[i]] << std::endl;
m[a[i]] = 0;
}
}
You need unordered_map to do the counting efficiently, so keep that.
When printing out according to the order in another container, it makes sense to simply iterate over that other container for your output loop.
(Note that this is a completely separate operation, so it could have been a different function.)
// loop over the original array (recommend std::vector)
// exit early if done (stole from fadedreamz)
for (int index = 0; !m.empty(); ++index) {
int number = arr[index];
// check to see if we need to print this number
// use contains if you have c++20
if (m.count(number)) {
std::cout << number << ":" << m[number] << std::endl;
m.erase(number); // print only once by deleting the entry
}
}

looping over set inside map in cpp

I am getting error in for loop ,compiler saying that 's' must have a pointer type (in s->second.begin()).I just want to iterate over set below.
vector<vector<int>> verticalTraversal(TreeNode* root) {
map<int, map<int, set<int>>> mep;
solve(root, 0, 0, mep);
vector<vector<int>> result;
for (auto p : mep) {
vector<int> temp;
map<int,set<int>> s = p.second;
for (auto ity = s->second.begin(); ity != s->second.end(); ity++) {
//getting error here in s->second.begin()
}
//reverse(temp.begin(), temp.end());
result.push_back(temp);
}
return result;
}
So here's some code that iterates through each set (and prints out all the integers).
vector<vector<int>> verticalTraversal() {
map<int, map<int, set<int>>> mep;
solve(root, 0, 0, mep);
vector<vector<int>> result;
for (map<int, map<int, set<int>>>::iterator i = mep.begin(); i != mep.end(); ++i) {
for (map<int, set<int>>::iterator j = i->second.begin(); j != i->second.end(); ++j) {
for (set<int>::iterator k = j->second.begin(); k != j->second.end(); ++k) {
cout << *k << '\n';
}
}
}
return result;
}
First thing you should notice is that there are three loops not two, since you have a set inside a map inside a map you have three levels to iterate though (not two as in your code).
Second thing is I removed all auto and all range based loops and been completely explicit about the types involved. Hopefully that makes the code easier to understand.
Once you've understood it you can put the auto back in if you like, or even replace the loops with range based loops.
The best thing to do is to write a small program, and then get an idea/experiment with the smaller program to understand the basic concepts.
Here is an example:
#include <map>
#include <set>
#include <iostream>
int main()
{
std::map<int, std::map<int, std::set<int>>> mep;
mep.insert({0, {{0, {0,1,2,3}}}}); // populate the map
mep.insert({1, {{1, {1,2,3,4,5}}}}); // populate the map
// traverse each set in the mep map
for (auto p : mep)
{
std::map<int, std::set<int>> s = p.second;
for (auto ity = s.begin(); ity != s.end(); ity++)
{
std::set<int>& theSet = ity->second;
for (auto& setV : theSet )
std::cout << setV << " ";
std::cout << "\n";
}
}
}
Output:
0 1 2 3
1 2 3 4 5
Also note that you really should be using references to the data within the map instead of copying them by value. Things like this:
for (auto p : mep)
and
std::map<int, std::set<int>> s = p.second;
incurs a copy. Rather, you should do this:
for (auto& p : mep)
...
std::map<int, std::set<int>>& s = p.second;

What's the most efficient way to print all elements of vector in ascending order till it's empty without duplicates?

I'm supposed to:
Print vector elements sorted without repetition.
Delete the elements that are printed from vector.
Repeat the the previous steps until vector is empty.
But it seems that my code takes more time so, I seek for optimisation. I've tried to do this task with std::vector and std::set.
Here is my approach:
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;
int main () {
int n;
cin >> n;
vector<int> v(n);
set<int> st;
for (int i = 0; i < n; i++) {
cin >> v[i];
}
while (!v.empty()) {
for (int i = 0; i < v.size(); i++)
st.insert(v[i]);
for (auto x : st) {
cout << x << ' ';
auto it = find(v.begin(), v.end(), x);
if (it != v.end())
v.erase(it);
}
st.clear();
cout << "\n";
}
return 0;
}
For example input is like:
7
1 2 3 3 2 4 3
Output gonna be like this:
1 2 3 4
2 3
3
You might use std::map instead of std::vector/std::set to keep track of numbers:
#include <iostream>
#include <map>
int main () {
map<int, int> m;
int size;
std::cin >> size;
for (int i = 0; i != size; i++) {
int number;
std::cin >> number;
++m[number];
}
while (!m.empty()) {
for (auto it = m.begin(); it != m.end(); /*Empty*/) {
const auto number = it->first;
auto& count = it->second;
std::cout << number << ' ';
if (--count == 0) {
it = m.erase(it);
} else {
++it;
}
}
std::cout << "\n";
}
}
Complexity is now O(n log(n)) instead of O(n²) (with lot of internal allocations).
Due to it overwriting the elements expected to be deleted, std::unique won't be much use for this problem. My solution:
std::sort(v.begin(), v.end());
while (!v.empty())
{
int last = v.front();
std::cout << last << " ";
v.erase(v.begin());
for (auto it = v.begin(); it != v.end(); /* no-op */)
{
if (*it == last)
{
++it;
}
else
{
last = *it;
std::cout << last << " ";
it = v.erase(it);
}
}
std::cout << std::endl;
}
You could probably improve performance further by reversing the sorting of the vector, and then iterating through backwards (since it's cheaper to delete from closer to the back of the vector), but that would complicate the code further, so I'll say "left as an exercise for the reader".
You can use std::map
auto n = 0;
std::cin >> n;
std::map<int, int> mp;
while (--n >= 0) {
auto i = 0;
std::cin >> i;
mp[i] += 1;
}
while (!mp.empty()) {
for (auto& it: mp) {
std::cout << it.first << " ";
it.second--;
}
for (auto it = mp.begin(); it != mp.end(); ++it) {
if (it->second == 0) mp.erase(it);
}
std::cout << "\n";
}
without any erase
auto n = 0;
std::cin >> n;
std::map<int, int> mp;
while (--n >= 0) {
auto i = 0;
std::cin >> i;
mp[i] += 1;
}
auto isDone = false;
while (!isDone) {
isDone = true;
for (auto& it: mp) {
if (it.second > 0) std::cout << it.first << " ";
if (--it.second > 0) isDone = false;
}
std::cout << "\n";
}
Here is a solution using sort and vector. It uses a second vector to hold the unique items and print them.
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
int main()
{
std::vector<int> v{1,2,3,3,2,4,3};
std::sort(v.begin(), v.end());
std::vector<int>::iterator vit;
while(!v.empty()){
std::vector<int> printer;
std::vector<int>::iterator pit;
vit = v.begin();
while (vit != v.end()){
pit = find(printer.begin(), printer.end(), *vit);
if (pit == printer.end()){
printer.push_back(*vit);
vit = v.erase(vit);
} else {
++vit;
}
}
std::copy(printer.begin(), printer.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
}
}
Output:
1 2 3 4
2 3
3
It's not clear (at least to me) exactly what you're talking about when you mention "efficiency". Some people use it to refer solely to computational complexity. Others think primarily in terms of programmer's time, while still others think of overall execution speed, regardless of whether that's obtained via changes in computational complexity, or (for one example) improved locality of reference leading to better cache utilization.
So, with that warning, I'm not sure whether this really improves what you care about or not, but it's how I think I'd do the job anyway:
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
// preconditions: input range is sorted
template <class BidiIt>
BidiIt partition_unique(BidiIt begin, BidiIt end) {
auto pivot = end;
for (auto pos = begin; pos != pivot; ++pos) {
auto mid = std::next(pos);
for ( ; mid < pivot && *mid == *pos; ++mid, --pivot)
;
std::rotate(std::next(pos), mid, end);
}
return pivot;
}
template <class It>
void show(It b, It e, std::ostream &os) {
while (b != e) {
os << *b << ' ';
++b;
}
os << '\n';
}
int main() {
std::vector<int> input{ 1, 2, 3, 3, 2, 4, 3 };
std::sort(input.begin(), input.end());
auto begin = input.begin();
auto pos = begin;
while ((pos = partition_unique(begin, input.end())) != input.end()) {
show(begin, pos, std::cout);
begin = pos;
}
show(begin, input.end(), std::cout);
}
I'm not really sure it's possible to improve the computational complexity much over what this does (but it might be--I haven't thought about it enough to be sure one way or the other). Compared to some versions I see posted already, there's a decent chance this will improve overall speed (e.g., since it just moves things around inside the same vector, it's likely to get better locality than those that copy data from one vector to another.
The code is in java but the idea remains the same.
At first, I sort the array. Now, the idea is to create buckets.
This means that each line of sorted elements is like a bucket. So, find the count of each element. Now, put that element into each bucket, count number of times. If it so happens that bucket size is less, create a new bucket and add the current element to it.
In the end, print all buckets.
Time Complexity is O(nlog(n)) for sorting and O(n) for the buckets since you have to visit each and every element to print it. So, it's O(nlog(n)) + O(n) = O(nlog(n)) asymptotically.
Code:
import java.util.*;
public class GFG {
public static void main(String[] args){
int[] arr1 = {1,2,3,3,2,4,3};
int[] arr2 = {45,98,65,32,65,74865};
int[] arr3 = {100,100,100,100,100};
int[] arr4 = {100,200,300,400,500};
printSeries(compute(arr1,arr1.length));
printSeries(compute(arr2,arr2.length));
printSeries(compute(arr3,arr3.length));
printSeries(compute(arr4,arr4.length));
}
private static void printSeries(List<List<Integer>> res){
int size = res.size();
for(int i=0;i<size;++i){
System.out.println(res.get(i).toString());
}
}
private static List<List<Integer>> compute(int[] arr,int N){
List<List<Integer>> buckets = new ArrayList<List<Integer>>();
Arrays.sort(arr);
int bucket_size = 0;
for(int i=0;i<N;++i){
int last_index = i;
if(bucket_size > 0){
buckets.get(0).add(arr[i]);
}else{
buckets.add(newBucket(arr[i]));
bucket_size++;
}
for(int j=i+1;j<N;++j){
if(arr[i] != arr[j]) break;
if(j-i < bucket_size){
buckets.get(j-i).add(arr[i]);
}else{
buckets.add(newBucket(arr[i]));
bucket_size++;
}
last_index = j;
}
i = last_index;
}
return buckets;
}
private static List<Integer> newBucket(int value){
List<Integer> new_bucket = new ArrayList<>();
new_bucket.add(value);
return new_bucket;
}
}
OUTPUT
[1, 2, 3, 4]
[2, 3]
[3]
[32, 45, 65, 98, 74865]
[65]
[100]
[100]
[100]
[100]
[100]
[100, 200, 300, 400, 500]
This is what i came up with:
http://coliru.stacked-crooked.com/a/b3f06693a74193e5
The key idea:
sort vector
print by iterating. just print a value if it differs from last printed
remove unique elements. i have done this with what i called inverse_unique. the std library comes with an algorithm called unique, which will remove all duplicates. i inverted this so that it will just keep all dublicates.
so we have no memory allocation at all. i cant see how one could make the algorithm more efficient. we are just doing the bare minimum and its exactly done the way a human thinks about.
i tested it with several combinations. hope its bug free ;-P
code:
#include <iostream>
#include <algorithm>
#include <vector>
template<class ForwardIt>
ForwardIt inverse_unique(ForwardIt first, ForwardIt last)
{
if (first == last)
return last;
auto one_ahead = first+1;
auto dst = first;
while(one_ahead != last)
{
if(*first == *one_ahead)
{
*dst = std::move(*first);
++dst;
}
++first;
++one_ahead;
}
return dst;
}
void print_unique(std::vector<int> const& v)
{
if(v.empty()) return;
// print first
std::cout << v[0] << ' ';
auto last_printed = v.cbegin();
// print others
for(auto it = std::next(std::cbegin(v)); it != std::cend(v); ++it)
{
if(*it != *last_printed)
{
std::cout << *it << ' ';
last_printed = it;
}
}
std::cout << "\n";
}
void remove_uniques(std::vector<int> & v)
{
auto new_end = inverse_unique(std::begin(v), std::end(v));
v.erase(new_end, v.end());
}
int main ()
{
std::vector<int> v = {1, 2, 3, 3, 2, 4, 3};
std::sort(std::begin(v), std::end(v));
while (!v.empty())
{
print_unique(v);
remove_uniques(v);
}
return 0;
}
Edit: updated inverse_unique function. should be easy to understand now.
Half baked at http://coliru.stacked-crooked.com/a/c45df1591d967075
Slightly modified counting sort.
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <map>
int main() {
std::vector<int> v{1,2,3,3,2,4,3};
std::map<int, int> map;
for (auto x : v)
++map[x];
while(map.size()) {
for(auto pair = map.begin(); pair != map.end(); ) {
std::cout << pair->first << ' ';
if (!--pair->second)
pair = map.erase(pair);
else
++pair;
}
std::cout << "\n";
}
return 0;
}

How to print frequency of each letter in a string in descending order c++?

#include <iostream>
#include <string>
using namespace std;
int main () {
int cnt[26] {};
char alpha[26];
string s = "abcdefffggghiii";
for (int i = 0; i < s.length(); i++) {
cnt[s[i] - 'a']++;
}
for (int i = 'a'; i <= 'z'; i++) {
alpha[i - 'a'] = i;
}
for (int i = 0; i < 26; i++) {
if (cnt[i]) {
cout << alpha[i] << " " << cnt[i] << endl;
}
}
return 0;
}
I wanted to print the frequencies of each letter in the string in descending order. I've thought to sort the cnt array and print from 25 to 0 but it will only print the frequencies with wrong letter. How can I fix it to print for example i 3 and so on in descending order?
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
// Create result container
auto x = vector<pair<char, int>>();
std::string s = "abcdefffggghiii";
for (auto& l : s) {
// Find the item that corresponds to letter
auto pLetter =
find_if(x.begin(), x.end(), [&l](pair<char, int> &arg) {
return arg.first == l;
});
if (pLetter != x.end())
pLetter->second++; // If item corresponding to letter is found, increment count
else {
x.push_back(make_pair(l, 1)); // Otherwise, create a new result entry
}
}
// Sort results by count in descending order
std::sort(x.begin(), x.end(),
[](auto &left, auto &right) { return left.second > right.second; });
for (auto i = x.begin(); i != x.end(); ++i)
std::cout << i->first << ':' << i->second << '\n';
}
Produces
f:3
g:3
i:3
a:1
b:1
c:1
d:1
e:1
h:1
You can run it here. This uses C++14 lambdas for the find_if and sort predicates. This solution is very similar to #Retired Ninja's, except that the result vector contains items only for those letters that have non-zero counts. This means that it is extendable to wstrings without the need for a large result vector.
Here's how I might do it. You just need to keep the letter and the count together.
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
struct LetterFreq
{
char letter;
int freq;
};
int main()
{
std::vector<LetterFreq> cnt(26);
for (size_t i = 0; i < cnt.size(); ++i)
{
cnt[i].freq = 0;
cnt[i].letter = static_cast<char>(i) + 'a';
}
std::string s = "abcdefffggghiii";
for (auto& l : s)
{
cnt[l - 'a'].freq++;
}
std::sort(cnt.begin(), cnt.end(), [](const LetterFreq& lhs, const LetterFreq& rhs)
{
return lhs.freq > rhs.freq;
});
for (auto& item : cnt)
{
if (item.freq == 0)
{
break;
}
std::cout << item.letter << " : " << item.freq << "\n";
}
return 0;
}
This is simple if all you have it lowercase ASCII letters. For more complicated input you can use the same idea of the letter and count in a struct, but you'd either want to increase the size of the vector to 256 to keep track of all possibilities, or use something like an unordered map to only store used symbols and then copy them out into a container you can sort to display them. You could also use parallel arrays and while sorting swap the letter positions at the same time you're swapping the counts. There are many ways to handle this.
You could use pairs, but it looks like you're doing this with more basic types. In that case you might have to use nested loops. Keep finding the highest frequency character, print it, and then set its frequency to -1 to indicate that you've processed it already.

String vector's sort compare and delete

In C++ I have
vector < vector <string> > Kblist;
Inside Kblist, there are many clause, and numbers of clauses=kblist.size(); and every clause in side Kblist is a string-type vector, and every word in sentence is split inside Kblist[i].
What is the fastest way to find the sentence in same words like one in "I love you" and the other in "you love i" and delete these two sentence from Kblist, My code might be work run, but I think it is too slow because of to many circulate. So I wonder is there any better solution which is fast like using sort, clause1==clause2 or other approach.
for (int a=0; a<KBlist.size(); a++){
for (int b=a+1; b<KBlist.size(); b++){
int checksize=0;
if (KBlist[a].size()==KBlist[b].size()) {
for (int c=0; c<KBlist[a].size(); c++){
for (int d=0; d<KBlist[b].size(); d++){
if (KBlist[a][b]==KBlist[c][d]&&KBlist[a][b+1]==KBlist[c][d]) {
checksize=checksize+1;
break;
}
}
}
if (checksize==c.size()) {
inset=1;
break;
}
}
}
}
}while (duplicate==0);
You could iterate over each std::vector and use the algorithms of the standard-library.
There is std::find
// find example
#include <iostream> // std::cout
#include <algorithm> // std::find
#include <vector> // std::vector
int main () {
int myints[] = { 10, 20, 30 ,40 };
int * p;
// pointer to array element:
p = std::find (myints,myints+4,30);
++p;
std::cout << "The element following 30 is " << *p << '\n';
std::vector<int> myvector (myints,myints+4);
std::vector<int>::iterator it;
// iterator to vector element:
it = find (myvector.begin(), myvector.end(), 30);
++it;
std::cout << "The element following 30 is " << *it << '\n';
return 0;
}
there is std::find_if
// find_if example
#include <iostream> // std::cout
#include <algorithm> // std::find_if
#include <vector> // std::vector
bool IsOdd (int i) {
return ((i%2)==1);
}
int main () {
std::vector<int> myvector;
myvector.push_back(10);
myvector.push_back(25);
myvector.push_back(40);
myvector.push_back(55);
std::vector<int>::iterator it = std::find_if (myvector.begin(), myvector.end(), IsOdd);
std::cout << "The first odd value is " << *it << '\n';
return 0;
}
As you are working with std::string this shouldn't be a large problem.
In your scenario it is probably better to use std::multiset< vector <string> > with comparator that compares std::vector<string> in a way you need it to. This will give you sorted container with duplicated values next to each other and cheap insert/erase.