How to iterate a list and erase from it? - c++

I'm having a lot of trouble with List iterators, and I asked a question previously but was not able to get the solution I was looking for.
I have a circular list, and I must replace the value of node n with node n + (step). I must then erase node n + (step). When I erase it puts the iterator to the element after the erased element. I need the iterator back at node n. How the heck can I do this because everytime I erase n + (step) I get an invalid iterator. My input is 5 and 2.
Please let me know if there is a better datastructure to do this with, if there is no way to iterate and erase from a list. I thought of using a Vector, but I would have to shift elements down and that would be costly if there are a lot of elements.
#include "roulette.h"
#include <iostream>
uint roulette(uint people, uint step)
{
std::list<uint>::iterator iterator;
for(uint i = people; i > 0; i--)
gl_myList.push_front(i);
iterator = gl_myList.begin();
while(people > 1)
{
iterator = advanceList(iterator, step - 1);
uint replaceValue = *iterator; // Node n's value
auto tempIterator = advanceList(iterator, step);
uint newValue = *tempIterator; //Node n + step value
iterator = gl_myList.erase(tempIterator);
//Makes it past the erase function ONCE.
//Puts the iterator back to the correct spot, and sets it value
while(*iterator != replaceValue)
{
advanceList(iterator, 1);
}
*iterator = newValue;
people--;
}
return *iterator;
}
advanceList
#include "roulette.h"
std::list<uint>::iterator advanceList(std::list<uint>::iterator& start, uint step)
{
for(uint i = 0; i < step; i++)
{
start++;
if(start == gl_myList.end())
{
start = gl_myList.begin();
}
}
return start;
}

You're not using the result of your erase() call correctly, nor are you checking for .end() prior to the next iteration. I'm all-but-certain the following is what you're at least attempting to do. And note, this is still brittle, as it is anything-but-ready for edge cases (like an initial empty list, a 0-step-value, etc.):
std::list<uint>::iterator advanceList(std::list<uint>::iterator& start, uint step)
{
for(uint i = 0; i < step; i++)
{
if(++start == gl_myList.end())
start = gl_myList.begin();
}
return start;
}
uint roulette(uint people, uint step)
{
std::list<uint>::iterator it;
for(uint i = people; i > 0; i--)
gl_myList.push_front(i);
it = gl_myList.begin();
while (gl_myList.size() > 1)
{
it = gl_myList.erase(advanceList(it, step - 1));
if (it == gl_myList.end())
it = gl_myList.begin();
}
return *it;
}

Let's fix a very simple bug in your code. advanceList modifies it's argument, when you call
auto tempIterator = advanceList(iterator, step);
both iterator and tempIterator are changed. Is it what you want to achieve?
Also in your advanceList if start was at the end as you entered teh function you must replace it with begin before entering loop.

You're not approaching this problem in the right way, I believe.
The best way to do what you want, is first to separate whathever has to be deleted from what has to be kept. You can do that with std::partition or std::stable_partition in header algorithm. Then you can delete a range of elements from your container easy and clean.
Example:
#include <vector>
#include <algorithm>
using namespace std;
// ...
bool differentFrom3(int n) { return n != 3; }
vector<int> v = { 1, 3, 4, 2, 1, 3, 4, 3, 7, 3, 1 };
// move all the 3's to one end of the vector
vector<int>::iterator it = stable_partition(v.begin(), v.end(), differentFrom3);
// v is now arranged in the following order:
// { 1, 4, 2, 1, 4, 7, 1, 3, 3, 3, 3 }
// ^
// +--- it
//
// and it points to the first element whose value is 3 (in this case, v[7])
// Now you can delete everything from the it to the end of the vector.
v.erase(it, v.end());
I'm using stable_partition here because it keeps the relative position between elements. If you don't care about that you can use partition instead.

Related

C++ How to find position of last occurrence of element in vector

