0

I was solving a simple problem of finding unique elements in an array. I used a std::unordered_map to count unique elements but it gave Time Limit Exceeded in one test case. Then I used a std::unordered_set with the same result.

PS: I used std::unordered_map and std::unordered_set because I read that these two are much much faster than std::map and std::set respectively.

#include<bits/stdc++.h> using namespace std; int main() { int n, a; cin >> n; unordered_set<int> s; for(int i = 0; i < n; i++) { cin >> a; s.insert(a); } cout << s.size(); } 

Test 7 exceeded the time limit.

My Question is:

If std::unordered_map and std::unordered_set are faster why did they give TLE?

8
  • 2
    Recommended reading: Why should I not #include <bits/stdc++.h>? Commented May 23, 2020 at 13:01
  • 2
    What are the bounds for n and a? Commented May 23, 2020 at 13:02
  • 1
    std::unordered_map and utd::unordered_set are not inherently faster. Nothing is just "magic speed" in programming. You have to know how and when to use what. Commented May 23, 2020 at 13:05
  • You could try to allocate memory first, this would probably increase your code speed Commented May 23, 2020 at 13:10
  • 2
    In your example any container will be much faster than user input you are waiting. Commented May 23, 2020 at 13:37

3 Answers 3

2

std::unordered_set<int> is a node-based container, where each element is stored in separately allocated list node. The list node contains an int element and two list node pointers, which, on a 64-bit platform, makes each list node occupy 24 bytes due to alignment. There is also allocator overhead for each allocated chunk of memory (8 bytes for GNU libc), so that there is at least 28 bytes of overhead for each 4-byte int element.

s.insert(a); makes a memory allocation for each new element and that is what makes the code slow.


To solve this problem efficiently you can use a bitset indexed by the integers, e.g. std::vector<bool>. Set the bit to 1 for each read integer and then count the number of set bits. If the elements are signed, covert them to unsigned to make the bit index a non-negative number.

A working example:

#include <vector> #include <iostream> #include <numeric> #include <limits> int main() { int n; std::cin >> n; std::vector<bool> bitset(1000000001); // a range is 1≤a≤10^9. for(int i = 0, a; i < n; ++i) { std::cin >> a; bitset[static_cast<unsigned>(a)] = true; } std::cout << std::accumulate(bitset.begin(), bitset.end(), 0u) << '\n'; } 

A version that passes that grader:

#include <bitset> #include <iostream> #include <numeric> #include <limits> int main() { int n; std::cin >> n; std::bitset<1000000001> bitset; // a range is 1≤a≤10^9. unsigned unique = 0; for(int i = 0, a; i < n; ++i) { std::cin >> a; if(!bitset.test(a)) { ++unique; bitset.set(a); } } std::cout << unique << '\n'; } 
Sign up to request clarification or add additional context in comments.

21 Comments

What if a is INT_MAX? This is only applicable if we know bounds on a.
You can create a bitset with 2^32 elements, it occupies 2^29 bytes, or 512MB.
What if a is -1? :D
@Ayxan std::bitset stores elements inline (not in dynamically allocated memory). std::bitset with 2^32 bits would take 512MB on the stack, which your platform may not support. For example, on Linux, the default maximum stack size is around 8MB.
Thank you so much @MaximEgorushkin.All Test Cases Passed(AC).You're genius.I learned a new technique!
|
1

The unordered containers are optimized for retrieving values, not for inserting them.

You could take a look at this comparison https://medium.com/@nonuruzun/stl-container-performance-3ec5a8fbc3be. There, you can see that the unordered containers have the O(N) worst case for inserting while the ordered ones have O(log(N))

But you can fix that by allocating memory first, so you will have less collisions.

1 Comment

How can i allocate memory first ?
1

std::unordered_map gives result in o(1) most of the time , not always . Coming to the TLE issue , there might be a possibility that constraints are more than 10^18 and in this case o(n) complexity will not work . Try o(log(n)) approach.

1 Comment

You mean O(1) instead of O(n)? O(n) insert is the worst case. cppreference

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.