0

I have a 2-D vector of hyperedges as well as an adjacency list. I have to find the union of hyperEdges[i].size() vectors, but I can only find the union of just two vectors. What improvement can I make to my code below to do this? I want to store the union into newly declared 2-D vector connectedEdges

void find_union()
{
    connectedEdges.resize(nEdges+1);
    for(int i = 1; i <= nEdges; i++)
    {
        vector<int>::iterator it;
        connectedEdges[i].resize(nEdges+1);

        for(int j = 1; j < hyperEdges[i].size()-1; j++)
        {
            int p = hyperEdges[i][j-1];
            int q= hyperEdges[i][j];
            it = set_union(adjL[p].begin(), adjL[p].end(),adjL[q].begin(),adjL[q].end(), connectedEdges[i].begin());
        connectedEdges[i].resize(it-connectedEdges[i].begin());
        }
    }    
}

Example : {1,2,4,6,8}

{1,2,3,5,6}

{1,4,7,13,15}

Union of these three sets should be {1,2,3,4,5,6,7,8,13,15} But my program returns {1,2,3,4,5,6,8}

timrau
  • 22,578
  • 4
  • 51
  • 64
Shravan40
  • 8,922
  • 6
  • 28
  • 48
  • 1
    What exactly is your problem? What doesn't work? – Stephan Dollberg Apr 30 '15 at 12:54
  • @inf : My problem is that, it only store the union of two vector, where i want to store the union of `hypeEdges[i].size()` vector. – Shravan40 Apr 30 '15 at 12:57
  • Your question is very unclear. Are you trying to find the union of two vectors? Or are you able to find the union of two vectors and trying to find the union of many vectors? – Beta Apr 30 '15 at 13:02
  • Can you use a debugger or print statements to verify that set_union returns what you expect it to return? I don't understand what the resize is doing but it looks very very weird. – Andy Newman Apr 30 '15 at 13:03
  • @Beta : I want to find the union of more than two vectors. – Shravan40 Apr 30 '15 at 13:04

3 Answers3

6

If you have a lot of vectors, I'd suggest to insert content of all of them into single std::set and then dump it back into std::vector.

Something like that:

std::vector<std::vector<int>> src = ...;
std::set<int> all;

for(int i = 0; i < src.size(); i++) {
    all.insert(src[i].begin(), src[i].end());
}

std::vector<int> result(all.begin(), all.end());
anxieux
  • 757
  • 5
  • 14
2

You can also run std::set_union with all adjacent vectors (make sure they are sorted), building up the result in steps, like in the following example:

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

int main()
{
    std::vector<std::vector<int>> v{{1, 1, 2}, {1, 1, 1}, {1, 5, 6, 7}};       
    std::vector<int> result, tmp;
    for(auto&& elem: v)
    {
        std::set_union(elem.begin(), elem.end(), 
                       result.begin(), result.end(),
                       std::back_inserter(tmp));
        result = std::move(tmp); 
        tmp.clear(); // see http://stackoverflow.com/q/9168823/3093378
    }
    // in case you want to remove duplicates
    result.erase(std::unique(result.begin(), result.end()), result.end());
    for(auto&& elem: result)
        std::cout << elem << " "; // 1 2 5 6 7
}
vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • I'd suggest using `swap` to copy `result` into `tmp`. – anxieux Apr 30 '15 at 13:33
  • Current version seems to be invalid, because you move into `result` which was just created... – anxieux Apr 30 '15 at 13:36
  • @anxieux I rolled the edit back, have to modify a bit the code for swap/move. Will update when having it correct. Done now. But probably your answer is the most appropriate here, in terms of efficiency and simplicity. – vsoftco Apr 30 '15 at 13:37
  • Your algorithm fails with `std::vector> v{{1,1,1},{1,1,1},{1,1,1}};`. – Lingxi Apr 30 '15 at 13:43
  • @Lingxi works for me, maybe you copied a previous edit (had small typos). Try the one now – vsoftco Apr 30 '15 at 13:44
  • I'm not sure, but it seems to be a bad idea to use `std::back_inserter` with the `vector` with content moved out. Suggest to re-init it. – anxieux Apr 30 '15 at 13:45
  • @anxieux hmm, that's a good point. I wonder if it's safe to reuse it after moving out, even after `tmp = {}`. I would guess it is. Most probably `std::move(tmp)` makes `tmp` empty, but I am not 100% sure about reusing it. I'd say it's safe. – vsoftco Apr 30 '15 at 13:47
  • See http://coliru.stacked-crooked.com/a/c4387325822b4d8b I think you failed to catch the complete behavior of `std::set_union`. – Lingxi Apr 30 '15 at 13:47
  • @Lingxi that's what the union should be, you don't duplicate elements in a set union (at least using the mathematical definition). – vsoftco Apr 30 '15 at 13:49
  • 1
    @anxieux I think this answer our question: http://stackoverflow.com/q/9168823/3093378 – vsoftco Apr 30 '15 at 13:50
  • @vsoftco The solution of yours and that of anxieux don't produce the same result for `{1, 1, 1}, {1, 1, 1}, {1, 1, 1}`. So, at least one of the two is incorrect. – Lingxi Apr 30 '15 at 13:53
  • @Lingxi ohh I see what you mean now... you have to run an additional `v.erase(std::unique(...))` on the end result and erase duplicates. Although I guess that in a strict "set-theory" sense, an input like `{1,1,1}` should be invalid. If accepted, then the `unique` trick will do the job of removing the duplicates. – vsoftco Apr 30 '15 at 13:54
  • @Lingxi This is a problem of unclear requirements (which are rarely clear in case of SO questions)... Given the context, I assume original vectors don't contain duplicates. If this is the case both solutions are equivalent. – anxieux Apr 30 '15 at 14:20
1

You could move them into a set and back out as anxieux suggests. Or you could just manually iterate over all of them directly:

#include <iostream>
#include <memory>
#include <vector>
#include <algorithm>

std::vector<int> find_union(const std::vector<std::vector<int>>& vecs)
{
    using iter = std::vector<int>::const_iterator;
    using pr = std::pair<iter, iter>;

    // construct pairs of iterators into each vector
    // we will iterate over all of these simultaneously
    std::vector<pr> iter_pairs;
    std::vector<int> results;

    iter_pairs.reserve(vecs.size());
    for (auto& v : vecs) {
        iter_pairs.emplace_back(v.begin(), v.end());
    }   

    while (!iter_pairs.empty()) {
        // pick the next smallest element
        int m = *std::min_element(iter_pairs.begin(), iter_pairs.end(), [](const pr& a, const pr& b){ 
                    return *a.first < *b.first;
                })->first;

        // add it to our results
        results.push_back(m);

        // any vector that contained this element should be advanced
        // if we're done with that vector, remove it
        iter_pairs.erase(
            std::remove_if(iter_pairs.begin(), iter_pairs.end(), [=](pr& p){ 
                if (*p.first == m) {
                    ++p.first;
                    return p.first == p.second;
                }   
                return false;
            }), 
            iter_pairs.end()
            );  
    }   

    return results;
}

int main() {
    for (int i : find_union({{1,2,3}, {1,2,4}, {3,5,42}})) {
        std::cout << i << ' '; // prints 1 2 3 4 5 42
    }   
    std::cout << std::endl;
}
Community
  • 1
  • 1
Barry
  • 286,269
  • 29
  • 621
  • 977