how can I change the code that after erasing first number, next iterator doesn't skip that number and compares it with the next one?
//previous number
auto i = aa.begin();
//next number
auto j = ++aa.begin();
while (j != aa.end()) {
if (*i < *j) {
//if next number is bigger - it gets erased
j = aa.erase(j);
continue;
}
i = j;
++j;
}
}
std::vector::erase returns an iterator to the first element after the removed elements. Hence you only need to increment the iterator when nothing has been erased:
if (*std::next(j) < *j) {
j = aa.erase(j);
} else {
++j;
}
Instead of keeping track of i manually you can use std::next to get the next iterator. In that case you need to stop the loop when std::next(j) == end.
Iterate through it backwards, and remove an element if its previous element is <= it.
You can store the value of the previous number in a variable.
//previous number
auto i = aa.begin();
//next number
auto previousValue = *i
auto j = ++aa.begin();
while (j != aa.end()) {
if (previousValue < *j) {
//if next number is bigger - it gets erased
previousValue = *j;
j = aa.erase(j);
continue;
}
i = j;
previousValue = *j;
++j;
}
}
It is best to use standard library for this kind of operations. Smaller chance to make mistake.
Also designing functions API keeping common standard from STL library is a good practice too:
template <typename It>
auto remove_smaller_then_prev(It b, It e)
{
if (b == e)
return e;
auto prev = *b++;
return std::remove_if(b, e, [&prev](const auto& x) { return x < std::exchange(prev, x); });
}
template <typename V>
void erase_smaller_then_prev(V& v)
{
auto newEnd = remove_smaller_then_prev(std::begin(v), std::end(v));
v.erase(newEnd, std::end(v));
}
https://godbolt.org/z/s96xW4qf9
Extra question: how about testcase: 5, 3, 4 what should be the result, I assumed: 5, 4.
Side note: please remember that code exists in some context, so it is a good practice to show problematic code inside a complete function which provides such context (like in my answer).
Do not keep erasing from the vector. Instead, use the read-write-pointer pattern. It performs much better and is easier to reason about.
template <typename T>
void removeUnsorted(std::vector<T>& vec) {
if (vec.empty()) return;
auto last = begin(vec);
auto write = begin(vec);
for (auto read = begin(vec); read != end(vec); ++read) {
if (!(*last < *read)) {
*write++ = *read;
}
last = read; // !
}
vec.resize(distance(begin(vec),write));
}
Basically this tracks all value you want to keep between begin(vec) and write. The read iterator goes over every value once and copies it to *write if and only if you decide to keep it.
You can decide with which value to compare, the last one read or the last one written. The code as it stands now compares against the last one read, such that this:
{7,6,9,4,3,8,5,2}
gets transformed to this:
{7,6,4,3,5,2}
^----- note the 5
If you do not want this, but want to always have a descending value, move the line with a // ! comment inside the if condition, thereby only updating the "compare-to" value when it was kept, not when it was encountered.
In the very last line, you truncate the vector, dropping every value that was not kept. Note that this is done in constant time for integers.
Related
I have the following given vector:
vector<int> arr = {2,1,2,2,2,3,4,2};
The goal is to move a target number all the way to the back. Say target is 2, then final result should be something like:
arr = {1,3,4,2,2,2,2,2}
Attempt
My approach is to loop through the vector, then if I find a 2, I would use push_back to add to the end, and at the same time, erase the current 2.
In code, it looks like this:
vector<int> moveEndV1(vector<int> &arr, int toMove){
for (unsigned int i = 0; i < arr.size() ; i++) {
if (arr[i] == toMove) {
arr.push_back(arr[i]); // add to the end
arr.erase(arr.begin()+i); // erase current
}
}
return arr;
}
Problem Once we erase the an element, the loop counter is wrong because now it is dealing with a modified vector.
In other words, say we start with the original vector:
{2,1,2,2,2,3,4,2}
At i = 0, the value is 2 so we moved 2 to the back and erased the first 2.
So we have at i = 0:
{1,2,2,2,3,4,2,2}
This is fine, but then when we go to i = 1, the value at i = 1 is no longer 1 like the original array, but instead it is 2. This is because we erased an element when we were at i = 0. So, at i = 1, after push_back and erasing, we get:
{1,2,2,3,4,2,2,2}
So far so good, but now if we go to i = 2, we get:
{1,2,3,4,2,2,2,2}
i will keep increasing till the end, and in the end we have that extra 2 at the front.
{1,2,3,4,2,2,2,2}
Is there a way to resolve this? Other than writing a separate function to search for this 2 at the front and then move to back?
Any help is greatly appreciated.
You can do this easily by using std::stable_partition:
std::stable_partition(arr.begin(), arr.end(),
[toMove](int i) { return i != toMove; });
#cigien solution is elegant;
but modified your code a bit, will work too;
void moveEndV1(std::vector<int> &arr, int toMove){
auto it = arr.begin();
for ( int i = 0; i < arr.size(); i++ )
{
if (*it == toMove )
{
int val = *it;
it = arr.erase( it );
arr.push_back( val );
}
else
{
++it;
}
}
}
A stable partition works, but seems like an overkill (O(n log n) time, O(log n) space). Since you know your target number, you don't have to push it back immediately. Instead, use two iterators, src and dst, along the lines of
auto dst = arr.begin();
for (auto src = arr.begin(); src != arr.end(); src++) {
if (*src != toMove) {
*dst++ = *src;
}
}
// At this point all non-target numbers are at the beginning of the
// array, and the order is preserved. Fill the rest with the target.
while (dst != arr.end()) {
*dst++ = toMove;
}
I've been working on a lowest cost algorithm for airports where the user enters the name of an airport, and then I run this algorithm to spit out all the destinations you can go to, and the lowest cost, including connecting flights. I use a list iterator to iterate through the reachable destinations from the source location, but after just one iteration, the code breaks and a message comes up telling me that the iterator is not dereferenceable. here is my code
//Finds minimum cost
void findPaths(std::string source)
{
std::list<int> Reachable;
int min = INTMAX_MAX;
int lowestIndex = -1;
bool existsInList = true;
std::stack<std::string> connectingFlights;
//Make arrays
//Initialize costs to a high value so any value will be smaller
int costs[MAX]{INTMAX_MAX};
//Initialize paths to negative one so that we know there is no location
int path[MAX]{ -1 };
//Find where the source is
int srcIndex = findOrInsert(source);
//Put the costs into the array, leaving the high number for where there is no path
for (int i = 0; i < MAX; i++)
{
costs[i] = priceEdges[srcIndex][i];
}
//Put the source index in places that have a path
for (int i = 0; i < MAX; i++)
{
if (priceEdges[srcIndex][i] == 0)
{
path[i] = -1;
}
else
{
path[i] = srcIndex;
Reachable.push_back(i);
}
}
//If the list is empty, we are done;
while (!Reachable.empty())
{
//Find the index that has the lowest value in costs
for (std::list<int>::iterator it = Reachable.begin(); *it < Reachable.size(); it)
{
if (costs[*it] < min)
{
min = costs[*it];
int lowestIndex = *it;
}
//Remove the index with the lowest value in costs
Reachable.erase(it);
//Save the previous cost to compare after a change may occur
int prevCost = costs[lowestIndex];
//Assign the value to the lowest cost it can find
costs[lowestIndex] = FindMin(costs[lowestIndex], costs[srcIndex] + priceEdges[srcIndex][lowestIndex]);
//If the price has changed
if (prevCost != costs[lowestIndex])
{
path[lowestIndex] = srcIndex;
}
existsInList = std::find(Reachable.begin(), Reachable.end(), lowestIndex) != Reachable.end();
if (!existsInList)
{
Reachable.push_back(lowestIndex);
}
}
}
Your for loop is just plain wrong. You are dereferencing an iterator without validating that it is even valid, and you are comparing a destination value the iterator refers to against the size of the vector, which makes no sense since they are two completely different things.
You need to replace the loop with this instead:
for (std::list<int>::iterator it = Reachable.begin(); it != Reachable.end(); )
Or even this:
std::list<int>::iterator it = Reachable.begin();
while (it != Reachable.end())
Then, to satisfy the loop's stop condition, you need to change this line:
Reachable.erase(it);
To this instead:
it = Reachable.erase(it);
You are removing an item from the list, which invalidates the iterator, but you are never updating the iterator to point at the next item, so the code will have problems when it tries to dereference the iterator again. erase() returns an iterator to the next item in the list following the item that is being removed.
Also, on this line:
int lowestIndex = *it;
You are declaring a new temp variable that goes out of scope immediately afterwards, so it is never used. You have a previous lowestIndex variable declared at the start of the function that you are never assigning a value to after initialization, so it is always -1. You need to remove the int from the assignment:
lowestIndex = *it;
//Remove the index with the lowest value in costs
Reachable.erase(it);
This is invalidating the iterator but the for loop performs *it < Reachable.size(), which dereferences the invalid iterator. Instead, should probably do.
it = Reachable.erase(it);
Furthermore, the *it < Reachable.size() should probably be replaced with it != Reachable.end().
Lastly, the increment portion of your for loop should probably be empty since it's not doing anything. You could also use a while loop instead.
auto it = Reachable.begin();
while (it != Reachable.end())
{
// ...
it = Reachable.erase(it);
// ...
}
1) I have got an error while I tried to access next iterator of the inner loop by using next(). I was wondering where this error comes from. To be specific I have a vector of lists and I want to check connection feasibility with the next element, that's what feas[it2][(next(it2))] does.
2) After the if condition satisfies, I would like to insert an element, lets say 3, at the same address. If you could correct the insert function I will be so grateful.
To sum up, I want to know how can I have access to next element within the list?, and also how to insert a value at the same address?
Many thanks
vector<list<int>>temp;
for (const auto& it1 : temp)
{
for (const auto& it2 : it1)
{
if (feas[it2][(next(it2))] == 1)
{
temp[it1].insert (it2,3);
}
}
}
If checking with the previous element can work for you :
int temp = -1;
for (const auto& it2 : it1)
{
if (feas[temp][itr2] == 1)
{
temp[it1].insert (temp,3);
}
temp = it2;
}
than you might want to try something like that , as itr2 holds the values in the list
You will never be able to insert into a vector that you are iterating over.
EDIT: You can insert and delete elements from a 'vector' as you iterate over it if the vector does not resize itself.
The only option is to move over the vector with indexes.
Example:
vector<list<int>>temp;
for (int i = 0; i < temp.size; i++)
{
for (int j = 0; j < temp[i].size; j++)
{
if (feas[i][j + 1)] == 1)
{
temp[i].insert(temp.begin()+j, 3);
}
}
}
Is there any STL function which does this?
For vector:
4 4 5 5 6 7
The expected output should be 2,because of one 6 and 7
Would you be kind to help me count them classic if there is no STL function?
I don't think there is an algorithm for that in STL. You can copy into a multimap or use a map of frequencies as suggested, but it does extra work that's not necessary because your array happens to be sorted. Here is a simple algorithm that counts the number of singular elements i.e. elements that appear only once in a sorted sequence.
int previous = v.front();
int current_count = 0;
int total_singular = 0;
for(auto n : v) {
if(previous == n) // check if same as last iteration
current_count++; // count the elements equal to current value
else {
if(current_count == 1) // count only those that have one copy for total
total_singular++;
previous = n;
current_count = 1; // reset counter, because current changed
}
}
if(current_count == 1) // check the last number
total_singular++;
You could also use count_if with a stateful lambda, but I don't think it'll make the algorithm any simpler.
If performance and memory doesn't matter to you, use std::map (or unordered version) for this task:
size_t count(const std::vector<int>& vec){
std::map<int,unsigned int> occurenceMap;
for (auto i : vec){
occurenceMap[i]++;
}
size_t count = 0U;
for (const auto& pair : occurenceMap){
if (pair.second == 1U){
count++;
}
}
return count;
}
with templates, it can be generalize to any container type and any containee type.
Use std::unique to count the unique entries(ct_u) and then user vector count on the original one (ct_o). Difference ct_o-ct_u would give the answer.
P.S.: this will only work if the identical entries are together in the original vector. If not, you may want to sort the vector first.
Using algorithm:
std::size_t count_unique(const std::vector<int>& v)
{
std::size_t count = 0;
for (auto it = v.begin(); it != v.end(); )
{
auto it2 = std::find_if(it + 1, v.end(), [&](int e) { return e != *it; });
count += (it2 - it == 1);
it = it2;
}
return count;
}
Demo
Hey here is a trick question asked in class today, I was wondering if there is a way to find a unique number in a array, The usual method is to use two for loops and get the unique number which does not match with all the others I am using std::vectors for my array in C++ and was wondering if find could spot the unique number as I wouldn't know where the unique number is in the array.
Assuming that we know that the vector has at least three
elements (because otherwise, the question doesn't make sense),
just look for an element different from the first. If it
happens to be the second, of course, we have to check the third
to see whether it was the first or the second which is unique,
which means a little extra code, but roughly:
std::vector<int>::const_iterator
findUniqueEntry( std::vector<int>::const_iterator begin,
std::vector<int>::const_iterator end )
{
std::vector<int>::const_iterator result
= std::find_if(
next( begin ), end, []( int value) { return value != *begin );
if ( result == next( begin ) && *result == *next( result ) ) {
-- result;
}
return result;
}
(Not tested, but you get the idea.)
As others have said, sorting is one option. Then your unique value(s) will have a different value on either side.
Here's another option that solves it, using std::find, in O(n^2) time(one iteration of the vector, but each iteration iterates through the whole vector, minus one element.) - sorting not required.
vector<int> findUniques(vector<int> values)
{
vector<int> uniqueValues;
vector<int>::iterator begin = values.begin();
vector<int>::iterator end = values.end();
vector<int>::iterator current;
for(current = begin ; current != end ; current++)
{
int val = *current;
bool foundBefore = false;
bool foundAfter = false;
if (std::find(begin, current, val) != current)
{
foundBefore = true;
}
else if (std::find(current + 1, end, val) != end)
{
foundAfter = true;
}
if(!foundBefore && !foundAfter)
uniqueValues.push_back(val);
}
return uniqueValues;
}
Basically what is happening here, is that I am running ::find on the elements in the vector before my current element, and also running ::find on the elements after my current element. Since my current element already has the value stored in 'val'(ie, it's in the vector once already), if I find it before or after the current value, then it is not a unique value.
This should find all values in the vector that are not unique, regardless of how many unique values there are.
Here's some test code to run it and see:
void printUniques(vector<int> uniques)
{
vector<int>::iterator it;
for(it = uniques.begin() ; it < uniques.end() ; it++)
{
cout << "Unique value: " << *it << endl;
}
}
void WaitForKey()
{
system("pause");
}
int main()
{
vector<int> values;
for(int i = 0 ; i < 10 ; i++)
{
values.push_back(i);
}
/*for(int i = 2 ; i < 10 ; i++)
{
values.push_back(i);
}*/
printUniques(findUniques(values));
WaitForKey();
return -13;
}
As an added bonus:
Here's a version that uses a map, does not use std::find, and gets the job done in O(nlogn) time - n for the for loop, and log(n) for map::find(), which uses a red-black tree.
map<int,bool> mapValues(vector<int> values)
{
map<int, bool> uniques;
for(unsigned int i = 0 ; i < values.size() ; i++)
{
uniques[values[i]] = (uniques.find(values[i]) == uniques.end());
}
return uniques;
}
void printUniques(map<int, bool> uniques)
{
cout << endl;
map<int, bool>::iterator it;
for(it = uniques.begin() ; it != uniques.end() ; it++)
{
if(it->second)
cout << "Unique value: " << it->first << endl;
}
}
And an explanation. Iterate over all elements in the vector<int>. If the current member is not in the map, set its value to true. If it is in the map, set the value to false. Afterwards, all values that have the value true are unique, and all values with false have one or more duplicates.
If you have more than two values (one of which has to be unique), you can do it in O(n) in time and space by iterating a first time through the array and filling a map that has as a key the value, and value the number of occurences of the key.
Then you just have to iterate through the map in order to find a value of 1. That would be a unique number.
This example uses a map to count number occurences. Unique number will be seen only one time:
#include <iostream>
#include <map>
#include <vector>
int main ()
{
std::map<int,int> mymap;
std::map<int,int>::iterator mit;
std::vector<int> v;
std::vector<int> myunique;
v.push_back(10); v.push_back(10);
v.push_back(20); v.push_back(30);
v.push_back(40); v.push_back(30);
std::vector<int>::iterator vit;
// count occurence of all numbers
for(vit=v.begin();vit!=v.end();++vit)
{
int number = *vit;
mit = mymap.find(number);
if( mit == mymap.end() )
{
// there's no record in map for your number yet
mymap[number]=1; // we have seen it for the first time
} else {
mit->second++; // thiw one will not be unique
}
}
// find the unique ones
for(mit=mymap.begin();mit!=mymap.end();++mit)
{
if( mit->second == 1 ) // this was seen only one time
{
myunique.push_back(mit->first);
}
}
// print out unique numbers
for(vit=myunique.begin();vit!=myunique.end();++vit)
std::cout << *vit << std::endl;
return 0;
}
Unique numbers in this example are 20 and 40. There's no need for the list to be ordered for this algorithm.
Do you mean to find a number in a vector which appears only once? The nested loop if the easy solution. I don't think std::find or std::find_if is very useful here. Another option is to sort the vector so that you only need to find two consecutive numbers that are different. It seems overkill, but it is actually O(nlogn) instead of O(n^2) as the nested loop:
void findUnique(const std::vector<int>& v, std::vector<int> &unique)
{
if(v.size() <= 1)
{
unique = v;
return;
}
unique.clear();
vector<int> w = v;
std::sort(w.begin(), w.end());
if(w[0] != w[1]) unique.push_back(w[0]);
for(size_t i = 1; i < w.size(); ++i)
if(w[i-1] != w[i]) unique.push_back(w[i]);
// unique contains the numbers that are not repeated
}
Assuming you are given an array size>=3 which contains one instance of value A, and all other values are B, then you can do this with a single for loop.
int find_odd(int* array, int length) {
// In the first three elements, we are guaranteed to have 2 common ones.
int common=array[0];
if (array[1]!=common && array[2]!=common)
// The second and third elements are the common one, and the one we thought was not.
return common;
// Now search for the oddball.
for (int i=0; i<length; i++)
if (array[i]!=common) return array[i];
}
EDIT:
K what if more than 2 in an array of 5 are different? – super
Ah... that is a different problem. So you have an array of size n, which contains the common element c more than once, and all other elements exactly once. The goal is to find the set of non-common (i.e. unique) elements right?
Then you need to look at Sylvain's answer above. I think he was answering a different question, but it would work for this. At the end, you will have a hash map full of the counts of each value. Loop through the hash map, and every time you see a value of 1, you will know the key is a unique value in the input array.