Mary
Mary

Reputation: 13

c++ how to print how many times each integer appears in the STL list

using namespace std;

int main()
{
    list<int> numbers; list<int> numb;

    for (int i = 0; i<10; i++)
        numbers.push_back(rand() % 20);

    list<int>::iterator it;

    for (it = numbers.begin(); it != numbers.end(); ++it)
    {
        cout << *it << " ";
    }

    return 0;
}

I wanted to use std::count() but I am not able to do it right. I tried to do the following:

using namespace std;

int main()
{
    list<int> numbers; list<int> numb;

    for (int i = 0; i<10; i++)
        numbers.push_back(rand() % 20);

    list<int>::iterator it;

    for (it = numbers.begin(); it != numbers.end(); ++it)
    {
        cout << *it << " ";

        while (it != numbers.begin() && it != numbers.end())
        {
            ++it;
            *it = count(it, numbers.begin(), numbers.end());
            cout << " " << *it;
        }

        cout << endl;
    }

    return 0;
}

But it gives me an error:

binary == no operator found which takes a left hand operator type 'int' (or there is not acceptable conversion).

I know I am doing something wrong.

I also tried a few more things, like int numb = std::count(numbers.begin()), numbers.end(), *it), but it didn't work either. So, I want to know if there is a special operator to count values in a list.

Upvotes: 1

Views: 719

Answers (5)

atru
atru

Reputation: 4744

In general, using a map is a better approach to your problem, but if you have to solve it using lists here is one possible solution:

#include <iostream>
#include <algorithm>
#include <list>

int main()
{
    std::list<int> numbers, unique_num, numb;
    int num;

    // Create both the original list and a list that
    // will be left with only unique numbers
    for (int i = 0; i<10; i++){
        num = rand() % 20;
        numbers.push_back(num);
        unique_num.push_back(num);
    }

    // Sort and select the unique numbers
    unique_num.sort();
    unique_num.unique();

    // Count unique numbers and store the count in numb
    std::list<int>::iterator iter = unique_num.begin();
    while (iter != unique_num.end())
        numb.push_back(count(numbers.begin(), numbers.end(), *iter++));

    // Print the results
    for(std::list<int>::iterator iter1 = unique_num.begin(), iter2 = numb.begin();
            iter2 != numb.end(); iter1++, iter2++)
        std::cout<< "Number " << *iter1 << " appears " <<
          *iter2 << ( *iter2 > 1 ? " times " : " time" ) << std::endl;

    return 0;
}

The program uses another list, unique_num, to hold unique numbers occurring in numbers. That list is initially created identical to numbers and is then sorted and the duplicates are removed.

The program then iterates through numbers in that unique list and uses count to get the number of occurrences of each of them in the original numbers list. The number of occurrences is then stored in a new list, numb.

When printing, the program uses a ternary operator to check whether it should print "time" or "times" depending whether the result implies one or more than one occurrence.

Note - if you want different list values each time you run your program you need to change the random seed using srand. Include the header #include <time.h> in your program and the line srand(time(NULL)); at the beginning of your main.

Upvotes: 1

mariusz_latarnik01
mariusz_latarnik01

Reputation: 659

WITH ADDITIONAL MEMORY:

You can use buckets, to get complexity O(N + MAX_NUM). So when MAX_NUM <= N we have O(N):

#include <iostream>
#include <list>
#include <algorithm>
#include <ctime>

const int MAX_NUM = 20;
const int N = 10;

int main() {
    std::list<int> numbers;
    int buckets[MAX_NUM];

    std::fill(buckets, buckets + MAX_NUM, 0);

    srand(time(NULL));

    for (int i = 0; i < N; i++) numbers.push_back(rand() % MAX_NUM);

    // computing
    for (auto it = numbers.begin(); it != numbers.end(); ++it) {
        buckets[*it]++;
    }

    //printing answers
    for (int i = 0; i < MAX_NUM; i++) {
        if (buckets[i]) std::cout << "value " << i << " appears in the list " << buckets[i] << " times." <<std::endl;
    }

    return 0;
}

For big data i would recommend using std::unordered_map for buckets and then geting complexity O(N) (thanks to hashing):

#include <iostream>
#include <list>
#include <algorithm>
#include <ctime>
#include <unordered_map>

const int N = 10;
const int MAX_NUM = 20;
int main() {
    std::list<int> numbers;
    std::unordered_map<int, int> buckets;

    srand(time(NULL));

    for (int i = 0; i < N; i++) numbers.push_back(rand() % MAX_NUM);

    // computing
    for (auto it = numbers.begin(); it != numbers.end(); ++it) {
        buckets[*it]++;
    }

    //printing answers
    for (auto & k_v : buckets) {
        std::cout << "value " << k_v.first << " appears in the list " << k_v.second << " times." <<std::endl;
    }
    return 0;
}

