Removing multiple elements from stl list while iterating - c++

This is not similar to Can you remove elements from a std::list while iterating through it?. Mine is a different scenario.
Lets say I have a list like this.
1 2 3 1 2 2 1 3
I want to iterate this stl list in such a way that
When I first encounter an element X I do some activity and then I need to remove all the elements X in that list and continue iterating. Whats an efficient way of doing this in c++.
I am worried that when i do a remove or an erase I will be invalidating the iterators. If it was only one element then I could potentially increment the iterator and then erase. But in my scenario I would need to delete/erase all the occurances.
Was thinking something like this
while (!list.empty()) {
int num = list.front();
// Do some activity and if successfull
list.remove(num);
}
Dont know if this is the best.

Save a set of seen numbers and if you encounter a number in the set ignore it. You can do as follows:
list<int> old_list = {1, 2, 3, 1, 2, 2, 1, 3};
list<int> new_list;
set<int> seen_elements;
for(int el : old_list) {
if (seen_elements.find(el) == seen_elements.end()) {
seen_elements.insert(el);
new_list.push_back(el);
}
}
return new_list;
This will process each value only once and the new_list will only contain the first copy of each element in the old_list. This runs in O(n*log(n)) because each iteration performs a set lookup (you can make this O(n) by using a hashset). This is significantly better than the O(n^2) that your approach runs in.

Related

Looking for the best place to insert into a vector

For example if I have a vector of ints
vector<int> my_vector;
my_vector[0] = 6;
my_vector[1] = 3;
So my vector is of size 2 right now.
Now let's say I want to add another integer in my vector. Let's just say this new integer is 10.
I want to be able to set it up 3 (my_vec.size() + 1) ways. In other words, check where placing my new value in my vector of ints would result in the value I'm interested in.
10, 6, 3
6, 10, 3
6, 3, 10
Out of those 3 options, I'll pick the one that best fits my needs. The one I pick, will be my new vector. So if I pick 6, 10, 3, that will be my vector afterwards.
That's the gist of what I want to be able to do.
I have a very inefficient brute force way of creating temp vectors and calculating it that way. I was wondering if there is a simple and optimal way to go about this. I essentially want to be able to compute a new value that I want to add into my vector in all possible areas and look for whatever value I'm interested in.
Just insert new element to the end (most efficient way for the vector) and then move it to the front step by step and test each combination:
vec.push_back( new_value );
test_vector( vec );
for( size_t i = vec.size() - 1; i > 0; --i ) {
std::swap( vec[i], vec[i-1] );
test_vector( vec );
}
live example on ideone for 6,3 + 10. New element will always end at the first position. You need to find best position and then move your element there, that should be pretty obvious.
You really only have 2 cases here. First case: the newly added number is less than the first number in the vector. In this case the number should always be added to the beginning of the vector. If the new number is larger, it should always go in the second spot in the vector (since moving it any farther down the vector will not affect the new total).
I guess I understand your question.
Your computing method is complex. It is a example just now.
Only can you pick a kind of arrangement?
You can implement a function like this:
int GetArrangemnetValue(vector<int>& sourceVec, int nNewPos, int nNewValue);
This function to simulate a whole vetor.
You can do some compute is this function.
Then you can do some select via the return value of funtion.
So you can use this function and not a temporary vector.

Iterating through and removing items from a list

b = [1,2,3,4,5,6,7]
for n in b:
if n > 3:
b.remove(n)
I print b, and get the following list:
[1,2,3,5,7]
Why are 5 and 7 still present? I can make a function that pretty much does the same thing and removes all numbers from the list about 3, so why can't I do the same in Terminal?
This is a well-known issue. Iterators are not reliable when you modify the underlying collection while using the iterator.
As for the behavior you experience:
With CPython, the list iterator is represented by an index into the array. If you remove an item from the list at the iterator position or before it while still iterating over it, the iterator jumps forward. The iterator position index is still the same, but all items "under" the iterator have just moved to the left by one position. This makes the iterator skip one element. Hence you only remove every second item.
l = [1, 2, 3, 4]
^it(pos=1)
l.remove(2)
l = [1, 3, 4]
^it(pos=1)
it.next() # automatically at the end of each for loop
l = [1, 3, 4] # we just skipped over an item
^it(pos=2)
Here's a nice little treatise on the topic from #mgiuca.
Interestingly enough, removing items after the iterator position is safe with the current implementation.
In short: don't modify collections while iterating over them. Alternatives for lists: Remove items from a list while iterating in Python
Thats because you are iterating over the same list. Try this:
b = [1,2,3,4,5,6,7]
c = b[:]
for n in c:
if n > 3:
b.remove(n)
If you see the below image, now I creating two different list.
When you remove an element, the array is modified (elements shifted to the left), so the next iteration takes you to the next element bypassing the shifted element. That is to say, the array is modified and the loop advances to the next index. That is why you notice a jump every time an element is removed.

