Search within a big vector in C++ - c++

I have following vectors:
std::vector<A*> vec;
std::vector<std::pair<A*, A*>> vec_pair;
vec_pair size is much more than vec size. I would like to find a pair within vec_pair which both members are inside vec.
contents of the vec_pair are constant. However after each iteration the contents of the vec will change and I would like to do the test again.
I know I can do a for loop and do the check. However considering size difference and recurrence of the job, I am looking for a smart and efficient way to do it.

If you are not going to change content of vec, create an std::unordered_set<A*> with the same content and search for occurrences there. Searching in an unordered_set is approximately O(1), so this would be a simple win.
The easiest and the most efficient way to construct an unordered_set from a vector is to use the constructor taking two iterators:
unordered_set<A*> us(vec.begin(), vec.end());

You can use an unordered_set (you don't really need a map) which allows O(1) search and insert.
1) From vec you build an unordered_set<A*> S;
2) For each pair in vec_pair you can check if both elements are present in S
Something like the following will do the job in average O(vec_pair.size())
std::vector<A*> vec;
std::vector<std::pair<A*, A*>> vec_pair;
unordered_set<A*> S;
for(auto a: vec)
S.insert(a);
for(auto p : vec_pair){
if(s.find(p.first)!=S.end() &&
s.find(p.second)!=S.end())
{
//PAIR GOOD
}else{
//THIS PAIR IS NOT GOOD
}
}

