the sequence of insert and erase using stl::list - c++

When I am doing practice on leetcode, I met a problem like this:
I used a stl::list container as cache for LRU algorithm. But the sequence of erasing an item and inserting an item made the result different.
I know that it is actually a double list as stl::list. And the sequence of inserting and erasing should not matter when I use iterator.
The code is here
class LRUCache{
public:
map<int, list<pair<int,int>>::iterator> mKey;
list<pair<int,int>> lCache;
int cap;
LRUCache(int capacity) {
cap = capacity;
}
int get(int key) {
auto iter = mKey.find(key);
if(iter != mKey.end()) {
int value = (iter->second)->second;
//**the sequence of next two lines can not be changed!***
lCache.erase(iter->second);
mKey[key] = lCache.insert(lCache.begin(), make_pair(key,value));
return value;
}
return -1;
}
void set(int key, int value) {
auto iter = mKey.find(key);
if(iter == mKey.end()) {
if(lCache.size() < cap) {
mKey[key] = lCache.insert(lCache.begin(), make_pair(key,value));
}
else{
mKey[key] = lCache.insert(lCache.begin(), make_pair(key,value));
mKey.erase(lCache.back().first);
lCache.pop_back();
}
}
else {
lCache.erase(iter->second);
mKey[key] = lCache.insert(lCache.begin(), make_pair(key,value));
}
}
};

It's not quite clear what you are asking. If your question is why these two lines can't be reordered:
//**the sequence of next two lines can not be changed!***
lCache.erase(iter->second);
mKey[key] = lCache.insert(lCache.begin(), make_pair(key,value));
then that's simple. iter points to the same node as mKey[key], so the assignment actually changes the value of iter->second. If the assignment would happen first, then iter->second would point to the freshly inserted list node, not the previously existing one.

Related

Iterator stops pointing to a value when it is added to a struct which is added to a vector of lists?

