Related
How do I use a for loop to erase elements from a vector by its index ? I am getting a vector out of range error. I have a sample code below.
vector<int> to_erase = {0, 1, 2};
vector<int> data = {3, 3, 3, 3};
for(int i = 0; i < to_erase.size(); i++) {
data.erase(data.begin() + to_erase[i]);
}
I think it is because the size of my vector reduces through every iteration therefore it cannot access index 2.
You would normally employ the erase–remove idiom to delete multiple elements from a vector efficiently (erasing them one by one is generally less efficient, and, as you’ve seen, not always trivial). In its most general form, the idiom looks like this:
data.erase(remove_algorithm(begin(data), end(data)), end(data));
In your case, the remove_algorithm is based off indices in another vector, so we need to provide those as well:
data.erase(
remove_indices(begin(data), end(data), begin(to_erase), end(to_erase)),
end(data));
Unfortunately, such an algorithm isn’t contained in the standard library. However, it’s trivial to write yourself1:
template <typename It, typename It2>
auto remove_indices(It begin, It end, It2 idx_b, It2 idx_e) -> It {
using idx_t = typename std::iterator_traits<It2>::value_type;
std::sort(idx_b, idx_e, std::greater<idx_t>{});
for (; idx_b != idx_e; ++idx_b) {
auto pos = begin + *idx_b;
std::move(std::next(pos), end--, pos);
}
return end;
}
Here, we first sort the indices to be removed from largest to smallest. Next, we loop over those indices. We then (maximally efficiently) move all elements between the current position (to be deleted) and the end of the vector forward by one. Subsequently, the end is decreased by one (to account for the fact that an element got deleted).
Live code
1 *Ahem* Once you’ve removed all the silly typos in your code.
I think it is because the size of my vector reduces through every iteration
Yes!
You could do it by keeping an extra variable, which counts the elements that are deleted, like this:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> to_erase = {0, 1, 2};
vector<int> data = {3, 3, 3, 3};
int count_removed = 0;
for(unsigned int i = 0; i < to_erase.size(); i++)
data.erase(data.begin() + to_erase[i] - count_removed++);
for(unsigned int i = 0; i < data.size(); ++i)
cout << data[i] << "\n";
return 0;
}
Output:
3
I had the same problem when I first used std::erase(), good question, +1.
Deleting a collection of elements meanwhile you iterate, is unsafe and probalby expensive. I would suggest that each element that meets your criteria gets swapped with an element at the end. (at the end because will be cheaper to erase from the end. You can keep track of how much back you came from the end of the vector (based on the number of swap), and break early our of the loop. Now based on how many elements you swapped you can do something like:
data.resize(data.size() - reverse_counter);
or
int pos = data.size() - reverse_counter;
data.erease(data.begin()+pos, data.end();
It is sudo code just to explain the idea.
As mentioned in the reference, erase not at the end cause re-allocation, which is expensive. Something worth keep in mind:
http://www.cplusplus.com/reference/vector/vector/erase/
I think this is a bad design, because you will change the for loop invariant and will need a lot of workaround to make this happen. Anyway, if you really want to use a for loop, you MAY flag what you whant to delete and run a stl remove_if, something like:
#include <iostream>
#include <vector>
#include <limits>
#include <algorithm>
using namespace std;
int main() {
vector<int> to_erase = {0, 1, 2};
vector<int> data = {3, 3, 3, 3};
cout << "Before:\n" ;
for(int i=0; i<data.size(); i++)
cout << i << "\t";
cout << endl;
for(int i=0; i<data.size(); i++)
cout << data[i] << "\t";
cout << endl;
for(int i = 0; i < to_erase.size(); i++) {
//data.erase(data.begin() + to_erase[i]);
data[i] = numeric_limits<int>::max();
}
data.erase(remove_if(data.begin(),
data.end(),
[](int i){return i==numeric_limits<int>::max();}), data.end());
cout << "Next:\n" ;
for(int i=0; i<data.size(); i++)
cout << i << "\t";
cout << endl;
for(int i=0; i<data.size(); i++)
cout << data[i] << "\t";
return 0;
}
This question already has answers here:
C++ sorting and keeping track of indexes
(16 answers)
Closed 7 years ago.
Suppose I have code such as the following, to sort elements of a data structure in another data structure, but keep a record of their original indices:
std::vector<int> numbers = {..};
std::vector<std::pair<int, std::vector<int>::size_type>> temp;
for (std::vector<int>::size_type i = 0; i < numbers.size(); i++)
{
temp.push_back({ numbers[i], i });
}
std::sort(temp.begin(), temp.end(), [](const auto& x, const auto& y) { return x.first < y.first; });
So far so good. But what I really want is to store the data and indices in different data structures:
std::vector<int> sorted;
std::vector<std::vector<int>::size_type> indices;
Such that the element at sorted[i] was at index indices[i] in the original data structure.
Other than rolling out my own sorting algorithm, or splitting the data structure after the fact, is there any easy trick using the standard library to do this?
The idea is to store your data in one container and have other containers with indices (based on different sorting criteria).
The idea here is to write function objects that compare two indices based on the sorting criteria and the value in the data container.
The next step is to pass your function object to the appropriate sorting function.
Clarifying Thomas Matthew answer ... Create a vectors of indices 0 to n-1. Use a lambda function to sort the vector of indices according to the data vector. Example code including an optional reorder in place function.
void reorder(std::vector<int>& vA, std::vector<size_t> vI);
int main()
{
std::vector <int> vdata = { 7,2,4,5,3,1,6,0 }; // original data
std::vector <size_t> vindex = { 0,1,2,3,4,5,6,7 }; // original indices
std::sort(vindex.begin(), vindex.end(), [&vdata](size_t i, size_t j)
{return vdata[i] < vdata[j];});
for (size_t i = 0; i < vdata.size(); i++)
std::cout << "[" << vindex[i] << "]=" << vdata[vindex[i]] << " ";
std::cout << std::endl;
reorder(vdata, vindex);
for (size_t i = 0; i < vdata.size(); i++)
std::cout << "[" << i << "]=" << vdata[i] << " ";
return 0;
}
// every move places an element in it's final location
// vI passed by value (copy)
void reorder(std::vector<int>& vA, std::vector<size_t> vI)
{
size_t i, j, k;
int tA;
for (i = 0; i < vA.size(); i++) {
if (i != vI[i]) {
tA = vA[i];
k = i;
while (i != (j = vI[k])) {
vA[k] = vA[j];
vI[k] = k;
k = j;
}
vA[k] = tA;
vI[k] = k;
}
}
}
i am currently trying to learn some C++ and now i got stuck in an exercise with vectors. So the task is to read ints from a text file and store them in the vector which should be dynamic.
I guess there is something wrong with the while-loop?
If I start this, the program fails and if I set the vector size to 6, I get
6 0 0 0 0 0 as output.
Thanks for any hints.
int main()
{
const string filename = "test.txt";
int s = 0;
fstream f;
f.open(filename, ios::in);
vector<int> v;
if (f){
while(f >> s){
int i = 0;
v[i] = s;
i = i+1;
}
f.close();
}
for(int i = 0; i < 6; i++){
cout << v[i] << "\n";
}
}
You don't grow the vector. It is empty and cannot hold any ints. You'll need to either resize it every time you want to add another int or you use push_back which automatically enlarges the vector.
You set i = 0 for every iteration so you would change the first value of the vector every iteration instead of the next one.
Go for:
v.push_back(s);
in your loop and
for(int i = 0; i < v.size(); i++) { // ...
Remark:
You normally don't hardcode vector sizes/bounds. One major point about using std::vector is its ability to behave dynamically with respect to its size. Thus, the code dealing with vectors should not impose any restrictions about the size of the vector onto the respective object.
Example:
for(int i = 0; i < 6; i++){ cout << v[i] << "\n"; }
requires the vector to have at least 6 elements, otherwise (less than 6 ints) you access values out of bounds (and you potentially miss elements if v contains more than 6 values).
Use either
for(int i = 0; i < v.size(); i++){ cout << v[i] << "\n"; }
or
for(std::vector<int>::const_iterator i = v.begin(); i != v.end(); ++i)
{
cout << *i << "\n";
}
or
for(auto i = v.begin(); i != v.end(); ++i)
{
cout << *i << "\n";
}
or
for(int x : v){ cout << x << "\n"; }
or
for(auto && x : v){ cout << x << "\n"; }
or
std::for_each(v.begin(), v.end(), [](int x){ std::cout << x << "\n"; });
or variants of the above which possibly pre-store v.size() or v.end()
or whatever you like as long as you don't impose any restriction on the dynamic size of your vector.
The issue is in the line i= 0. Fixing that will give an issue in the line v[i] = s.
You always initialise i to 0 in the while loop, and that is responsible for the current output. You should shift it out of the while loop.
After fixing that, you have not allocated memory to that vector, and so v[i] doesn't make sense as it would access memory beyond bounds. This will give a segmentation fault. Instead, it should be v.push_back(i), as that adds elements to the end of a vector, and also allocates memory if needed.
If you are using std::vector you can use v.push_back(i) to fill this vector
Error is this line int i = 0;
because you declare i=0 every time in while-loop.
To correct this move this line outside from loop.
Note: this will work, if you declare v like normal array for example int v[101]
When you use std vectors you can just push element at the end of vector with v.push_back(element);
v[i] = s; //error,you dont malloc room for vector
change into : v.push_back(s);
I'm using this approach: First find the largest among 5 numbers then save the subscript of array of the largest number in an "ivariable" after displaying the largest number, do like this
array[ivariable] = 0 ;
so that first largest set to zero and its no longer here in the array.
And do the same again, find the largest, but I'm not getting what I'm trying to.
Its a logical error.
Thanks
#include <iostream>
using namespace std;
int main(void)
{
int counter, large,number,det_2, i , large3, det_3= 0;
int det[5] = {0,0,0,0,0};
for(int k(0); k < 5 ; k++)
{
cout << "Enter the number " << endl ;
cin >> det[k] ;
}
for( i; i<5; i++)
{
large = det[i] ;
if (large > det_2)
{
det_2= large ;
counter = i ;
}
else
{
}
}
cout << "Largest among all is " << det_2 << endl;
det[i] = 0 ;
for( int j(0); j<5; j++)
{
large3 = det[j] ;
if(large3 > det_3)
{
det_3= large3 ;
}
else
{
}
}
cout << "Second largest " << large3 << endl ;
system("PAUSE");
}
You've got possible syntax and initialization errors. Fix those first:
for(int k(0); k < 5 ; k++): I've never seen an integer initialized this way. Shouldn't it be:
for (int k = 0; k < 5; k++) ? (Same with the last loop.)
Also,
for( i; i<5; i++)
The variable i is uninitialized. Variables are not initialized to any default value in C++. Because you've left it uninitialized, it might execute 5 times, no times, or 25,899 times. You don't know.
This should be:
for (i = 0; i < 5; i++)
But the whole thing could probably be a bit clearer anyway:
#include <iostream>
using namespace std;
int main(void)
{
int largest = -1;
int second_largest = -1;
int index_of_largest = -1;
int index_of_second_largest = -1;
int det[5] = {0, 0, 0, 0, 0};
for (int i = 0; i < 5; i++)
{
cout << "Enter the number " << endl;
cin >> det[i]; // assuming non-negative integers!
}
for (int j = 0; j < 5; j++) // find the largest
{
if (det[j] >= largest)
{
largest = det[j];
index_of_largest = j;
}
}
for (int k = 0; k < 5; k++) // find the second largest
{
if (k != index_of_largest) // skip over the largest one
{
if (det[k] >= second_largest)
{
second_largest = det[k];
index_of_second_largest = k;
}
}
}
cout << "Largest is " << largest << " at index " << index_of_largest << endl;
cout << "Second largest is " << second_largest <<
" at index " << index_of_second_largest << endl;
return 0;
}
Always give your variables values before you use them
det_2 = det[0];
counter = 0;
for (i = 1; i < 5; i++)
first problem I saw was that you are iterating using i as an index, but you don't initialize i.
code should be:
for(i = 0; i<5; i++)
^^^^
same goes for det_2. You compare elements against it, but do not initialize it. You should set it to det[0] before the loop where you use it.
third problem: Your "set largest value to zero after printing" sounds like it is there so that you can apply the same algorithm the second time.
You should create an additional function that gives you the index of the largest element, and call it like this:
int index = find_largest_index(a);
cout << "largest element: " << a[index] << endl;
a[index] = 0;
cout << "second largest element: " << a[ find_largest_index(a) ] << endl;
GCC 4.7.3: g++ -Wall -Wextra -std=c++0x largest.cpp
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
int main() {
std::cout << "Enter 5 numbers: ";
// Read 5 numbers.
std::vector<int> v;
for (auto i = 0; i < 5; ++i) {
int x = 0;
while (!(std::cin >> x)) {
// Error. Reset and try again.
std::cin.clear();
std::cin.ignore();
}
v.push_back(x);
}
// partition on element 3 (4th number)
std::nth_element(std::begin(v), std::next(std::begin(v), 3), std::end(v));
std::cout << "Two largest are: ";
std::copy(std::next(std::begin(v), 3), std::end(v), std::ostream_iterator<int>(std::cout, " "));
}
In the specific case of 5 elements, the algorithm you use is unlikely to make any real difference.
That said, the standard algorithm specifically designed for this kind of job is std::nth_element.
It allows you to find the (or "an", if there are duplicates) element that would end up on position N if you were to sort the entire collection.
That much is pretty obvious from the name. What's not so obvious (but is still required) is that nth_element also arranges the elements into two (or three, depending on how you look at it) groups: the elements that would short before that element, the element itself, and the elements that would sort after that element. Although the elements are not sorted inside of each of those groups, they are arranged into those groups -- i.e., all the elements that would sort before it are placed before it, then the element itself, then the elements that would sort after it.
That gives you exactly what you want -- the 4th and 5th elements of the 5 you supply.
As I said originally, in the case of just 5 elements, it won't matter much -- but if you wanted (say) the top 50000 out of ten million, choosing the right algorithm would make a much bigger difference.
nth_element isn't always suitable (or as efficient as it could be) as it needs to rearrange the input elements.
It's very common to want just the top two elements, and can be done efficiently in one pass by keeping the best and second-best values seen so far, and whenever a value you iterate over is better than the second-best, you'll either replace the second-best with it or the best, and in the latter case you also overwrite the best with the new value. That can look like this:
#include <utility>
template <typename It, typename EndIt, typename Less = std::less<>>
auto top_two(It it, EndIt end, Less less = Less{}) -> std::pair<It, It>
{
It first = it;
if (it == end || ++it == end)
return {first, end};
std::pair<It, It> results = less(*it, *first) ? std::pair{first, it} : std::pair{it, first};
while (++it != end)
if (less(*results.second, *it))
results.second = less(*results.first, *it)
? std::exchange(results.first, it) : it;
return results;
}
(See it running at http://coliru.stacked-crooked.com/a/a7fa0c9f1945b3fe)
I return iterators so the caller can know where in the input the top two elements are, should they care (e.g. to erase them from a container, or calculate their distance from begin(), or modify their values).
It you want the two lowest values, just pass std::greater<>{} as your "less" argument.
Some convenience functions to make it easier to call with containers or initializer_lists:
template <typename Container, typename Less = std::less<>>
auto top_two(const Container& c, Less less = Less{})
{
return top_two(begin(c), end(c), less);
}
template <typename T, typename Less = std::less<>>
auto top_two(const std::initializer_list<T>& il, Less less = Less{})
{
return top_two(begin(il), end(il), less);
}
If you want a general solution for the top-N elements, it's better to make N an argument and create a multiset of N top values (using a dereferencing comparison type), putting the initial N elements in, then whenever a new element is more than the **top_n.begin() value, do a top_n.insert(it); followed by top_n.erase(top_n.rbegin()); to drop the worst element: these operations are O(log N) so remain reasonably efficient even in pathological cases, such as input that is incrementing numbers.
So, I'm trying to tally the elements of an array. By this I mean, I have a large array, and each element will have multiples of itself throughout the array. I am trying to figure out how many times each element occurs, however I keep running into the issue of there being duplicate tallies. Since "x" could exist at 12 different places in the array, when I loop through it and keep a running sum, I get the tally for "x" 12 different times. Does anyone know of a simpler/better way to keep a tally of an array with no duplicates?
My code is:
where count is the number of elements
for(i=0;i<count;i++)
{
for(x=0; x<count;x++)
{
if(array[i]==array[x])
{
tallyz++;
}
}
tally[i]=tallyz-1;
tallyz=0;
}
}
std::map<X, unsigned> tally;
for(i = 0; i < count; ++i)
++tally[array[i]];
Note that this is best if the redundancy in the array is fairly high. If most items are unique you're probably better just sorting the array as others have mentioned.
If you can sort the array, simply sort it. Then all you have left is a linear scan of the elements, checking if the element behind this one is the same as the current element (don't forget bounds checking).
As an alternative to sorting, you could use a map:
template<class T, size_t N>
void printSums(T (array&)[N]) {
map<T, size_t> m;
for(T*p = array; p < array+N; ++p) {
++m[*p];
}
for(map<T,size_t>::iterator it = m.begin(); it != m.end(); ++it) {
cout << it->first << ": " << it->second << "\n";
}
}
Warning: this is untested code.
first use a map just as John said,then traverse the tally array:
std::map<X, unsigned> data;
for(i = 0; i < count; i++)
data[array[i]]++;
for(i = 0; i < count; i++)
tally[i]=data[tally[i]]-1;