vec_pair size is much more than vec size.
This should act as a clue to you to use std::map.
Put all the elements of vector vec inside a map : myMap[vec[i]] = 1;
Then go through each pair in vec_pair, and do
if (myMap.find(vec_pair[i].first != myMap.end()) &&
myMap.find(vec_pair[i].second != myMap.end()) )
{
return FOUND;
}
else
return NOT_FOUND;`
As commented by Gates, use unordered_map for faster operations.

I don't know yours exactly requirements but, if the order of element in vec_pair isn't important, I suppose you can substitute it with a std::multimap or, I suppose better, a std::unordered_multimap.
I mean (using a type A equal to int as example) that instead
using A = int;
std::vector<std::pair<A, A>> const vec_pair
{ {1, 1}, {1, 2}, {1, 3}, {1, 4},
{2, 1}, {2, 2}, {2, 3}, {2, 4},
{3, 1}, {3, 2}, {3, 3}, {3, 4},
{4, 1}, {4, 2}, {4, 3}, {4, 4} };
you can use
std::unordered_multimap<A, A> const cM
{ {1, 1}, {1, 2}, {1, 3}, {1, 4},
{2, 1}, {2, 2}, {2, 3}, {2, 4},
{3, 1}, {3, 2}, {3, 3}, {3, 4},
{4, 1}, {4, 2}, {4, 3}, {4, 4} };
If you need that vec_pair is a vector of pairs, using the fact that vec_pair is constant (I understand correctly?) you can contruct a constant unordered multimap.
The advantage of this solution is that if you find that a key of the map isn't in vec, you can avoid the test for all the values with the same key.
More: if construct a set (or, better, an unordered_set) starting from vec (that is little, if I understand correctly, you can check the pairs as follows
for ( auto ci = cM.cbegin() ; ci != cM.cend() ; )
{
auto val = ci->first;
auto cnt = cM.count(val);
if ( s.end() == s.find(val) )
{
for ( auto i = 0U ; i < cnt ; ++i )
++ci;
}
else for ( auto i = 0U ; i < cnt ; ++i, ++ci )
if ( s.end() != s.find(ci->second) )
std::cout << "- good for <" << val << ", " << ci->second
<< '>' << std::endl;
}
I know: isn't an elegant solution.
Another way is to use a combination of map and set (unorderd, better) and instead of
std::vector<std::pair<A, A>> const vec_pair
{ {1, 1}, {1, 2}, {1, 3}, {1, 4},
{2, 1}, {2, 2}, {2, 3}, {2, 4},
{3, 1}, {3, 2}, {3, 3}, {3, 4},
{4, 1}, {4, 2}, {4, 3}, {4, 4} };
use (or construct)
std::unordered_map<A, std::unordered_set<A>> const cM
{ {1, {1, 2, 3, 4}}, {2, {1, 2, 3, 4}},
{3, {1, 2, 3, 4}}, {4, {1, 2, 3, 4}} };
In this case, the search part is more elegant (IMHO)
for ( auto const & p : cM2 )
if ( s.end() != s.find(p.first) )
for ( auto const & sec : p.second )
if ( s.end() != s.find(sec) )
std::cout << "- good for <" << p.first << ", " << sec
<< '>' << std::endl;
The following is a full compilable example for both solutions
#include <vector>
#include <utility>
#include <iostream>
#include <unordered_map>
#include <unordered_set>
int main()
{
using A = int;
std::unordered_multimap<A, A> const cM
{ {1, 1}, {1, 2}, {1, 3}, {1, 4},
{2, 1}, {2, 2}, {2, 3}, {2, 4},
{3, 1}, {3, 2}, {3, 3}, {3, 4},
{4, 1}, {4, 2}, {4, 3}, {4, 4} };
std::unordered_set<A> s { 4, 3 };
for ( auto ci = cM.cbegin() ; ci != cM.cend() ; )
{
auto val = ci->first;
auto cnt = cM.count(val);
if ( s.end() == s.find(val) )
{
for ( auto i = 0U ; i < cnt ; ++i )
++ci;
}
else for ( auto i = 0U ; i < cnt ; ++i, ++ci )
if ( s.end() != s.find(ci->second) )
std::cout << "- good for <" << val << ", " << ci->second
<< '>' << std::endl;
}
std::unordered_map<A, std::unordered_set<A>> const cM2
{ {1, {1, 2, 3, 4}}, {2, {1, 2, 3, 4}},
{3, {1, 2, 3, 4}}, {4, {1, 2, 3, 4}} };
for ( auto const & p : cM2 )
if ( s.end() != s.find(p.first) )
for ( auto const & sec : p.second )
if ( s.end() != s.find(sec) )
std::cout << "- good for <" << p.first << ", " << sec
<< '>' << std::endl;
}

How much smaller is vec than vec_pair? Is the square of the size of vec still much smaller than the size of vec_pair? If so, you make an unordered_set out of vec_pair, and then search it for every possible pair generated from two things from vec.
Why can't you sort the two containers? Can you make a sorted copy of each? If so, take a pointer to the start of both lists, and increment both looking for matches. You would still be O(size of vec_pair) but your constant would be really small -- often just a single pointer comparison.

Related

Sort 2d C++ array by first element in subarray

I am trying to sort a c++ subarray by first element.
My code is currently set up like this:
int umbrellas[3][2] = {{5, 6}, {2, 7}, {9, 20}};
int n = sizeof(umbrellas) / sizeof(umbrellas[0]);
sort(umbrellas, umbrellas + n, greater<int>());
The sort function doesn't seem to be functioning properly and when I run the code it generates errors. Is there a way to sort the array from
{{5, 6}, {2, 7}, {9, 20}}
into
{{2, 7}, {5, 6}, {9, 20}}
?
Use a std::vector of std::vector as your container and the sort becomes much easier to do with. Not only is std::vector the preferred container for C++ but using STL functions on it is way simpler and direct , without any substantiable overhead.
Define your data as
std::vector<std::vector<int>> umbrellas{
{5, 6},
{2, 7},
{9, 20}
};
Now you can use a custom comparator lambda that takes in two vector element references and returns True when the first element of the above vector is smaller than that of the one below.
std::sort(umbrellas.begin(),
umbrellas.end(),
[](const std::vector<int> &above, const std::vector<int> &below)
{
return (above[0] < below[0]);
});
And the output :
for (auto &&row : umbrellas) {
for (auto element : row) {
std::cout<< element<< " ";
}
std::cout<< "\n";
}
2 7
5 6
9 20
Taking this to C++20 it's even easier:
std::ranges::sort(umbrellas, std::less(),
[](const auto &v) { return v[0];});
If time complexity doesn't matter, this code will achieve the desired with O(n^2) complexity
int arr[3][2] = {{5, 6}, {2, 7}, {9, 20}};
int n = sizeof(arr) / sizeof(arr[0]);
for(int i = 0 ; i < n - 1; i++){
for(int j = 0 ; j < n - 1 ; j++){
if(arr[j][0] > arr[j + 1][0])
swap(arr[j],arr[j + 1]);
}
}

c++ insert vector in other vector from specific position

My target is to insert vector in another vector from specific position with c++.
Example:
std::vector<int> a = {1, 2, 3};
std::vector<int> b = {4, 5, 6};
int position = 1;
Output: 1, 4, 5, 6, 2, 3.
It's easy:
vector<int> a = {1, 2, 3};
vector<int> b = {4, 5, 6};
int position = 1;
a.insert(a.begin()+position,b.begin(),b.end());
We can easily do it by insert function.
std::vector<int> a = {1, 2, 3};
std::vector<int> b = {4, 5, 6};
auto it = a.begin();
int position = 1;
a.insert(it+position, b.begin(), b.end());

C++ How to print this map std::map<int, pair<vector<pair<int, int>>, int>> [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 9 years ago.
Improve this question
I want to print pairs in map like:
std::map<int, pair<vector<pair<int, int>>, int>> Mymap;
Example for data in this map:
Mymap[0] = (0, ({ { 1, 3 }, { 1, 5 } }, 4))
Mymap[1] = (1, ({ { 2, 3 }, { 3, 7 }, { 1, 3 } }, 8))
I want to print pairs like:
{ 1, 3 }, { 1, 5 }
{ 2, 3 }, { 3, 7 }, { 1, 3 }
So, a good first step is to actually write the example data such that it matches the type you've requested.
Perhaps something like:
Mymap[0] = {{{1, 3}, {1, 5}}, 4};
Mymap[1] = {{{2, 3}, {3, 7}, {1, 3}}, 8};
Then, we can pretty easily iterate over this...
#include <map>
#include <vector>
#include <iostream>
int main() {
std::map<int, std::pair<std::vector<std::pair<int, int>>, int>> Mymap;
Mymap[0] = {{{1, 3}, {1, 5}}, 4};
Mymap[1] = {{{2, 3}, {3, 7}, {1, 3}}, 8};
for (const auto & pair : Mymap) {
for (const auto & pair : pair.second.first) {
std::cout << "{" << pair.first << ", " << pair.second << "}, ";
}
std::cout << "\n";
}
}
Which outputs:
{1, 3}, {1, 5},
{2, 3}, {3, 7}, {1, 3},
I do not see any sense in your question, what exactly are you trying to do ?
Anyhow, this code should be able to do it:
std::map<int, pair<vector<pair<int, int>>, int>> Mymap;
Mymap[0] = {{{1, 3}, {1, 5}}, 4};
Mymap[1] = {{{2, 3}, {3, 7}, {1, 3}}, 8};
map<int, pair<vector<pair<int, int>>, int>>::iterator it;
for (it=Mymap.begin(); it!=Mymap.end(); it++){
pair<vector<pair<int, int>>, int> myPair=it->second;
vector<pair<int, int>> myVec=myPair.first;
for (int i=0; i<myVec.size(); i++){
cout<< "{ " << myVec[i].first << ","<<myVec[i].second<< " }, ";
}
cout<<endl;
}

The most neat way to remove all but the last element with the same key from a sorted std::vector?

I have a vector of structures {key; value}, sorted by key:
{ {0, 1}, {0, 2}, {1, 1}, {1, 2}, {1, 3}, {2, 1}, {2, 2} }
I need to erase all but the last elements with the same key. The result should be:
{ {0, 2}, {1, 3}, {2, 2} }
What is the most neat way to do this? Which STL algorithms can I use? Apparently, this task doesn't fit remove-erase idiom.
A naive but efficient solution is to iterate over the vector, copying the relevant elements into a new vector.
An alternative is to use std::unique (with an appropriate predicate). As you want to preserve the last element in each group, you'd need to use a reverse iterator.
I words, the algorithm needed would be:
iterate the container backwards
if you encoutner a new key, leave the element,
if you encoutner a key you already had, remove the element.
in code:
#include <vector>
#include <iostream>
#include <algorithm>
struct S { int key; int value; };
int main() {
std::vector<S> vec{ {0, 1}, {0, 2}, {1, 1}, {1, 2}, {1, 3}, {2, 1}, {2, 2} };
auto lastKey = std::numeric_limits<int>::max();
auto rLast = std::remove_if(vec.rbegin(), vec.rend(), [&lastKey](S const& s) -> bool {
if (s.key == lastKey) return true;
lastKey = s.key;
return false;
});
vec.erase(begin(vec),rLast.base());
for (auto& s : vec) {
std::cout << '{' << s.key << ',' << s.value << '}';
}
}
Or using std::unique as recommended in the other answer:
auto rLast = std::unique(vec.rbegin(), vec.rend() [](S const& s1, S const& s2) {
return s1.key == s2.key;
});
vec.erase(vec.begin(), rLast.base());
If you use an std::map the problem just disappears:
std::map<int, int> theMap;
// insert the elements of { {0, 1}, {0, 2}, {1, 1}, {1, 2}, {1, 3}, {2, 1}, {2, 2} }
theMap[0] = 1;
theMap[0] = 2;
theMap[1] = 1;
theMap[1] = 2;
theMap[1] = 3;
theMap[2] = 1;
theMap[2] = 2;
// result: { {0, 2}, {1, 3}, {2, 2} }

Find if one map is subset of another

I have two STL maps std::map<int, int> foo = {{1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}}; and std::map<int, int> bar = {{2, 0}, {4, 0}, {5, 0}};
I want to find if bar is a subset of foo.
Since the elements are sorted in map, I would think
to find the first element from bar in foo, and then find consecutive elements
from bar in foo from that location.
The problem here is I'm not able to figure out a way to do that with STL maps in cpp.
Can I reduce the search range in map for every find from a location in map to the end of the map?
I hope I explained the problem.
Use std::includes algorithm with a custom comparator that compares only the keys:
#include <map>
#include <algorithm>
#include <iostream>
int main()
{
std::map<int, int> foo = {{1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}};
std::map<int, int> bar = {{2, 0}, {4, 0}, {5, 0}};
typedef std::pair<int,int> pair;
std::cout <<
std::includes(foo.begin(), foo.end(), bar.begin(), bar.end(),
[](const pair& p1, const pair& p2)
{
return p1.first < p2.first;
});
}
You could extract key sets (set1 and set2) of both maps (foo and bar), and as long as they are sorted, you can do the following:
if (std::includes(set1.begin(), set1.end(),
set2.begin(), set2.end())) {
// ...
}
See std::includes.
A simple way is to use Boost.Range in combination with boost::includes:
using namespace boost::adaptors;
bool result = includes(foo | map_keys, bar | map_keys);
Here is how a minimal, complete program could look like (mapped values are disregarded):
#include <map>
#include <iostream>
#include <boost/range.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
int main()
{
std::map<int, int> foo = {{1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}};
std::map<int, int> bar = {{2, 0}, {4, 0}, {5, 0}};
using namespace boost::adaptors;
std::cout << includes(foo | map_keys, bar | map_keys);
}
Here is a live example.