Related
I want to write a function that changes the vector [2, 1, 4, 0, 5] to
[2, 2, 1, 4, 4, 4, 4, 5, 5, 5, 5, 5]
I could do it by popping the vector into an array and then pushing the elements back to the vector.
How can I use insert to do it? Can I modify the following program? What is the most efficient way?
void timesDuplicates(vector<int>& a)
{
int s = a.size(), count = 0;
for(int i = 0; count < s ; i+=a[i], count++) {
if(a[i] == 0) continue;
a.insert(a.begin()+i, a[i], a[i]);
}
}
How can I use insert to do it? Can I modify the following program?
Regarding efficiency, your vector might undergo several reallocations on each time when insertion happens, as in provided code no memory has been std::vector::reserve ed, even it could have been done by summing up the elements. Like #IgorTandetnik pointed out, transforming the passed vector, wouldn't be possible as well.
The easiest way you could do is, create a new vector in which simply std::vector::insert elements as per the number of elements exist in the passed vector.
Following is an example code. (See Live)
#include <iostream>
#include <vector>
#include <numeric> // std::accumulate
std::vector<int> timesDuplicates(const std::vector<int>& vec)
{
std::vector<int> result;
// reserve the amount of memory for unwanted reallocations
result.reserve(std::accumulate(std::cbegin(vec), std::cend(vec), 0));
// you do not need to check element == 0 here
// as std::vector::insert(end, 0, 0) will insert nothing
for (const int element : vec) result.insert(result.end(), element, element);
// return the result
return result;
}
int main()
{
const auto result{ timesDuplicates({ 2, 1, 4, 0, 5 }) };
for (const int ele : result) std::cout << ele << " ";
return 0;
}
Or if you do not believe in NRVO or copy elision to happen, pass the vector result as a parameter(ref) to the function, after reserving the memory that it needs.
#include <iostream>
#include <vector>
#include <numeric> // std::accumulate
void timesDuplicates(
const std::vector<int>& vec,
std::vector<int>& result)
{
for (const int element : vec)
result.insert(result.end(), element, element);
}
int main()
{
const std::vector<int> vec{ 2, 1, 4, 0, 5 };
std::vector<int> result;
result.reserve(std::accumulate(std::cbegin(vec), std::cend(vec), 0));
timesDuplicates(vec, result);
for (const int ele : result) std::cout << ele << " ";
return 0;
}
Try this snippet with recursion. Since you are popping and pushing into a new temporary vector, push_back will suffice ( insert will require you to locate new insert positions)
void timesDuplicates(vector<int>& vec, int idx = 0)
{
static vector<int> result;
int v = vec[idx]; // get value
for (int i = 0; i < v; i++) // multiply value
{
result.push_back(v); // push value to temp vector (result)
}
if (idx == vec.size() - 1) { // border condition
vec.swap(result); // swap result
return;
}
timesDuplicates(vec, ++idx); // increment index
}
void main()
{
vector<int> vec = { 2, 1, 4, 0, 5 };
timesDuplicates(vec);
for (auto e : vec)
cout << e << " ";
cout << "\n";
}
As the title says, I'm looking for a way to sort a vector without modifying the original one.
My first idea is of course to create a copy of the vector before the sort, e.g.:
std::vector<int> not_in_place_sort(const std::vector<int>& original)
{
auto copy = original;
std::sort(copy.begin(), copy.end());
return copy;
}
However, maybe there is a more efficient way to perform the sort using C++ standard algorithm (maybe a combination of sort and transform?)
Use partial_sort_copy. Here is an example:
vector<int> v{9,8,6,7,4,5,2,0,3,1};
vector<int> v_sorted(v.size());
partial_sort_copy(begin(v), end(v), begin(v_sorted), end(v_sorted));
Now, v remains untouched but v_sorted contains {0,1,2,3,4,5,6,7,8,9}.
Here is my favorite. Sort an index and not the original array/vector itself.
#include <algorithm>
int main() {
int intarray[4] = { 2, 7, 3, 4 };//Array of values
//or you can have vector of values as below
//std::vector<int> intvec = { 2, 7, 3, 4 };//Vector of values
int indexofarray[4] = { 0, 1, 2, 3 };//Array indices
std::sort(indexofarray, indexofarray + 4, [intarray](int index_left, int index_right) { return intarray[index_left] < intarray[index_right]; });//Ascending order.
//have intvec in place of intarray for vector.
}
After this, indexofarray[] elements would be 0, 2, 3, 1, while intarray[] is unchanged.
As suggested in the comments pass the function argument by value std::vector<int> original:
#include <iostream>
#include <vector>
#include <algorithm>
std::vector<int> not_in_place_sort(std::vector<int> original) {
std::sort(original.begin(), original.end());
return original;
}
int main() {
std::vector<int> v = { 8, 6, 7, 2, 3, 4, 1, 5, 9 };
std::vector<int> v2 = not_in_place_sort(v); // pass the vector by value
std::cout << "v1: " << '\n';
for (auto el : v) {
std::cout << el << ' ';
}
std::cout << "\nv2: " << '\n';
for (auto el : v2) {
std::cout << el << ' ';
}
}
That will sort a copy of your original vector leaving the original intact.
As pointed out below this might restrict some optimizations such as RVO but will call vector's move constructor in the return statement instead.
For the case where you are interested in proxy sorting (sorting an index list), you may want to implement a more flexible algorithm that allows you to deal with containers which do not support random access (such as std::list). For example:
#include <algorithm>
#include <iostream>
#include <list>
#include <numeric>
#include <vector>
template <typename Container>
auto sorted_indices(const Container& c) {
std::vector<typename Container::size_type> indices(c.size());
std::iota(indices.begin(), indices.end(), 0);
std::sort(indices.begin(), indices.end(), [&c](auto lhs, auto rhs) {
return (*(std::next(c.begin(), lhs)) < *(std::next(c.begin(), rhs)));
});
return indices;
}
template <typename Container, typename Indices>
auto display_sorted(const Container& c, const Indices& indices) {
std::cout << "sorted: ";
for (auto&& index : indices) {
std::cout << *(std::next(c.begin(), index)) << " ";
}
std::cout << std::endl;
}
template <typename Container>
auto display_sorted(const Container& c) {
return display_sorted(c, sorted_indices(c));
}
template <typename Container>
auto display(const Container& c) {
std::cout << "as provided: ";
for (auto&& ci : c) std::cout << ci << " ";
std::cout << std::endl;
}
int main() {
// random access
const std::vector<int> a{9, 5, 2, 3, 1, 6, 4};
display(a);
display_sorted(a);
display(a);
std::cout << "---\n";
// no random access
const std::list<int> b{9, 5, 2, 3, 1, 6, 4};
display(b);
display_sorted(b);
display(b);
}
Sample run:
$ clang++ example.cpp -std=c++17 -Wall -Wextra
$ ./a.out
as provided: 9 5 2 3 1 6 4
sorted: 1 2 3 4 5 6 9
as provided: 9 5 2 3 1 6 4
---
as provided: 9 5 2 3 1 6 4
sorted: 1 2 3 4 5 6 9
as provided: 9 5 2 3 1 6 4
As you would expect, relying on proxy sorting could have important performance implications. For example: every time you want to traverse in order, you will possibly incur cache misses. In addition, the traversal will have the same complexity as the underlying container for random access: In the case of std::vector, std::next(v.begin(), n) is O(1), but in the case of std::list, std::next(l.begin(), n) is O(n).
For int's it doesn't make much difference if you're sorting an index or making a copy & sorting the copy; the data still needs to be initialized, and in the case of the indexes, this will involve a loop assigning values rather than faster memcpy routines; so may end up slower; in addition you're going to be jumping around memory lots more; so now the cache can't do its job nicely.
For larger objects I'd not sort the index, but use a vector of pointers. The copy of the pointers is cheap compared to copying the objects themselves; the containers are still obvious because they're containing pointers of your object; and the sort isn't attempting to reference another vector.
You can create another vector to store the indices. Here is the code:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
vector<int> numbers = {50,30,20,10,40};
vector<int> indexOfNumbers;
for(int i = 0; i < numbers.size(); i++)
{
indexOfNumbers.push_back(i);
}
// Now, indexOfNumbers = [0,1,2,3,4]
std::sort(
indexOfNumbers.begin(), indexOfNumbers.end(),
[numbers](int leftIndex, int rightIndex)
{
return numbers[leftIndex] < numbers[rightIndex]; // sort in ascending order
}
);
// After sorting, indexOfNumbers = [3, 2, 1, 4, 0]
// Access the sorted elements
cout << "Accessing the sorted elements : ";
for(int i = 0; i < numbers.size(); i++)
{
cout << numbers[indexOfNumbers[i]] << " ";
}
// prints numbers in sorted order i.e. [10,20,30,40,50]
return 0;
}
Source: Made slight modification according to Tyrer's answer (https://stackoverflow.com/a/47537314)
This mean that while I am sorting the v2 in the nonincreasing order, the v1 should looks like this:
Vectors look as following.
v1 = {0, 5, 5, 2, 10};
v2 = {0 ,2, 6, 20, 5};
The output:
v1 = {2, 5, 10, 5, 0};
v2 = {20, 6, 5, 2, 0};
I was trying to solve that problem mixing std::sort and lambdas.
That is why I have read several questions about std::sort, there were no answers which solve the problem similar to mine so that is why I am asking.
I am especially interested in the answers which contains it's usage or other features of C++11 and C++14.
It is not a question like:
"I completely do not know what to do."
I know how to achieve the output using C++98, but I am wondering if there is an more efficient and prettier way to do it.
Big thanks for your help :)
You could zip, sort, and unzip.
#include <iostream>
#include <vector>
#include <algorithm>
//converts two vectors into vector of pairs
template <typename T, typename U>
auto zip(T t, U u) {
std::vector<std::pair<typename T::value_type,typename U::value_type>> pairs;
for (size_t i = 0; i < t.size(); ++i){
pairs.emplace_back(u[i],t[i]);
}
return pairs;
}
//converts a vector of pairs, back into two two vectors
template <typename T, typename U, typename V>
void unzip(V pairs, T & t, U & u) {
for (auto const& it: pairs){
u.emplace_back(it.first);
t.emplace_back(it.second);
}
}
int main(){
//vectors
std::vector<int> v1 = {0, 5, 5, 2, 10};
std::vector<int> v2 = {0 ,2, 6, 20, 5};
//zip vectors
auto pairs = zip(v1,v2);
//sort them
std::sort(pairs.begin(),pairs.end(),std::greater<>());
//unzip them
v1.clear();
v2.clear();
unzip(pairs,v1,v2);
//print
std::cout << '\n';
for (auto i: v1) std::cout << i << ' ';
std::cout << '\n';
for (auto i: v2) std::cout << i << ' ';
std::cout << '\n';
}
Well, I don't if this is would be efficient, or not, but this demonstrates how to do this with std::generate, std::sort, and std::transform, with some extra seasoning from mutable lambdas, and iterators.
#include <algorithm>
#include <iostream>
int main()
{
std::vector<int> v1={0, 5, 5, 2, 10},
v2 = {0, 2, 6, 20, 5};
std::vector<int> index;
index.resize(5);
std::generate(index.begin(), index.end(),
[n=0]
()
mutable
{
return n++;
});
std::sort(index.begin(), index.end(),
[&]
(auto &a, auto &b)
{
return v2[b] < v2[a];
});
std::vector<int> v1_out, v2_out;
std::transform(index.begin(), index.end(),
std::back_insert_iterator<std::vector<int>>(v1_out),
[&]
(auto a)
{
return v1[a];
});
std::transform(index.begin(), index.end(),
std::back_insert_iterator<std::vector<int>>(v2_out),
[&]
(auto a)
{
return v2[a];
});
for (auto n: v1_out)
std::cout << n << ' ';
std::cout << std::endl;
for (auto n: v2_out)
std::cout << n << ' ';
std::cout << std::endl;
}
Big thanks for all your answers. I found an easy way to achieve the same effect and the idea comes from this answer.
1.
Firstly I used all code from answer which I linked, so it is:
template <typename T>
vector<size_t> sort_indexes(const vector<T> &v)
{
// initialize original index locations
vector<size_t> idx(v.size());
for (size_t i = 0; i != idx.size(); ++i) idx[i] = i;
// sort indexes based on comparing values in v
sort(idx.begin(), idx.end(),
[&v](size_t i1, size_t i2)
{
return v[i1] >= v[i2];
});
return idx;
}
2.Then I did a function which will sort first and a second vector using the third one.To achieve it I had to create temporary vectors one for a first vector, one for a second, and two for the last one.
Why two?
Because I have to remember the sorted indexes of the third vector and I need a one temporary to which I will be pushing elements of the original third vector according to sorted indexes.
void SortByIndexes(vector<int>& Pi,vector<int> &Wi,vector<int>& PidivWi)
{
vector<int> Pitemp, Witemp, PidivWitemp,SortedIndexes;
for (auto i : sort_indexes(PidivWi))
{
SortedIndexes.push_back(i);
}
for (auto i : SortedIndexes)
{
Pitemp.push_back(Pi[i]);
Witemp.push_back(Wi[i]);
PidivWitemp.push_back(PidivWi[i]);
}
swap(Pi, Pitemp);
swap(Wi, Witemp);
swap(PidivWi,PidivWitemp);
}
3. After sorting just swap sorted vectors with original ones.
Done.
Thank you all guys.
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
Pseudo Code:
int arr[ 5 ] = { 4, 1, 3, 2, 6 }, x;
x = find(3).arr ;
x would then return 2.
The syntax you have there for your function doesn't make sense (why would the return value have a member called arr?).
To find the index, use std::distance and std::find from the <algorithm> header.
int x = std::distance(arr, std::find(arr, arr + 5, 3));
Or you can make it into a more generic function:
template <typename Iter>
size_t index_of(Iter first, Iter last, typename const std::iterator_traits<Iter>::value_type& x)
{
size_t i = 0;
while (first != last && *first != x)
++first, ++i;
return i;
}
Here, I'm returning the length of the sequence if the value is not found (which is consistent with the way the STL algorithms return the last iterator). Depending on your taste, you may wish to use some other form of failure reporting.
In your case, you would use it like so:
size_t x = index_of(arr, arr + 5, 3);
Here is a very simple way to do it by hand. You could also use the <algorithm>, as Peter suggests.
#include <iostream>
int find(int arr[], int len, int seek)
{
for (int i = 0; i < len; ++i)
{
if (arr[i] == seek) return i;
}
return -1;
}
int main()
{
int arr[ 5 ] = { 4, 1, 3, 2, 6 };
int x = find(arr,5,3);
std::cout << x << std::endl;
}
The fancy answer:
Use std::vector and search with std::find
The simple answer
Use a for loop
If the array is unsorted, you will need to use linear search.
#include <vector>
#include <algorithm>
int main()
{
int arr[5] = {4, 1, 3, 2, 6};
int x = -1;
std::vector<int> testVector(arr, arr + sizeof(arr) / sizeof(int) );
std::vector<int>::iterator it = std::find(testVector.begin(), testVector.end(), 3);
if (it != testVector.end())
{
x = it - testVector.begin();
}
return 0;
}
Or you can just build a vector in a normal way, without creating it from an array of ints and then use the same solution as shown in my example.
int arr[5] = {4, 1, 3, 2, 6};
vector<int> vec;
int i =0;
int no_to_be_found;
cin >> no_to_be_found;
while(i != 4)
{
vec.push_back(arr[i]);
i++;
}
cout << find(vec.begin(),vec.end(),no_to_be_found) - vec.begin();
We here use simply linear search. At first initialize the index equal to -1 . Then search the array , if found the assign the index value in index variable and break. Otherwise, index = -1.
int find(int arr[], int n, int key)
{
int index = -1;
for(int i=0; i<n; i++)
{
if(arr[i]==key)
{
index=i;
break;
}
}
return index;
}
int main()
{
int arr[ 5 ] = { 4, 1, 3, 2, 6 };
int n = sizeof(arr)/sizeof(arr[0]);
int x = find(arr ,n, 3);
cout<<x<<endl;
return 0;
}
You could use the STL algorithm library's find function provided
#include <iostream>
#include <algorithm>
using std::iostream;
using std::find;
int main() {
int length = 10;
int arr[length] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int* found_pos = find(arr, arr + length, 5);
if(found_pos != (arr + length)) {
// found
cout << "Found: " << *found_pos << endl;
}
else {
// not found
cout << "Not Found." << endl;
}
return 0;
}
There is a find(...) function to find an element in an array which returns an iterator to that element. If the element is not found, the iterator point to the end of array.
In case the element is found, we can simply calculate the distance of the iterator from the beginning of the array to get the index of that element.
#include <iterator>
using namespace std;
int arr[ 5 ] = { 4, 1, 3, 2, 6 }
auto it = arr.find(begin(arr), end(arr), 3)
if(it != end(arr))
cerr << "Found at index: " << (it-begin(arr)) << endl;
else
cerr << "Not found\n";