I wrote this method to find the minor of a sparse matrix:
SpMatrixVec SparseMatrix::minor(SpMatrixVec matrix, int col) const{
SpMatrixVec::iterator it = matrix.begin();
int currRow = it->getRow();
int currCol = col;
while(it != matrix.end()) {
if(it->getRow() == currRow || it->getCol() == currCol){
matrix.erase(it);
}
// if we have deleted an element in the array, it doesn't advance the
// iterator and size() will be decreased by one.
else{
it++;
}
}
// now, we alter the cells of the minor matrix to be of proper coordinates.
// this is necessary for sign computation (+/-) in the determinant recursive
// formula of detHelper(), since the minor matrix non-zero elements are now
// in different coordinates. The row is always decreased by one, since we
// work witht he first line, and the col is decreased by one if the element
// was located after 'col' (which this function receives as a parameter).
//change the cells of the minor to their proper coordinates.
for(it = matrix.begin(); it != matrix.end(); it++){
it->setRow(it->getRow()-1);
int newY;
newY = (it->getCol() > col) ? it->getCol() + 1 : it->getCol();
it->setCol(newY);
}
return matrix;
}
Now, i'm probably doing something wrong, because when reaching the second interation of the while loop, the program crashes.
The basic idea was to go over the vector, and see if it is the relevant coordinate, and if so - to delete it. I increment the iterator only if there was no deletion (and in this case, the vector should update the iterator to be pointing the next element..unless i got these things wrong).
Where is the problem?
Thanks a lot.
erase() invalidates your iterator.
You must update it using the return value of erase() for the loop to work:
while(it != matrix.end()) {
if(it->getRow() == currRow || it->getCol() == currCol){
//matrix.erase(it);
it = matrix.erase(it); // Here is the change
}
// if we have deleted an element in the array, it doesn't advance the
// iterator and size() will be decreased by one.
else{
//it++;
++it; // ++i is usually faster than i++. It's a good habit to use it.
}
}
erase invalidates your iterator. Do it = matrix.erase(it) instead.
You can't change a collection while you're iterating between its elements.
Use another temp collection to store the partial results.
edit: Even better, use a functor to delete elements: remove_if
You write the condition.
Related
So here's my problem.. I have a 2d array of 2 char strings.
9D 5C 6S 9D KS 4S 9D
9S
If 3 found I need to delete the first 3 based on the first char.
card
My problem is I segfault almost anything i do...
pool is the 2d vector
selection = "9S";
while(col != GameBoard::pool.size() ){
while(GameBoard::pool[col][0].at(0) == selection.at(0) || cardsRem!=0){
if(GameBoard::pool[col].size() == 1){
GameBoard::pool.erase(GameBoard::pool.begin() + col);
cardsRem--;
}
else{
GameBoard::pool[col].pop_back();
cardsRem--;
}
}
if(GameBoard::pool[col][0].at(0) != selection.at(0)){
col++;
}
}
I've tried a series of for loops etc, and no luck! Any thoughts would save my sanity!
So I've tried to pull out a code segment to replicate it. But I can't...
If I run my whole program in a loop it will eventually throw a segfault. If I run that exact code in the same circumstance it doesn't... I'm trying to figure out what I'm missing. I'll get back in if I figure out exactly where my issue is..
So in the end the issue is not my code itself, i've got memory leaks or something somewhere that are adding up to eventually crash my program... That tends to be in the same method each time I guess.
The safer and most efficient way to erase some elements from a container is to apply the erase-remove idiom.
For instance, your snippet can be rewritten as the following (which is testable here):
using card_t = std::string;
std::vector<std::vector<card_t>> decks = {
{"9D", "5C", "6S", "9D", "KS", "4S", "9D"},
{"9S"}
};
card_t selection{"9S"};
// Predicate specifing which cards should be removed
auto has_same_rank = [rank = selection.at(0)] (card_t const& card) {
return card.at(0) == rank;
};
auto & deck = decks.at(0);
// 'std::remove_if' removes all the elements satisfying the predicate from the range
// by moving the elements that are not to be removed at the beginning of the range
// and returns a past-the-end iterator for the new end of the range.
// 'std::vector::erase' removes from the vector the elements from the iterator
// returned by 'std::remove_if' up to the end iterator. Note that it invalidates
// iterators and references at or after the point of the erase, including the
// end() iterator (it's the most common cause of errors in code like OP's).
deck.erase(std::remove_if(deck.begin(), deck.end(), has_same_rank),
deck.end());
So for anyone else in the future who comes across this...
The problem is I was deleting an element in the array in a loop, with the conditional stop was it's size. The size is set before hand, and while it was accounted for in the code it still left open the possibility for while(array.size() ) which would be locked in at 8 in the loop be treated as 6 in the code.
The solution was to save the location in the vector to delete and then delete them outside of the loop. I imagine there is a better, more technical answer to this, but it works as intended now!
for (double col = 0; col < size; ++col)
{
if(GameBoard::pool[col][0].at(0) == selection.at(0)){
while(GameBoard::pool[col][0].at(0) == selection.at(0) && cardsRem !=0){
if( GameBoard::pool[col].size() > 1 ){
GameBoard::pool[col].pop_back();
cardsRem--;
}
if(GameBoard::pool[col].size() <2){
toDel.insert ( toDel.begin() , col );
//GameBoard::pool.erase(GameBoard::pool.begin() + col);
cardsRem--;
size--;
}
}
}
}
for(int i = 0; i< toDel.size(); i++){
GameBoard::pool.erase(GameBoard::pool.begin() + toDel[i]);
}
I got a method that adds adjacent Voxels to a vector. This method uses an vector with all the remaining points (means, they still need to be looked at as they are possible adjacents) and looks on every one of them if it is near enough to be added. If an element is a neighbor it also checks all adjacents of this element to add them too. This happens in a recursive manner.
void remove(std::vector<pcl::PointXYZ> &vec, pcl::PointXYZ p) {
for (int i = 0; i < vec.size(); i++) {
if (vec[i].x == p.x && vec[i].y == p.y && vec[i].z == p.z) {
vec.erase(vec.begin() + i);
break; // as all points should be unique
}
}
}
void addAdjacents(pcl::PointXYZ start, std::vector<pcl::PointXYZ> &newCluster, std::vector<pcl::PointXYZ> &remainingPoints) {
for (pcl::PointXYZ p : remainingPoints) {
if (distance(p, start) < 0.015) {
newCluster.push_back(p);
remove(remainingPoints, p);
if (remainingPoints.size() > 0)
addAdjacents(p, newCluster, remainingPoints);
}
}
}
The problem is, that many points from the remainingPoints-vector are added to the newCluster multiple times. I thought this wouldn't happen but it seems like internally it makes copies of the vector in the recursion? If a point is removed in a deeper layer the for-loop in the outer execution is somehow still iterating over this (removed) element.
I am fairly new to c++ so I am not sure how to prevent this. Can anyone help me? Thanks!
For sure I can just write a method addToCluster which just checks if the vector has this element before adding it but I thought that maybe there is a more elegant way to prevent this happening in the first place.
Edit:
As I understand I am breaking my iterator in the loop. So I would need to somehow update my iterator after calling addAdjacents. Is this right? Can I do something like that?
You need to separate identifying the points you wish to migrate with erasing them from the input.
template<typename BidirIt>
BidirIt addAdjacentsImpl(pcl::PointXYZ start, std::vector<pcl::PointXYZ> &newCluster, BidirIt first, BidirIt last) {
auto part = std::stable_partition(first, last, [&](auto p){ return distance(p, start) >= 0.015; });
for (auto it = part; it != last; ++it) {
newCluster.push_back(*it);
part = addAdjacentsImpl(*it, newCluster, first, part);
}
return part;
}
This only re-orders the elements, such that those we wish to remove are after those we wish to keep. I've written it as a template because I don't care to name the particular iterator types.
void addAdjacents(pcl::PointXYZ start, std::vector<pcl::PointXYZ> &newCluster, std::vector<pcl::PointXYZ> &remainingPoints) {
auto last = addAdjacentsImpl(start, newCluster, remainingPoints.begin(), remainingPoints.end());
remainingPoints.erase(last, remainingPoints.end());
}
Here
list.erase(list.begin() + i);
You erase an element from the std::vector called list (really not the best name for a vector) which invalidates iterators at and after the erased position. It isnt that obvious, but this erasing happens while addAdjacents iteratates over the same container. Leaving only the broken part in we have
void addAdjacents(pcl::PointXYZ start, std::vector<pcl::PointXYZ> &newCluster, std::vector<pcl::PointXYZ> &remainingPoints) {
for (pcl::PointXYZ p : remainingPoints) {
newCluster.push_back(p);
//remove(remainingPoints, p); // <- calls erase
remainingPoints.erase( remainingPoints.begin() + some_index);
}
}
}
Erasing an element from remainingPoints does break the range-based loop, because under the hood it uses iterators that potentially got invalidated in remove.
Given a vector of integers, I want to wrote a fast (not obvious O(n^2)) algorithm to remove all odd elements from it.
My idea is: iterate through vector till first odd element, then copy everything before it to the end of vector (call push_back method) and so on until we have looked through all original elements (except copied ones), then remove all of them, so that only the vector's tail survive.
I wrote the following code to implement it:
void RemoveOdd(std::vector<int> *data) {
size_t i = 0, j, start, end;
uint l = (*data).size();
start = 0;
for (i = 0; i < l; ++i)
{
if ((*data)[i] % 2 != 0)
{
end = i;
for (j = start, j < end, ++j)
{
(*data).push_back((*data)[j]);
}
start = i + 1;
}
}
(*data).erase((*data).begin(), i);
}
but it gives me lots of errors, which I can't fix. I'm very new to the programming, so expect that all of them are elementary and stupid.
Please help me with error corrections or another algorithm implementation. Any suggestions and explanations will be very appreciative. It is also better not to use algorithm library.
You can use the remove-erase idiom.
data.erase(std::remove_if(data.begin(), data.end(),
[](int item) { return item % 2 != 0; }), data.end());
You don't really need to push_back anything (or erase elements at the front, which requires repositioning all that follows) to remove elements according to a predicate... Try to understand the "classic" inplace removal algorithm (which ultimately is how std::remove_if is generally implemented):
void RemoveOdd(std::vector<int> & data) {
int rp = 0, wp = 0, sz = data.size();
for(; rp<sz; ++rp) {
if(data[rp] % 2 == 0) {
// if the element is a keeper, write it in the "write pointer" position
data[wp] = data[rp];
// increment so that next good element won't overwrite this
wp++;
}
}
// shrink to include only the good elements
data.resize(wp);
}
rp is the "read" pointer - it's the index to the current element; wp is the "write" pointer - it always points to the location where we'll write the next "good" element, which is also the "current length" of the "new" vector. Every time we have a good element we copy it in the write position and increment the write pointer. Given that wp <= rp always (as rp is incremented once at each iteration, and wp at most once per iteration), you are always overwriting either an element with itself (so no harm is done), or an element that has already been examined and either has been moved to its correct final position, or had to be discarded anyway.
This version is done with specific types (vector<int>), a specific predicate, with indexes and with "regular" (non-move) assignment, but can be easily generalized to any container with forward iterators (as its done in std::remove_if) and erase.
Even if the generic standard library algorithm works well in most cases, this is still an important algorithm to keep in mind, there are often cases where the generic library version isn't sufficient and knowing the underlying idea is useful to implement your own version.
Given pure algorithm implementation, you don't need to push back elements. In worst case scenario, you will do more than n^2 copy. (All odd data)
Keep two pointers: one for iterating (i), and one for placing. Iterate on all vector (i++), and if *data[I] is even, write it to *data[placed] and increment placed. At the end, reduce length to placed, all elements after are unecessary
remove_if does this for you ;)
void DeleteOdd(std::vector<int> & m_vec) {
int i= 0;
for(i= 0; i< m_vec.size(); ++i) {
if(m_vec[i] & 0x01)
{
m_vec.erase(m_vec.begin()+i);
i--;
}
}
m_vec.resize(i);
}
I was going through some legacy code, and found out something that could be improved.
The vector has pointers to a class and all elements are unique in the vector, as per the design.
A function ReplaceVal replaces an element having old_value to a new_value in the vector, in the following fashion:
iterator i, i_e;
i = vector->begin();
i_e = vector->end ();
for (; i != i_e; ++i)
{
if ((*i) == old_child)
break;
}
// Insertion
vector->insert_call(new_child, i);
// Since, the pointers are invalidated, do another find for erase
i = vector->begin();
i_e = vector->end ();
for (; i != i_e; ++i)
{
if ((*i) == old_child)
break;
}
// Finally, erase the old_value
vector->erase_call(i);
So, essentially, this involves shifting of elements twice, each for insertion and erase, if you are inserting and erasing elements in the middle of the vector.
For n insertions and remove calls, the complexity is O(n*m), if m elements are shifted every time, on an average.
I think, this can be improved, if I use std::replace, as mentioned here # MSDN documentation and std_replace_example.
The complexity of the std::replace would be O(n) comparisons for the old_value and new_value & 1 assignment operation. It'd be as simple as:
replace (vector.begin( ), vector.end( ), old_value , new_value);
Please correct me, if I am wrong and share feedback on anything that I missed.
P.S. The insert and erase are custom calls, which also update pointers to left_sibling and right_sibling for a given element.
You don't even need to do that:
iterator position = std::find( vector->begin() vector->end(), old_child );
if ( position == vector->end() ) {
throw NoSuchElement();
}
*position = new_child;
should do the trickāno erase and no insert.
vector erase returns an iterator pointing to location of the element that followed the erased
iter = myvector->erase(i);
then you can use that iterator to insert.
myvector->inster(iter, new_value);
or the other way around. vector insert returns an iterator pointing to the inserted element
iter = myvector->inster(i, new_value);
myvector->erase(iter);
Why not use one of these?
std::set
std::multiset
std::unordered_set
std::unordered_multiset
Why you have such complexity? Is there any purpose. May be the vector is used somewhere else also, but you may also use sets along with vectors for searching.
I have a list of Star structs. These structs are in a std::list
I am double looping this list and compairing there locations to detect a collision. When A collision is found I will delete Star with the lowest mass. But how can I delete the Star when I am in the double Loop, and keep the loop going to check for more collisions?
It's worth mentioning that the second loop is a reverse loop.
Here is some code
void UniverseManager::CheckCollisions()
{
std::list<Star>::iterator iStar1;
std::list<Star>::reverse_iterator iStar2;
bool totalbreak = false;
for (iStar1 = mStars.begin(); iStar1 != mStars.end(); iStar1++)
{
for (iStar2 = mStars.rbegin(); iStar2 != mStars.rend(); iStar2++)
{
if (*iStar1 == *iStar2)
break;
Star &star1 = *iStar1;
Star &star2 = *iStar2;
if (CalculateDistance(star1.mLocation, star2.mLocation) < 10)
{
// collision
// get heaviest star
if (star1.mMass > star2.mMass)
{
star1.mMass += star2.mMass;
// I need to delete the star2 and keep looping;
}
else
{
star2.mMass += star1.mMass;
// I need to delete the star1 and keep looping;
}
}
}
}
}
You need to utilize the return value of the erase method like so.
iStar1 = mStars.erase(iStar1);
erase = true;
if (iStar1 == mStars.end())
break; //or handle the end condition
//continue to bottom of loop
if (!erase)
iStar1++; //you will need to move the incrementation of the iterator out of the loop declaration, because you need to make it not increment when an element is erased.
if you don't increment the iterator if an item is erased and check if you deleted the last element then you should be fine.
Since modifying the list invalidates the iterators (so that you cannot increment them), you have to keep safe the iterators before the list is changed.
In the most of the implementation std::list is a dual-linked list, hence a iteration like
for(auto i=list.begin(), ii; i!=list.end(); i=ii)
{
ii = i; ++ii; //ii now is next-of-i
// do stuff with i
// call list.erasee(i).
// i is now invalid, but ii is already the "next of i"
}
The safest way, is to create a list containing all the "collided", then iterate on the "collided" calling list.remove(*iterator_on_collided)
(but inefficient, since has O2 complexity)
You want to use the result of erase() to get the next iterator and advance the loop differently:
If you erase using the outer iterator you clearly can abondon checking this Star against others and break out of the inner loop. Only if the inner loop was complete you'd want to advance the outer iterator because otherwise it would be advanced by the erase().
If you erase using the inner loop you already advanced the iteration, otherwise, i.e. if no star was erased, you need to advance.
Sample code would look somethimg like this:
for (auto oit(s.begin()), end(s.end()); oit != end; )
{
auto iit(s.begin());
while (iit != end)
{
if (need_to_delete_outer)
{
oit = s.erase(oit);
break;
}
else if (need_to_delete_inner)
{
iit = s.erase(iit);
}
else
{
++iit;
}
}
if (iit == end)
{
++oit;
}
}