Efficiently mark nodes in a graph that must no longer be considered

I have a graph and iterate over every node multiple times until i mark it as finished eventually and ignore it in future iterations. This process is repeated until all nodes are marked.
So far, i have a std::vector that stores the status for all nodes: finished[v] = 1 when the node is finished and 0 otherwise. The code looks like this:
for every node v {
if finished[v] == 0 {
[...]
}
}
The problem is that near the end of the computation, only a few nodes are not marked but i still check every single one for finshed[v] == 0
Would it be better to save all node id's in a vector and then remove them until the vector is empty (I heard removing elements in a vector is not really efficient)?
Since I already store the number of finished nodes as a smple integer, I could just move all marked nodes at the end of the vector and cut it (at the position totalNumberOfNodes - numberOfFinishedNodes) in case moving elements is more efficient than deleting. Or is a vector just inferior to other data structures in this scenario?
Using std::list<T>:
#include <list>
std::list<int> unvisited_nodes;
// fill in unvisited_nodes with all nodes' ids
loop of you algorithm
{
// iterate only over unvisited nodes
for (auto it = unvisited_nodes.begin(); it != unvisited_nodes.end(); )
{
visit(*it);
if (shouldNotBeVisitedAgain(*it))
{
unvisited_nodes.erase(it++);
}
else
{
++it;
}
}
}
Using your std::vector<T>:
#include <vector>
std::vector<int> unvisited_nodes;
// fill in unvisited_nodes with all nodes' ids
loop of you algorithm
{
// iterate only over unvisited nodes
for (int i = 0; i < unvisited_nodes.size(); )
{
visit(unvisited_nodes[i]);
if (shouldNotBeVisitedAgain(unvisited_nodes[i]))
{
std::swap(unvisited_nodes[i], unvisited_nodes.back());
unvisited_nodes.pop_back();
}
else
{
++i;
}
}
}
Regarding elements' removal from std::vector<T>: this operations has O(N) complexity only in case you want to preserve the original order of elements. This operation can be optimized if the order of elements after removal does not need to be the same:
std::vector<int> v = { 1, 2, 3, 4, 5, 6, 7 } ;
// now, let's remove element under index 3, v[3] == 4:
std::swap(v[3], v.back());
v.pop_back();
// now v == { 1, 2, 3, >7<, 5, 6 }
If you need them to stay in a particular order: a linked list may be the only efficient solution (you can consider other data structures like "ropes" if you want, but I suspect you won't want to implement them).
If you only need them to stay in sorted order: std::multiset should also work; just remove the elements that you've visited.
If you don't care about order at all: just keep a vector of the indices of all the nodes to be processed, but instead of actually erasing an element from the middle, swap it with the last element and then pop the back of the vector.

Assertion Error, using STL Vector

for(myIterator = numbers.begin();myIterator != numbers.end() ;myIterator++)
{
resultVect.push_back(*myIterator+2);
numbers.erase(myIterator+2);
}
numbers consist of a series of numbers (eg 1,2,3,4,5,6,7)
Then I would like to erase every 3rd number.
Something like,
1 2 3 4 5 6 ( First round -> 3 is out)
1 2 4 5 6 ( Second round -> 6 is out)
1 2 4 5 ( Third round -> 4 is out)
and so on.
I will store the number that goes out in another vector (resultVect).
Im getting Assertion error. Pls advise tq
When you use erase for a vector, it will relocate the elements after the erase position so the iterators after that will be invalidated.
Second when you say iterator + 2 and that could go beyond the range of the vector too.
Removing an element from the vector invalidates all iterators to that element and beyond (in the current standard, there is an open issue to change this).
The first question is how efficient you want the process to be, if you don't care (much) about performance you can do a simple loop:
for (int i = 3; i < input.size(); i+=3) {
result.push_back(input[i]);
}
for (int i = (input.size()+2)/3 - 1; i >= 0; --i) {
input.erase(input.begin()+i*3);
}
If performance is critical, you can take a look at the std::remove algorithm and use the same approach to avoid doing multiple copies of the elements while you run the algorithm. Basically you need a read and a write head into the original container and you only copy from the read to the write location if the condition is not met.
Simply put: you cannot modify a vector while iterating it. The iterator will become invalid and that will get you an assertion.
To properly do what you want, you might consider creating a copy of the vector with values to keep, and a vector with values to remove. Then replace the number vector by the one with the values to keep.

std::list - Sort one item

Is it possible to sort a list based off one item?
For instance, if I have
1,3,2,4,5,6,7 ... 1000000
And I know that 3 is the second element, is it possible to efficiently sort 3 into it's correct position between 2 and 4 without re-sorting the entire list?
EDIT: I should also note that, in this scenario, it is assumed that the rest of the list is already sorted; it is simply the 3 that is now out of place.
You could simply find that unordered object (O(n)), take the object out (O(1)), find the correct position (O(n)), then insert it again (O(1)).
Assuming C++11,
#include <list>
#include <algorithm>
#include <iostream>
int main() {
std::list<int> values {1, 2, 3, 9, 4, 5, 6, 7, 12, 14};
auto it = std::is_sorted_until(values.begin(), values.end());
auto val = *--it;
// ^ find that object.
auto next = values.erase(it);
// ^ remove it.
auto ins = std::lower_bound(next, values.end(), val);
// ^ find where to put it back.
values.insert(ins, val);
// ^ insert it.
for (auto value : values) {
std::cout << value << std::endl;
}
}
Before C++11 you need to implement std::is_sorted_until yourself.
For this very limited case, writing your own bubblesort would probably be faster than std::sort.
If you have that level of knowledge, why don't you just swap the items yourself rather than trying to coerce sort to do it for you?
Surely that's a better way.
Even if you don't know where it has to go, you can find that out quickly, remove it, then insert it at the correct location.
I suppose you could use the insert method to move the element, but I'd like to know more about the way you calculate its "correct" position: there could be a better suited algorithm.
If you think about the traversal possible for a list, it's clearly only end-to-end. So:
if you don't know where the mis-ordered element is you have to first scan through the elements one by one until you find it, then
you can remember the value and delete the out-of-order element from the list, then
there are two possibilities:
that element is greater in your sorting order than any other element you've yet encountered, in which case you need to keep going through the remaining elements until you find the correct place to insert it.
the element would belong somewhere amongst the elements you've already passed over, in which case:
you can move backwards, or forwards from the first element again, until you find the correct place to put it.
if you've created some records from your earlier traversal you can instead use it to find the insertion place faster, for example: if you've created a vector of list iterators, you can do a binary search in the vector. Vectors of every Nth element, hash tables etc. are all other possibilities.
This is If you dont use std::list.
With a Selection sort algorthm, you simply sort items 0 to 3 ( selectionSort(list,3)) if you know that thats the range.
Not the entire range till the end.
Sample code :
#include <iostream>
using namespace std;
void selectionSort(int *array,int length)//selection sort function
{
int i,j,min,minat;
for(i=0;i<(length-1);i++)
{
minat=i;
min=array[i];
for(j=i+1;j<(length);j++) //select the min of the rest of array
{
if(min>array[j]) //ascending order for descending reverse
{
minat=j; //the position of the min element
min=array[j];
}
}
int temp=array[i] ;
array[i]=array[minat]; //swap
array[minat]=temp;
}
}
void printElements(int *array,int length) //print array elements
{
int i=0;
for(i=0;i<length;i++) cout<<array[i]<<" ";
cout<<" \n ";
}
int main(void)
{
int list[]={1,3,2,4,5,6}; // array to sort
int number_of_elements=6;
cout<<" \nBefore selectionSort\n";
printElements(list,number_of_elements);
selectionSort(list,3);
cout<<" \nAfter selectionSort\n";
printElements(list,number_of_elements);
cout<<" \nPress any key to continue\n";
cin.ignore();
cin.get();
return 0;
}
Output:
Before selectionSort
1 3 2 4 5 6
After selectionSort
1 2 3 4 5 6
Press any key to continue