Is there any STL/Boost function in C++ that allows me to find the indices of all unique elements in a vector?
I have seen many solutons to find unique elements, but I need their index.
vector<int> v = { 1,1,1, 2,2,2,2, 3, 3, ,4,5,5,5,5,5,5 };// already sorted
Either I need first index of unique elemnt
vector<int> unique_index={0,3,7,9,10};
or I need last index of unique elements
vector<int> unique_index={2,6,8,9,15};
A simple way (aside from just keeping track of what the last element was) is to use a std::set to test if the current element is unique in the elements of the vector -- seen so far, and populate your unique indexes as you go. This provides a single pass through to collect the indexes where the first unique element is seen, e.g.
#include <iostream>
#include <vector>
#include <set>
int main (void) {
std::vector<int> v = { 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5 },
uniqueidx{};
std::set<int> s{};
for (size_t i = 0; i < v.size(); i++)
if (s.insert(v[i]).second)
uniqueidx.push_back(i);
for (const auto i : uniqueidx)
std::cout << i << '\n';
}
Example Use/Output
$ ./bin/set_index_of_unique_in_vector
0
3
7
10
11
(note: the last two values are 10 and 11, not 9 and 10 -- you are missing a value in your vector initialization, e.g. 3, ,4)
If you just wanted a simple-old loop to do the same thing, you could use:
#include <iostream>
#include <vector>
int main (void) {
std::vector<int> v = { 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5 },
uniqueidx{};
for (size_t i = 0; i < v.size(); i++)
if (!i || v[i-1] != v[i])
uniqueidx.push_back(i);
for (const auto i : uniqueidx)
std::cout << i << '\n';
}
(same output)
The benefit of the approach with std::set is you leave the determination of uniqueness up to std::set, with the simple loop -- it's up to you....
Look things over and let me know if you have questions.
Similar to David's answer of using std::set, you could also use std::map with its member function try_emplace(key, value):
#include <iostream>
#include <vector>
#include <map>
int main (void) {
std::vector<int> v = { 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5 };
std::map<int, int> m;
for (size_t i = 0; i < v.size(); i++)
{
// `i` is only entered if the `m[v[i]]` isn't filled yet.
m.try_emplace(v[i], i);
}
for (auto [valueFromV, indexFromV] : m)
{
std::cout << indexFromV << '\n';
}
}
you can change this code and using int instead of a string.
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <iterator>
#include <algorithm>
std::map<std::string, int> get_unique_indices(const std::vector<std::string>& products) {
std::vector<std::string> tempProducts(products);
std::map<std::string, int> result;
std::sort(std::begin(tempProducts), std::end(tempProducts));
for (auto it1 = std::begin(tempProducts), it2 = it1; it1 != std::end(tempProducts); it1 = it2) {
int duplication = 0;
for (; it2 != std::end(tempProducts) && (*it2 == *it1); ++it2) {
duplication++;
}
if (duplication == 1) {
result.insert({ *it1, std::find(std::begin(products), std::end(products), *it1) - std::begin(products) });
}
}
return result;
}
int main()
{
using namespace std;
vector<string> products = { "apple", "orange", "lemon", "apple", "kivi", "orange", "kivi", "melon"};
auto result = get_unique_indices(products);
for (const auto& [key, value] : result) {
std::cout << key << " " << value << std::endl;
}
return 0;
}
At first, I create another vector to save the original data. (Cause the position of the item will change in the sort method).
then I use two iterators to find duplicate items and count them. the result will be saved in a map instance.
this code must compile with c++17 onwards.
Related
I want to compare the elements in array whether there is a similar element is present or not?
I am using two loops and it gives TLE can anyone help me in this?
code:-
int main() {
int arr=[1, 2, 3, 3, 4, 5, 6, 5, 6]
int max=0;
int k;
int c=0;
for (int i=0;i<n;i++) {
for (int j= i+1;j<n;j++) {
if (a[i]==a[j]) {
c++;
if (k>max) {
max= k;
}
}
}
}
}
how to optimize this?
The std::sort function time complexity is nlog(n) (According to Wikipedia, it's Intro Sort)
So one approach for your question is to:
Sort the array (It's better to use std::vector instead of C-Style array)
Compare every element with the next one (Which is going to be O(n))
If the two elements are equal, so show it or add it to a std::set to use it later
Here is my implementation for the algorithm that I mentioned before:
#include <vector>
#include <iostream>
#include <set>
#include <algorithm>
int main() {
std::vector<int> data{1, 2, 3, 4, 6, 2, 7, 1, 5, 4};
std::set<int> duplicatedValues;
std::sort(data.begin(), data.end());
for (int i{0}; i < data.size()-1; ++i)
if (data[i] == data[i+1])
duplicatedValues.insert(data[i]);
for (int value : duplicatedValues)
std::cout << value << " ";
std::cout << std::endl;
}
So time complexity for code above is going to be nlog(n)
Note: I should add that if you have n data and all of them are between range of 0 to n-1, We have O(n) for that, take a look at here.
Second Approach: (By using std::unordered_map)
(It takes a little shorter time than the previous approach, but roughly 2 times more memory!)
We can add every element in a dictionary and if we had that same data again in our array, increase its number in dictionary; and finally, show elements that have number more than 1.
#include <iostream>
#include <unordered_map>
#include <vector>
#include <stdlib.h>
int main() {
std::vector<int> data{1, 2, 8, 2, 5, 4, 1, 9, 2, 3, 8};
std::unordered_map<int, int> duplicatedData;
for (int i{0}; i < data.size(); ++i) {
if (duplicatedData.find(data[i]) == duplicatedData.end()) {
duplicatedData[data[i]] = 1;
}
else {
duplicatedData[data[i]] += 1;
}
}
for (auto x : duplicatedData) {
if (x.second > 1) {
std::cout << x.first << " ";
}
}
std::cout << std::endl;
}
EDIT: I recorded a video for this, with performance measurement in here
i will do it like
#include <algorithm>
#include <vector>
#include <execution>
#include <atomic>
#include <iostream>
int main()
{
int arr[] = {1,2,3,5,6};
std::atomic<bool> com_tr = false;
std::for_each(std::execution::par, &arr[0], (&arr[0] + sizeof(arr)/sizeof(int)),
[&](auto a)
{ com_tr = std::count(&arr[0], (&arr[0] + sizeof(arr)/sizeof(int)), a) == 1 ? (bool)com_tr : !false; });
if (com_tr) std::cout << "have one " << std::endl;
}
compiled with c++ -std=c++17 -O3 '/home/alex/etr/b.cpp' -o t -ltbb
but if it's really all you need to accomplish better would be std::any_of()
I am trying to replace the elements in a 2D vector (vector<vector<int>>). I want to change the elements not only by one value, but by a list, which means, for example, change 1,3,4,5,8,9 to 1,2,3,4,5,6 one-to-one correspondence. I have made a very slow code with double loops. Is there any way to speed up the process, with new function or sort the element? Because my 2D vector is very big, 3*300000 actually. My example code is below:
int myints[] = { 1,3,4,5,8,9 };
int myints2[] = { 1,2,3,4,5,6 };
std::vector<int> vals (myints, myints+6);
std::vector<int> vals2 (myints2, myints2+6);
vector<vector<int>> V0(3);
V0[0]={1,4,5};
V0[1]={3,1,8};
V0[2]={1,9,4};
for (size_t j = 0; j < V0.size(); j++)
{
for (int i = 0; i < vals.size(); i++)
replace(V0[j].begin(), V0[j].end(), vals[i], vals2[i]);
};
The ideal output V0 should be
1 3 4
2 1 5
1 6 3
You can use an unordered_map to replace each value directly, instead of searching through the whole vector for each replacement:
#include <vector>
#include <unordered_map>
#include <algorithm>
#include <iostream>
using namespace std;
int main()
{
unordered_map<int, int> replacements{{1, 1}, {3, 2}, {4, 3}, {5, 4}, {8, 5}, {9, 6}};
vector<vector<int>> v0(3);
v0[0] = {1, 4, 5};
v0[1] = {3, 1, 8};
v0[2] = {1, 9, 4};
for_each(v0.begin(), v0.end(), [&](vector<int>& v)
{
transform(v.begin(), v.end(), v.begin(), [&](int val)
{
auto it = replacements.find(val);
return it != replacements.end() ? replacements[val] : val;
});
});
// Print
for (auto& v : v0)
{
cout << "[ ";
for (auto val : v)
{
cout << val << ", ";
}
cout << "]" << endl;
}
return 0;
}
Output:
[ 1, 3, 4, ]
[ 2, 1, 5, ]
[ 1, 6, 3, ]
In C++17, you may also choose a parallel execution policy in for_each and/or transform, since all the changes can be done in parallel.
Goal: Return all elements in vector A that appear N times and put results in vector B.
Expected Result:
--Begin With---
Vector A=(10,20,30,30,40,50,100,50,20,100,10,10,200,300)
Do some code to return the name of elements that appear in Vector A
when N=3
Result should be Vector B=(10) //because only 10 is in vector A N=3 times.
My Attempt:
I got the counts of all the elements placed into another vector but I don't have the part that can give back all of the elements that appear N times. I'm very flexible with how it can be done if it means a speed increase.
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <iterator> // std::back_inserter
#include <algorithm> // std::copy
int main()
{
std::vector<int> v{ 1, 2, 3, 4, 4, 3, 7, 8, 9, 10 };
std::vector<std::pair<int, int> > shows;
int target1;
int num_items1;
int size = static_cast<int>(v.size());
for(int x=0; x<size; x++)
{
target1 = v[x];
num_items1 = std::count(v.begin(), v.end(), target1);
shows.push_back(std::make_pair(target1, num_items1));
std::cout << "number: " << target1 << " count: " << num_items1 << '\n';
}
}
ACCEPTED SOLUTION TO QUESTION
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <iterator> // std::back_inserter
#include <algorithm> // std::copy
#include <set>
#include <map>
using namespace std;
int main()
{
std::vector<int> v{ 1, 2, 3, 4, 4, 3, 7, 8, 9, 10 };
std::vector<int> shows;
std::map<int, int> freqOfv;
for(const auto& i: v)
{
freqOfv[i]++;
}
std::set<int> s(v.begin(), v.end());
int N = 2; //Can be read from stdin as well...
for ( auto it = s.begin(); it != s.end(); it++ )
{
if(freqOfv[*it] ==N)
{
shows.push_back(*it);
}
}
for (std::vector<int>::const_iterator i = shows.begin(); i != shows.end(); ++i)
{
std::cout << *i << ' ';
}
return 0;
}
As suggested in the comments, std::map will simplify the code:
int main()
{
std::vector<int> v{ 1, 2, 3, 4, 4, 3, 7, 8, 9, 10 };
std::map<int, int> freqOfv;
for(const auto& i: v)
freqOfv[i]++;
int N = 2; //Can be read from stdin as well...
for(const auto& i: freqOfv)
{
if(N == i.second)
std::cout << "The value " << i.first << " occurs " << N << " times." << std::endl;
}
}
This produces the following output:
The value 3 occurs 2 times.
The value 4 occurs 2 times.
Of course, you need #include <map> at the beginning to use maps in your code.
I have vectors and I would like to retrieve one vector that contains all entries which aren't duplicated anywhere in all input vectors.
#include <vector>
int main() {
std::vector<int> a = {2, 1, 3};
std::vector<int> b = {99, 1, 3, 5, 4};
std::vector<int> c = {5, 6, 7, 1};
// magic to retrieve {2, 99, 4, 6, 7} (order doesn't matter)
}
Is there a library function that can help performing this task efficiently?
I'm not tied to using vectors. The solution could include lists, sets, or whatever are most appropriate for the task.
Using unordered_map, O(N) space complexity and O(N) time complexity:
#include <vector>
#include <unordered_map>
#include <iostream>
std::vector<int>
get_unique_values(std::initializer_list<std::vector<int>> vectors)
{
std::unordered_map<int, size_t> tmp;
auto insert_value_in_tmp = [&tmp](int v) {
auto i = tmp.find(v);
if (i == tmp.end())
tmp[v] = 1;
else if (i->second != 2)
i->second = 2;
};
for ( auto& vec : vectors) {
for ( auto vec_value : vec ) {
insert_value_in_tmp(vec_value);
}
}
std::vector<int> result;
for (auto v : tmp) {
if (v.second == 1)
result.push_back(v.first);
}
return result;
};
int main() {
std::vector<int> a = {2, 1, 3};
std::vector<int> b = {99, 3, 5, 4};
std::vector<int> c = {5, 6, 7};
std::vector<int> result = get_unique_values({a,b,c});
for (auto v : result) {
std::cout << v << " ";
}
std::cout << '\n';
return 0;
}
I have the vector that defines the order of items (0..N-1), e.g.
{5, 0, 4, 3, 2, 1, 7, 6}.
I have to sort subsets of that vector. So, for {0, 1, 2, 5} I should get {5, 0, 2, 1}.
I tested the following solutions:
Create a set of items in a subset, then clear the subset, go through the ordering vector, adding only items in the set.
Create new sorted vector by going through the ordering vector, adding only items found by in the subset by std::lower_bound.
The second solution seems much faster, although it needs subset to be sorted. Are there any better solutions? I am using C++/STL/Qt, but the problem is probably not language-dependent.
Check this code :-
#include <iostream>
#include <algorithm>
#include <vector>
struct cmp_subset
{
std::vector<int> vorder;
cmp_subset(const std::vector<int>& order)
{
vorder.resize(order.size());
for (int i=0; i<order.size(); ++i)
vorder.at(order[i]) = i;
}
bool operator()(int lhs, int rhs) const
{
return vorder[lhs] < vorder[rhs];
}
};
int main()
{
std::vector<int> order = {5, 0, 4, 3, 2, 1, 7, 6};
std::vector<int> subset = {0, 1, 2, 5};
for (auto x : subset)
std::cout << x << ' ';
std::cout << '\n';
std::sort(subset.begin(), subset.end(), cmp_subset(order));
for (auto x : subset)
std::cout << x << ' ';
std::cout << '\n';
return 0;
}
The code is copied from here