0
votes

I am trying to implement this problem in C++ using unordered map:

Given a non-empty array of integers, every element appears twice except for one. Find that single one.

Note:

Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

Example 1:

Input: [2,2,1]
Output: 1

Example 2:

Input: [4,1,2,1,2]
Output: 4

My solution:

class Solution {
 public:
  int singleNumber(vector<int>& nums) {
    std::unordered_map<int, int> umap;

    for (auto i = nums.begin(); i != nums.end(); i++) {
      umap[*i] = umap[*i] + 1;
    }

    for (auto i = umap.begin(); i != umap.end(); i++) {
      if (umap[*i] == 1) {
        return *i;
      }
    } 
  }
};

But unfortunately, it does not work. I get this error while compiling

Line 16: Char 17: fatal error: no viable overloaded operator[] for type 'std::unordered_map'
        if (umap[*i] == 1) {
            ~~~~^~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/unordered_map.h:973:7: note: candidate function not viable: no known conversion from 'std::pair' to 'const std::unordered_map, std::equal_to, std::allocator > >::key_type' (aka 'const int') for 1st argument
      operator[](const key_type& __k)
      ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/unordered_map.h:977:7: note: candidate function not viable: no known conversion from 'std::pair' to 'std::unordered_map, std::equal_to, std::allocator > >::key_type' (aka 'int') for 1st argument
      operator[](key_type&& __k)
      ^
1 error generated.

I cannot understand the error. Can anyone explain to me?

2
The unordered map would take up extra memory, though. So even if you get this to work, it wont satisfy the task given.bitmask
Could you implement it without using extra memory? -- You didn't follow the instructions when you used unordered_map. This is a question where if you don't know the trick, you may never solve it.PaulMcKenzie
(umap[*i] == 1) is problamatic, you cannot compare it that way. use (i->first == 1)Build Succeeded
Yes, as @PaulMcKenzie, the solution is actually very easy to implement once you think of the trick. Think of the mathematical properties of your input array. You have to read each value exactly once.bitmask

2 Answers

-2
votes

You do not need std::unordered_map as std::unordered_set would be sufficient in this case:

std::unordered_set<int> set;
for( auto i : nums ) {
   auto p = set.insert( i );
   if( !p.second ) set.erase( p.first );
}

So logic is you try to insert a number, but if it was already there you remove it. Another benefit of this approach beside smaller memory footprint is that after loop you will have only one element in the set - one you need. So you do not need to iterate and search.

But proper solution is a trick that you should be aware about. It based of the properties of xor operation:

A xor A == 0 for any A
A xor 0 == A for any A

and because xor operation has commutative property as well:

A xor B xor A == A xor A xor B == 0 xor B == B

I think you now can get the idea of implementation without additional memory.

2
votes

The relevant bit of the error is:

candidate function not viable: no known conversion from
   'std::pair' to
   'const std::unordered_map, std::equal_to, std::allocator > >::key_type'
       (aka 'const int')
for 1st argument operator[](const key_type& __k)

So what this means is then when using the subscript operator in umap[*i] == 1 the type of *i is some std::pair and the type that the operator expects is const int (look at the "aka").

That's because map iterators return an std::pair containing a reference to the key data and to the value data. You can get the value data easily just from the iterator without invoking the subscript operator:

if (i->second == 1)
  return i->first;

However, as stated in the comments you don't need any additional container to solve this puzzle. The constraint "Could you implement it without using extra memory?" is actually a hint to the solution. There is a mathematical property in a non-empty array of integers, where every element appears twice (!) except for one.


Also, I'd recommend using the variable name i for indexes and calling iterators it, but that's just personal preference.