The premise of the project I am doing is to make skip lists with iterators and not pointers. I have created a vector of lists of nodes. And in the node struct, it contains an iterator which is supposed to be the iterator of the list below it while preserving position. The problem is when I create a new node, set its below iterator to the list below's iterator, and later try to access it by de referencing it, it seg faults. I think this is because the iterator is not initialized and it cant be dereferenced as it does not appear to be a bounds issue.
struct Node // in header file
{
int value;
list<Node>::iterator below;
Node(int v, list<Node>::iterator b){
value = v;
below = b;
}
Node(){}
Node(int v){
value = v;
}
};
vector<list<Node>> skipList; //this is the skipList initialized in the header
//insert called to add numbers to skiplist
void SkipLists::insert(int num){
list<Node>::iterator loc;
if(skipList.empty()){
list<Node> nodes;
nodes.push_back(Node(num));
skipList.push_back(nodes);
}else{
loc = insertPlace(num, skipList[skipList.size()-1].begin(), skipList.size() -1);
skipList[0].insert(loc, Node(num));
}
cout << "1. " << *this << "\n\n\n";
stack(num, loc);
//this if statement also segfaults
if(skipList.size() > 1){
cout << (*(skipList[1].front().below)).value;
}
}
//in insertPlace function it segfaults on the while loop's only if a recursive call is made. Meaning the previous value added to the skiplist had height to it. It segfaults when dereferencing it. I tested this by moving it out of the while loop.
list<Node>::iterator SkipLists::insertPlace(int num, list<Node>::iterator it, int height){
if(height == 0){
while(it != skipList[0].end() && skipList[0].size() > 0 && num > (*it).value){ // problem: likely not returning a good (*it).below or never setting it properly.
it++;
}
return it;
}
while(it != skipList[height].end() && skipList[height].size() > 0 && num > (*it).value){
cout << "he\n";
it++;
cout << "lo\n";
}
return insertPlace(num, (*it).below, --height);
}
stack is used to add vertical elements in the skip list based on probability. This is where the nodes are given a "below" iterator.
void SkipLists::stack(int num, list<Node>::iterator loc){
int flip = rand() % 2;
int count = 1;
list<Node>::iterator prev = loc;
list<Node>::iterator it;
while(flip == 1){
count++;
flip = rand() % 2;
if(skipList.size() < count){
list<Node> nodes;
nodes.push_back(Node(num, prev));
skipList.push_back(nodes);
prev = skipList[skipList.size()-1].begin();
}else{
it = skipList[count-1].begin();
while(it != skipList[count -1].end() && num > (*it).value){
it++;
}
prev = skipList[count -1].insert(it,Node(num, prev));
}
}
}
vector<list<Node>> skipList; is dangerous. If a new list is added then the vector might relocate all other lists and that invalidates all stored iterators. Even though the lists can be move constructed in a new place, they are still new objects and comparing .end() with a iterator obtained from another object is undefined behaviour.
I think that is what eventually happens in your code.
[Probably not a proper answer, but its too long for a comment and I won't debug author's code to make sure.]
One obvious error is your Node class implementation.
If you look at your Node constructor that takes a single int, you failed to initialize the below iterator. Thus any access in attempting to dereference below will result in undefined behavior occurring, as you're doing in this line:
cout << (*(skipList[1].front().below)).value;
If the skip list is empty, you will see that your code will produce Node objects where below is not initialized.
Here is a stripped down, simple example using more or less the code you posted:
#include <list>
#include <vector>
#include <iostream>
struct Node // in header file
{
int value;
std::list<Node>::iterator below;
Node(int v, std::list<Node>::iterator b) {
value = v;
below = b;
}
Node() {}
Node(int v) {
value = v;
}
};
class SkipLists
{
private:
std::vector<std::list<Node>> skipList;
public:
void insert(int num);
std::list<Node>::iterator insertPlace(int num, std::list<Node>::iterator it, int height);
void stack(int num, std::list<Node>::iterator loc);
};
using namespace std;
void SkipLists::insert(int num)
{
list<Node>::iterator loc;
if (skipList.empty())
{
list<Node> nodes;
nodes.push_back(Node(num));
skipList.push_back(nodes);
}
else
{
loc = insertPlace(num, skipList[skipList.size() - 1].begin(), skipList.size() - 1);
skipList[0].insert(loc, Node(num));
}
stack(num, loc);
//this if statement also segfaults
if (skipList.size() > 1) {
cout << (*(skipList[1].front().below)).value;
}
}
list<Node>::iterator SkipLists::insertPlace(int num, list<Node>::iterator it, int height)
{
if (height == 0) {
while (it != skipList[0].end() && skipList[0].size() > 0 && num > (*it).value)
{
it++;
}
return it;
}
while (it != skipList[height].end() && skipList[height].size() > 0 && num > (*it).value)
{
cout << "he\n";
it++;
cout << "lo\n";
}
return insertPlace(num, (*it).below, --height);
}
void SkipLists::stack(int num, list<Node>::iterator loc) {
int flip = rand() % 2;
int count = 1;
list<Node>::iterator prev = loc;
list<Node>::iterator it;
while (flip == 1) {
count++;
flip = rand() % 2;
if (skipList.size() < count) {
list<Node> nodes;
nodes.push_back(Node(num, prev));
skipList.push_back(nodes);
prev = skipList[skipList.size() - 1].begin();
}
else {
it = skipList[count - 1].begin();
while (it != skipList[count - 1].end() && num > (*it).value) {
it++;
}
prev = skipList[count - 1].insert(it, Node(num, prev));
}
}
}
// Test
int main()
{
SkipLists s;
s.insert(4);
}
You will see that below is not initialized on the line you are saying your application crashes on when running this very small sample.
You also have the same issue with the Node default constructor where both the value and below members are not initialized. When you create an object, all the members should be in some sort of valid state, or "null" in some way. For iterators, it is harder to do this since there isn't a "null" iterator, unless you can set the iterator to an existing list's end() iterator.
Basically you need to design your class so that you are sure that the iterator is pointing somewhere valid, or some other means of indicating that the iterator should not be dereferenced.

How to delete an object from a map which contains a vector as value in C++