Let's say I have a vector with the numbers: 10 10 58 31 63 40 76. I want to find the position of the last occurrence of the minimum element. The minimum element is 10. The position of the last occurrence of 10 is 1.
I've tried using reverse iterators, but I'm still a little bit confused.
Assuming the vector:
std::vector<int> v = { 10, 10, 58, 31, 63, 40, 76 };
You can get the last minimum element using reverse iterators and std::min_element:
auto last_min = std::min_element(std::rbegin(v), std::rend(v));
Then get the distance from the beginning of the vector using std::distance and the "base" of the reverse iterator:
auto last_min_distance = std::distance(std::begin(v), last_min.base());
Finally subtract one to get the index:
auto last_min_index = last_min_distance - 1;
Your idea with the reverse iterator was the right thing to do.
First you need to find the last element.
This can be done with std::min_element and reverse iterators:
auto min_elem_iter = std::min_element(std::crbegin(vec), std::crend(vec));
where vec is the std::vector you want to search through.
Now you have an iterator to the last element you searched. You need to check if it is the same as std::crend(vec) to make sure it points to a valid element.
If you want to know the index, you need std::distance, which can be used to calculate the distance between two iterators. With that you can find out the distance between std::crbegin(vec) and the iterator which points to the element we found.
This can than be used together with the size of the vector to calculate the index.
So all in all you can get what you want with:
template<class T>
auto getIndexOfLastMin(const std::vector<T>& vec)
-> std::optional<std::size_t>
{
auto last_elem_iter = std::min_element(std::crbegin(vec),
std::crend(vec));
if(last_elem_iter == std::crend(vec)){
return std::nullopt;
}
auto idx = std::distance(std::rbegin(vec), last_elem_iter);
return static_cast<std::size_t>(vec.size() -1 - idx);
}
You can checkout and run the code here
let's say minimum element is k
for (int i = 0; i < vector.size(); i++)
{
if( k == vector[i])
{
index = i;
}
}
At the end of the loop, index would be the last position of the minimum element in vector.
... And if you haven't found the minimum element up front, you can do:
int min_element = std::numeric_limits <int>::max;
size_t index = 0;
for (size_t i = 0; i < vector.size(); i++)
{
if (vector[i] <= min_element)
{
min_element = vector[i];
index = i;
}
}
Not everything has to be sophisticated (& this is the main pitfall of Modern C++: Any programmer tries to be a genius). Keep it simple:
#include <vector>
#include <limits.h>
int main()
{
std::vector<int> v = { 10, 10, 58, 31, 63, 40, 76 }; // This is the vector
int min = INT_MAX;
size_t i = 0, last_min_i = 0;
for (auto item : v) {
if (item <= min) {
min = item;
last_min_i = i;
}
i++;
}
// last_min_i holds the result
}
I'm assuming that you've already found the minimum element. Then a simple backwards loop seems easiest
size_t index = vec.size();
do
{
--index;
}
while (vec.at(index) != min_element);
If by chance you've made a mistake then using at will result in an exception, that's a bit safer than the UB you'd get if you used [].
You could do something with reverse iterators but why complicate?
To find out the last occurrence of an element in a vector, we could use find_end().
It is used to find out the subsequence's last occurrence.
vector<int> v1 = {1,3,5,7,9};
vector<int> v2 = {7};
auto it = find_end(v1.begin(),v1.end(),v2,begin(),v2.end());
//printing the position of the element, use it-v1.begin();

C++: Move a particular number to the back, using vector

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;
}

C++: Every time, the function is returning empty vector. Why?

I have been trying to solve this problem:
Given an array of integers, return indices of the two numbers such
that they add up to a specific target.
You may assume that each input would have exactly one solution,
and you may not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
I have been trying to solve this using map and my attempted solution is below:
#include <iostream>
#include <map>
#include <vector>
using namespace std;
vector<int> twoSum(vector<int>& nums, int target) {
std::map<int, int> indices;
for (int i = 0; i < nums.size(); ++i) {
auto it = indices.find(target - nums[i]);
if (it != indices.end())
return {it->first, i};
indices[i] = nums[i];
}
return {};
}
int main() {
std::vector<int> nums = {2, 7, 11, 15};
int target = 9;
std::vector<int> ans = twoSum(nums, target);
for(const auto &elem : ans) {
std::cout << elem << " ";
}
std::cout << "\n";
return 0;
}
To avoid two for loops, I'm trying to find target - num2 because num1 + num2 = target.
So, the logic is to
create a map from the input vector nums
use map.find(target - num[i])
return the first value of iterator pointing to target-nums[i], i.e., the key for the value = target-nums[i], in the map and i.
Therefore, for the above code, the function should return {0, 1}
However, every time, I get an empty vector. Could anyone please tell me whether my logic is wrong or I'm manipulating the map or vector wrongly?
You are very close to solving the problem but your code has a small yet critical issue.
You add elements to your map as (key=index, value=number)
indices[i] = nums[i];
... Essentially, you just recreated the array in a different format, so if you think about it, for every i, nums[i] == indices[i]. This approach really doesn't get you any further in solving your problem.
Instead, you should try mapping (key=number, value=index)
indices[nums[i]] = i;
This way, when you search the map for a previously-encountered number as you already are
auto it = indices.find(target - nums[i]);
... You end up searching for the number itself instead of an index!
Note that you will also need to change the return to use the index instead of the value, as the format of your map is now different
auto it = indices.find(target - nums[i]);
if (it != indices.end())
return {it->second, i};
try doing: indices[num[i]] = 1.
You are using the index as the key of the map, but you should really be using the values.
Don't use a map at all.
std::vector<int> twoSum(const std::vector<int> & nums, int target) {
for (int it1 = nums.begin(); it1 != nums.end(); ++it1) {
auto it2 = std::find(std::next(it1), nums.end(), target - *it1);
if (it2 != nums.end())
return {std::distance(nums.begin(), it1), std::distance(nums.begin(), it2)};
}
// You can assume this is never reached, throwing is noisier than returning empty
throw std::runtime_error("invalid arguments to twoSum");
}