WITHOUT ADDITIONAL MEMORY:

In more universal way, you can use std::vector instead of std::list and std::sort on it, and then count value changes in a simple for. Complexity is O(N log N):

#include <iostream>
#include <vector>
#include <algorithm>
#include <ctime>

const int N = 10;
const int MAX_NUM = 20;

int main() {
    std::vector<int> numbers;

    srand(time(NULL));

    for (int i = 0; i < N; i++) numbers.push_back(rand() % MAX_NUM);

    // sorting
    std::sort(numbers.begin(), numbers.end());

    //printing answers for sorted vector
    if (numbers.size() > 0) {
        int act_count = 1;
        for (int i = 1; i < numbers.size(); i++) {
            if (numbers[i] != numbers[i -1]) {
                std::cout << "value " << numbers[i-1] << " appears in the list " << act_count << " times." <<std::endl;
                act_count = 1;
            } else {
                act_count++;
            }
        }
        std::cout << "value " << numbers[numbers.size() - 1] << " appears in the list " << act_count << " times." <<std::endl;
    }
    return 0;
}

You can also do the above on std::list, getting also O(nlogn), but can't use std::sort:

#include <iostream>
#include <list>
#include <ctime>

const int N = 10;
const int MAX_NUM = 20;

int main() {
    std::list<int> numbers;

    srand(time(NULL));

    for (int i = 0; i < N; i++) numbers.push_back(rand() % MAX_NUM);

    // sorting
    numbers.sort();

    //printing answers for sorted list
    if (!numbers.empty()) {
        int act_count = 0;
        auto prev = numbers.begin();
        for (auto it = numbers.begin(); it != numbers.end(); it++) {
            if (*it != *prev) {
                std::cout << "value " << *it << " appears in the list " << act_count << " times." <<std::endl;
                act_count = 1;
            } else {
                act_count++;
            }
            prev = it;
        }
        std::cout << "value " << *prev << " appears in the list " << act_count << " times." <<std::endl;
    }

    return 0;
}

Upvotes: 0

Remy Lebeau
Remy Lebeau

Reputation: 596352

You are not using iterators correctly (you are modifying it while you are still using it to iterate the list), and you are not calling std::count() correctly.

The code should look more like this instead:

#include <iostream>
#include <list>
#include <algorithm>
#include <cstdlib>

int main()
{
    std::list<int> numbers;
    int numb;

    for (int i = 0; i < 10; i++)
        numbers.push_back(std::rand() % 20);

    std::list<int>::iterator it;    

    for (it = numbers.begin(); it != numbers.end(); ++it)
    {
        numb = std::count(numbers.begin(), numbers.end(), *it);
        std::cout << *it << " " << numb << std::endl;
    }

    /* or:
    for (int value : numbers)
    {
        numb = std::count(numbers.begin(), numbers.end(), value);
        std::cout << value << " " << numb << std::endl;
    }
    */

    return 0;
}

But, like others said, you should use a std::map to track the counts, so you can account for duplicates, eg:

#include <iostream>
#include <list>
#include <map>
#include <cstdlib>

int main()
{
    std::list<int> numbers;
    std::map<int, int> numb;

    for (int i = 0; i < 10; i++)
        numbers.push_back(rand() % 20);

    for (std::list<int>::iterator it = numbers.begin(); it != numbers.end(); ++it)
        numb[*it]++;

    /* or:
    for (int value : numbers)
        numb[value]++;
    */

    for (std::map<int, int>::iterator it = numb.begin(); it != numb.end(); ++it)
        std::cout << it->first << " " << it->second << std::endl;

    /* or:
    for (auto &item : numb)
        std::cout << item.first << " " << item.second << std::endl;
    */

    return 0;
}

Which can be reduced to this:

#include <iostream>
#include <map>
#include <cstdlib>

int main()
{
    std::map<int, int> numb;

    for (int i = 0; i < 10; i++)
        numb[rand() % 20]++;

    for (std::map<int, int>::iterator it = numb.begin(); it != numb.end(); ++it)
        std::cout << it->first << " " << it->second << std::endl;

    /* or:
    for (auto &item : numb)
        std::cout << item.first << " " << item.second << std::endl;
    */

    return 0;
}

Upvotes: 2

SoronelHaetir
SoronelHaetir

Reputation: 15162

I suggest you use a map:

map<int, int> counts;
for(int val : Numbers)
  ++counts[val];

Upvotes: 1

Justin Randall
Justin Randall

Reputation: 2278

You need to look at the signature for std::count again. It takes three parameters std::count(InputIterator first, InputIterator last, const T& val); and it returns the number of occurrences of val in your data set. So something like this should work for you where theNumber is the number you're counting.

#include <algorithm>

int occurrences = std::count(numbers.begin(), numbers.end(), theNumber); 

Upvotes: 2

Related Questions