hkBattousai
hkBattousai

Reputation: 10941

How do I check if two std::vector's contain only the same elements?

I need an algorithm or a standard library function for comparing two vector elements, like below:

class Utility
{
    template <class T>
    static bool CheckIfVectorsEquivalent(   const std::vector<T> & Vec1,
                                            const std::vector<T> & Vec2)
    {
        // ???
    }
};

Working under the following specifications:

std::vector<int> v1, v2, v3, v4, v5, v6, v7, v8;

// Returns false when not all the elements are matching between vectors
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
v2.push_back(2);
v2.push_back(3);
v2.push_back(8);
Utility::CheckIfVectorsEquivalent(v1, v2);  // Must return false

// Returns true when all the elements match, even if the are not in the same order
v3.push_back(3);
v3.push_back(1);
v3.push_back(7);
v4.push_back(7);
v4.push_back(3);
v4.push_back(1);
Utility::CheckIfVectorsEquivalent(v3, v4);  // Must return true

// Returns false when one of the vectors is subset of the other one
v5.push_back(3);
v5.push_back(1);
v5.push_back(7);
v6.push_back(7);
v6.push_back(3);
v6.push_back(1);
v6.push_back(18);
v6.push_back(51);
Utility::CheckIfVectorsEquivalent(v5, v6);  // Must return false

// Returns true when the both vectors are empty
Utility::CheckIfVectorsEquivalent(v7, v8);  // Must return true

Is there any standard (with STL) way of doing this? If not, how can I write this algorithm? It confused me too much.

Upvotes: 7

Views: 29243

Answers (7)

Rafał Rawicki
Rafał Rawicki

Reputation: 22700

The standard way will be sorting these two vectors and using operator ==, which compares corresponding values.

The sample solution realizing this algorithm is:

#include <vector>
#include <algorithm>

template<typename T>
bool compare(std::vector<T>& v1, std::vector<T>& v2)
{
    std::sort(v1.begin(), v1.end());
    std::sort(v2.begin(), v2.end());
    return v1 == v2;
}

Its complexity is O(n*log(n)), because of the sorting.

Upvotes: 21

Marshall Clow
Marshall Clow

Reputation: 16700

If you can live with just a c++11 solution, then std::is_permutation is exactly what you want

template <class FI1, class FI2>
bool is_permutation ( FI1 first, FI1 last, FI2 d_first );

If you can't do that, then in the upcoming boost 1.50 release, there will be

boost::algorithm::is_permutation

with the same interface.

Upvotes: 21

bootry
bootry

Reputation: 11

  v7.push_back(3);
  v7.push_back(1);
  v7.push_back(7);

  v8.push_back(7);
  v8.push_back(3);
  v8.push_back(1);
  v8.push_back(1);
  v8.push_back(7);

  std::cout << compare(v7, v8) << " true or false" << std::endl;  

the condition as above, the function compare will return true or false.I lost count. if return true. we can't use the way: The standard way will be sorting these two vectors and using operator ==, which compares corresponding values.

Upvotes: 0

Captain Giraffe
Captain Giraffe

Reputation: 14705

If we consider an int to be large enough for the computations done in the following algorithm, there is a simple O(N) algorithm.

0: initialization: There are primes to the size of max(v1,v2), product1=1, product2=1

1: return false if size of v1 != size of v2

2:

foreach (  i in v1 ) {
   product1 *= primes[v1[i]]
   product2 *= primes[v2[i]]
}

3.

result product1 == product2;

Upvotes: 0

SigTerm
SigTerm

Reputation: 26439

Simplest way to do it is to create copies of Vec1 and Vec2, sort them and compare using ==.

Another way to do it is to build two multisets from Vec1 and Vec2 and compare them using ==.

Yet another way to do it is to use maps (std::map or unordered_map) to store counters - i.e. increment stored value for every element of Vec1 and decrement for every stored element of Vec2, then check if map contains non-zero elements. If there are non-zero elements stored in map, vectors are non-equal.

messy pseudocode example:

std::vector<Value> vec1, vec2;
//initialize vec1 and vec2, fill with data
typedef int Counter;
typedef unordered_map<Value, Counter> CounterMap;

CounterMap counters;
for (size_t i = 0; i < vec1.size(); i++)
    counters[vec1[i]]++;
for (size_t i = 0; i < vec2.size(); i++)
    counters[vec2[i]]--;

bool equal = true;
for (CounterMap::const_iterator i = coutners.begin(); equal && (i != counters.end()); i++)
    if (i->second != 0)
        equal = false;

Depending on your STL (or boost) implementation, data type, and order of stored data in vectors) one of those methods will be faster, but it is hard to tell which one.

Upvotes: 0

There's an O(n) solution to this problem for unsorted inputs:

#include <vector>
#include <algorithm>
#include <iostream>
#include <boost/function_output_iterator.hpp>

template <typename T>
T xorfunc(const T& a, const T& b) {
  return a^b;
}

template <typename T>
bool compare(const std::vector<T>& v1, const std::vector<T>& v2) {
  if (v1.size() != v2.size())
    return false;

  T result = 0;
  std::transform(v1.begin(), v1.end(), v2.begin(), boost::make_function_output_iterator([&result](const T& r) { result ^= r; }), std::ptr_fun(&xorfunc<T>));
  return !result;
}

that works for integer inputs and exploits the fact that a ^ b ^ c ^ d == 0 for any combination of paired values. It will never give false negatives, it might give false positives potentially, but you can reduce those further in O(n) space/time. If you're mostly hitting negatives then this might be useful as a pre-sort+compare step to rule them out quickly. It works for all the test cases you've shown:

int main() {
    std::vector<int> v1, v2, v3, v4, v5, v6, v7, v8;

    // Returns false when not all the elements are matching between vectors
    v1.push_back(1);
    v1.push_back(3);
    v1.push_back(5);
    v2.push_back(2);
    v2.push_back(3);
    v2.push_back(8);
    std::cout << compare(v1, v2) << " (false)" << std::endl;  // Must return false

    // Returns true when all the elements match, even if the are not in the same order
    v3.push_back(3);
    v3.push_back(1);
    v3.push_back(7);
    v4.push_back(7);
    v4.push_back(3);
    v4.push_back(1);
    std::cout << compare(v3, v4) << " (true)" << std::endl;  // Must return true

    // Returns false when one of the vectors is subset of the other one
    v5.push_back(3);
    v5.push_back(1);
    v5.push_back(7);
    v6.push_back(7);
    v6.push_back(3);
    v6.push_back(1);
    v6.push_back(18);
    v6.push_back(51);
    std::cout << compare(v5, v6) << " false" << std::endl;  // Must return false

    // Returns true when the both vectors are empty
    std::cout << compare(v7, v8) << " true" << std::endl;  // Must return true

}

Gives:

0 (false)
1 (true)
0 false
1 true

Upvotes: 0

Alan Stokes
Alan Stokes

Reputation: 18972

Create a multiset from each vector, then just compare them for equality.

Upvotes: 3

Related Questions