Increasing Sequence C++

I try to solve this challenge on CodeFights, but, it doesn't work. My best solution got 25/26 (time limit exceeded on the last test) but I deleted that because I tried it yesterday (it was O(n^2)). Now I tried a new one in O(n). I am very tired and I really want to get this done today, so please help me.
Here are the statements:
Given a sequence of integers as an array, determine whether it is possible to obtain a strictly increasing sequence by removing no more than one element from the array.
Example
For sequence = [1, 3, 2, 1], the output should be
almostIncreasingSequence(sequence) = false;
There is no one element in this array that can be removed in order to get a strictly increasing sequence.
For sequence = [1, 3, 2], the output should be
almostIncreasingSequence(sequence) = true.
You can remove 3 from the array to get the strictly increasing sequence [1, 2]. Alternately, you can remove 2 to get the strictly increasing sequence [1, 3].
And here is my code until now... (poor code):
#include <iostream>
#include <vector>
#include <algorithm>
bool almostIncreasingSequence(std::vector<int> sequence)
{
int count = 0;
for(int i = 0; i < sequence.size()-1; i++)
{
if(sequence[i] > sequence[i+1])
{
count++;
sequence.erase(sequence.begin(), sequence.begin() + i);
i--;
}
if(count == 2)
return false;
}
return true;
}
int main()
{
std::cout << std::endl;
return 0;
}
Here is a C++11 solution with O(N) runtime:
constexpr auto Max = std::numeric_limits<std::size_t>::max();
bool is_sorted_but_skip(const std::vector<int>& vec, std::size_t index = Max){
auto Start = index == 0 ? 1 : 0;
auto prev = vec[Start];
for(std::size_t i = Start + 1; i < vec.size(); i++){
if(i == index) continue;
if(prev >= vec[i]) return false;
prev = vec[i];
}
return true;
}
bool almostIncreasingSequence(std::vector<int> v)
{
auto iter = std::adjacent_find(v.begin(), v.end(), [](int L, int R){ return L >= R; });
if(is_sorted_but_skip(v, std::distance(v.begin(), iter)))
return true;
return is_sorted_but_skip(v, std::distance(v.begin(), std::next(iter)));
}
We use std::adjacent_find to find the first element, iter greater than or equal its next element. Then we check that sequence is strictly sorted while skipping iter's position.
Otherwise, we check that the sequence is strictly sorted while we skip iter+1's position
Worse case complexity: 3 linear scan
Demo
Here's a hint (well, almost a solution really):
If you see a decrease between one element to the next, then you have to remove one of them (*).
Now, what if you find two decreases, between two disjoint pairs of elements? That's right :-)
Keeping that in mind, you should be able to solve your problem using a linear scan and a bit of constant-time work.
(*) excluding the first and the last pair of elements.
This is still O(N^2), because you delete the first element of the vector in each iteration. Don't delete the first element and don't i-- in the loop.
If you must erase the numbers (you don't, but still), at least do it from the end of the list. That way erasing a number is probably an O(1) operation (I'm not 100% sure that's how std::vector is implemented).
You really don't have to erase the numbers.
#include<iostream>
#include<vector>
using namespace std;
int almostIncreasingSequence( vector<int> sequence );
int main(){
int array[] = {40, 50, 60, 10, 20, 30};
std::vector<int> vect (array, array + sizeof(array) / sizeof(int) );
bool ret = almostIncreasingSequence(vect);
if( ret ){
std::cout<<"Array is strictly increasing.";
}
else{
std::cout<<"Array is not strictly increasing.";
}
return 0;
}
bool almostIncreasingSequence(std::vector<int> sequence) {
int val = 0;
int currentBig = sequence.at(0);
for (int i = 1; i < sequence.size(); i++){
if( currentBig < sequence.at(i))
{
currentBig = sequence.at(i);
}
else{
val++;
if( val>1)
{
return false;
}
if( i > 1 ){
if (sequence.at(i) > sequence.at(i-2)){
if( currentBig < sequence.at(i) ){
}
else{
currentBig = sequence.at(i);
}
}
}
else{
currentBig = sequence.at(i);
}
}
}
return true;
}

Finding consecutive elements in a vector

I am working on a problem where I have to create subvectors from a bigger vector. If the elements in the vector are consecutive I have to create a vector of those elements. If there are elements which are not consecutive then a vector of that single elements is created. My logic is as below
vector<int> vect;
for (int nCount=0; nCount < 3; nCount++)
vect.push_back(nCount);
vect.push_back(5);
vect.push_back(8);
vector<int>::iterator itEnd;
itEnd = std::adjacent_find (vect.begin(), vect.end(), NotConsecutive());
The functor NotConsecutiveis as below
return (int first != int second-1);
So I am expecting the std::adjacent_find will give me back the iterators such that I can create vector one{0,1,2,3}, vector two{5} and vector{8}. But I am not sure if there is any simpler way?
Edit:I forgot to mention that I have std::adjacent_find in a loop as
while(itBegin != vect.end())
{
itEnd = std::adjacent_find (vect.begin(), vect.end(), NotConsecutive());
vector<int> groupe;
if( std::distance(itBegin, itEnd) < 1)
{
groupe.assign(itBegin, itBegin+1);
}
else
{
groupe.assign(itBegin, itEnd);
}
if(boost::next(itEnd) != vect.end())
{
itBegin = ++itEnd;
}
else
{
vector<int> last_element.push_back(itEnd);
}
}
Does it make any sense?
I think this is what is being requested. It does not use adjacent_find() but manually iterates through the vector populating a vector<vector<int>> containing the extracted sub-vectors. It is pretty simple, IMO.
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> vect { 0, 1, 2, 3, 5, 8 };
// List of subvectors extracted from 'vect'.
// Initially populated with a single vector containing
// the first element from 'vect'.
//
std::vector<std::vector<int>> sub_vectors(1, std::vector<int>(1, vect[0]));
// Iterate over the elements of 'vect',
// skipping the first as it has already been processed.
//
std::for_each(vect.begin() + 1,
vect.end(),
[&](int i)
{
// It the current int is one more than previous
// append to current sub vector.
if (sub_vectors.back().back() == i - 1)
{
sub_vectors.back().push_back(i);
}
// Otherwise, create a new subvector contain
// a single element.
else
{
sub_vectors.push_back(std::vector<int>(1, i));
}
});
for (auto const& v: sub_vectors)
{
for (auto i: v) std::cout << i << ", ";
std::cout << std::endl;
}
}
Output:
0, 1, 2, 3,
5,
8,
See demo at http://ideone.com/ZM9ssk.
Due to the limitations of std::adjacent_find you can't use it quite the way you want to. However it can still be useful.
What you can do is to iterate over the collection, and use std::adjacent_find in a loop, with the last returned iterator (or your outer loop iterator for the first call) until it returns end. Then you will have a complete set of consecutive elements. Then continue the outer loop from where the last call to std::adjacent_find returned a non-end iterator.
Honestly, I don't find any clear disadvantage of using a simple hand-crafted loop instead of standard functions:
void split(const std::vector<int> &origin, vector<vector<int> > &result)
{
result.clear();
if(origin.empty()) return;
result.resize(1);
result[0].push_back(origin[0]);
for(size_t i = 1; i < origin.size(); ++i)
{
if(origin[i] != origin[i-1] + 1) result.push_back(vector<int>());
result.back().push_back(origin[i]);
}
}