I have a map which contains a of vector of type Messages.
std::map<std::string, std::vector<Message>> storage;
class Message has 3 member variables.
class Message
{
private:
std::string msg;
std::string msg_type;
int priority;
}
Now i am trying to delete an object which has priority(say 3) from the map. i am using the following function for it. But it doesn't work.
void deleteByMessagePriority(int priority)
{
if (checkPriorityOfMessage(priority))
{
for (std::map<std::string, std::vector<Message>>::iterator it = storage.begin(); it != storage.end(); it++)
{
std::vector<Message> listOfMsgs = it->second;
for (std::vector<Message>::iterator vec_it = listOfMsgs.begin(); vec_it != listOfMsgs.end(); vec_it++)
//for(int index = 0;index < listOfMsgs.size();index++)
{
if (vec_it->getPriority() == priority)
{
listOfMsgs.pop_back();
}
}
}
}
}
Look carefully at this:
if (vec_it->getPriority() == priority)
{
listOfMsgs.pop_back();
}
You're looking at the priority of one message (the one referred to by vec_it), but then what are you deleting if it matches?
Instead of writing your own loop, I'd use erase and std::remove_if to remove all the items you care about in that vector at once.
for (auto & item : storage) {
auto &vec = item.second;
auto start_junk = std::remove_if(
vec.begin(), vec.end(),
[=](Message const &m) { return m.priority == priority; });
vec.erase(start_junk, vec.end());
}
if (vec_it->getPriority() == priority)
{
listOfMsgs.pop_back();
pop_back() removes the last element of the vector which you don't want.You want to check erase
Also remember erase() invalidates the iterators so you need iterator to the next element after a deleted element for which we can fortunately use return value of erase
if (vec_it->getPriority() == priority)
{
vec_it = listOfMsgs.erase(vec_it); //returns iterator to the element after vec_it which can also be `listOfMsgs.end()`
std::vector<Message> listOfMsgs = it->second;
.
.
.
listOfMsgs.pop_back();
You're copying the list, only to modify the copy. What you meant is:
std::vector<Message>& listOfMsgs = it->second;
Then you can proceed erasing elements. As Gaurav Sehgal says, use the return value of erase:
std::vector<Message>::iterator vec_it = listOfMsgs.begin();
while (vec_it != listOfMsgs.end())
{
if (vec_it->getPriority() == priority)
{
vec_it = listOfMsgs.erase(vec_it);
}
else
{
++vec_it;
}
}

How to replace std::vector by linked list?

I have used std::vector for making my algorithm. I would like to replace the vectors by linked lists.
In order to do so, I was thinking of using the std::list, but I have no idea how to do this, for example I have tried following example for finding a value within a vector/list:
void find_values_in_vector(const std::vector<int>& input_vector, int value, int &rv1, int &rv2)
{
if (input_vector[0] >= value) { // too small
rv1 = 0; rv2 = 0; return;
}
int index = (int)input_vector.size() - 1;
if (input_vector[index] <= value) { // too big
rv1 = index; rv2 = index; return;
}
// somewhere inside
index = 0;
while (input_vector[index] <= value) {
index++;
}
rv1 = index - 1; rv2 = index; return;
}
void find_values_in_list(const std::list<int>& input_list, int value, int &rv1, int &rv2)
{
if (*input_list.begin() >= value) { // too small
rv1 = 0; rv2 = 0; return;
}
if (*input_list.end() <= value) { // too big
rv1 = (int)input_list.size() - 1; rv2 = (int)input_list.size() - 1; return;
}
// somewhere inside
int index = 0; int temp = *input_list.begin();
while (temp <= value) {
temp = *input_list.next(); index++;
}
rv1 = index - 1; rv2 = index; return;
}
This seems not to work, as the member function next() is not existing. However I remember that browsing through a linked list is done by going to the beginning, and moving further to the next element until the a certain point is reached. I have seen that there is a way to get this done by using an interator in a for-loop, but I wonder what's wrong with my approach? I was under the impression that a std::list was a standard implementation of a double-directional linked list, or am I wrong and in that case, what std class is the implementation of a linked list (it does not need to be a double-directional linked list)?
The standard way to iterate through containers is like this:
for(std::list<int>::iterator it = input_list.begin();
it != input_list.end();
it++)
{
....
}
This also works for vectors,maps,deque,etc. The Iterator concept is consistently implemented throughout the STL so it's best to get used to this concepts.
There are also iterator operations like std::distance and std::advance etc. for the different types of iterators (I suggest you read up on them and their advantages/limitations)
If you have C++ 11 available you can also use this syntax (may not be useful for your problem though.)
for(const auto& value : input_list)
{
...
}
This also works throughout the STL container.
This should work for vector, list, deque, and set (assuming the contents are sorted).
template <class T>
void find_values_in_container(const T& container, int value, int &rv1, int &rv2)
{
rv1 = rv2 = 0; // Initialize
if (container.empty() || container.front() >= value)
{
return;
}
for (const auto& v : container)
{
rv2++;
if (v > value)
{
break;
}
rv1++;
}
return;
}

iterator over multiple std containers subsequently

I have the following class (which obviously does not yet work as intended):
class A
{
private:
std::vector<int> firstVector, secondVector;
public:
std::vector<int>::iterator begin(){
return firstVector.begin();
}
std::vector<int>::iterator end(){
return secondVector.end();
}
};
How can I define an iterator which will go over the two member containers subsequently, e.g. after firstVector.end()-1 secondVector.begin() is returned and going all the way to secondVector.end() ?
Basically you need to define some custom iterator that internally checks for the end of the first range, then goes on to the next one.
However, this sort of stuff occurs a lot. Eventually you'd ask why an iterator for two vectors, why an iterator for two vectors, why a sequence of the same container type, and so on. Nir Tzachar & I have written a C++ port of Python itertools that does this sort of common stuff. In this case, you'd just use
chain(firstVector, secondVector)
It can be downloaded from this bitbucket repo.
Nothing to stop you from rolling your own. Can even make it random access!
struct chain_iterator
: std::iterator<std::random_access_iterator_tag, int>
{
using it = std::vector<int>::iterator;
std::pair<it, it> v1, v2;
bool first;
it cur;
};
We keep the initial iterator pairs so that we can do random access correctly.
Incrementing is what you'd expect:
chain_iterator& operator++() {
++cur;
if (first && cur == v1.second) {
first = false;
cur = v2.first;
}
return *this;
}
Dereference is trivial:
int& operator*() { return *cur; }
Advance has to do some extra checking:
chain_iterator& operator+=(size_t n) {
if (!first) {
// trivial case
cur += n;
}
else {
size_t d = v1.second - cur;
if (d < n) {
cur += n;
}
else {
first = false;
cur = v2.first + (d - n);
}
}
return *this;
}
I'll leave the rest of the operations as an exercise.
You could write your own function to do the incrementing:
std::vector<int>::iterator& inc(std::vector<int>::iterator& it) {
++it;
if (it == firstVector.end())
it = secondVector.begin();
return it;
}
This is also a good indication to others that the increment doesn't happen normally.

C++ map of objects erase when key not present

I have the following map in C++ (gcc):
map<int, EdgeExtended> myMap;
where the definition of EdgeExtended is:
struct EdgeExtended {
int neighborNodeId;
int weight;
int arrayPointer;
bool isCrossEdge;
EdgeExtended(Edge _edge, int _arrayPointer) {
neighborNodeId = _edge.neighborNodeId;
weight = _edge.weight;
arrayPointer = arrayPointer;
isCrossEdge = _edge.isCrossEdge;
}
EdgeExtended(const EdgeExtended & _edge) {
neighborNodeId = _edge.neighborNodeId;
weight = _edge.weight;
arrayPointer = _edge.arrayPointer;
isCrossEdge = _edge.isCrossEdge;
}
EdgeExtended(int _neighborNodeId, int _weight, bool _isCrossEdge, int _arrayPointer) {
neighborNodeId = _neighborNodeId;
weight = _weight;
arrayPointer = _arrayPointer;
isCrossEdge = _isCrossEdge;
}
void setValues(int _neighborNodeId, int _weight, bool _isCrossEdge, int _arrayPointer) {
neighborNodeId = _neighborNodeId;
weight = _weight;
arrayPointer = _arrayPointer;
isCrossEdge = _isCrossEdge;
}
EdgeExtended() {
neighborNodeId = -1;
weight = -1;
arrayPointer = -1;
isCrossEdge = false;
}
};
I want to do this (plain example):
EdgeMap edge;
int nodeId=18;
edge=map.erase(nodeId);
a) Is this code correct, does erase return the object that corresponds to the key? b) If yes, what does erase return when the key is not present? c) if this code is wrong, how can I check if a key is present, the object mapped to the key and then erase the pair from map. Keep in mind that performance is rather crucial, so I need the most efficient way.
a) No, it is not correct. The std::map::erase method you call returns the number of erased elements.
What you can do is use std::map::find to check if the an element with the given key is in the map. This returns an iterator to the element if it exists, to end() if it doesn't. You can pass this iterator to the relevant std::map::erase overload.
EdgeMap edge;
int nodeId=18;
ExtendedEdge removedEdge;
....
EdgeMap::iterator it = edge.find(nodeId);
if( it != edge.end() )
{
// found element.
removedEdge = it->second; // or removeEdge = std::move(it->second) in C++11
edge.erase(it);
}
a) Is this code correct, does erase return the object that corresponds
to the key?
No, this returns the number of elements that were removed.
b) If yes, what does erase return when the key is not present?
N/A. It simply returns 0.
c) if this code is wrong, how can I check if a key is present, the
object mapped to the key and then erase the pair from map. Keep in
mind that performance is rather crucial, so I need the most efficient
way.
auto it = yourMap.find(nodeId);
if (it != yourMap.end()) {
EdgeExtended theObjectToRemove = *it;
yourMap.erase(it);
}
The complexity of this is the same as for a plain erase(nodeId);, as erase(it) takes amortized constant time.
I would do it with an iterator, like this:
typedef map<int, EdgeExtended> EdgeMap;
bool RemoveEdge( EdgeMap & myMap, int nodeId, EdgeExtended &edge )
{
EdgeMap::iterator e = myMap.find(nodeId);
if( e == myMap.end() ) return false;
edge = e->second;
myMap.erase(e);
return true;
}