I'm having trouble figuring out how to delete an item from a list.
Please note that I would like to perform the deletion from the advance() function. This code is just boiled down from my actual project, to try to isolate the error.
#include <iostream>
#include <list>
#include <iterator>
#include <algorithm>
using namespace std;
const int SCT_OSC_FILLED = 11;
class OrderInfo {
private:
std::string id;
public:
OrderInfo(std::string a, int aStatusCode);
std::string key();
int statusCode;
};
OrderInfo::OrderInfo(std::string a, int aStatusCode) {
id = a;
statusCode = aStatusCode;
}
std::string OrderInfo::key() {
return id;
}
std::list <OrderInfo> MasterOrders;
void testList();
void add(OrderInfo ordInfo);
void advance(OrderInfo ordInfo, std::list <OrderInfo> ::iterator& orderIter);
void testList() {
OrderInfo o1("1", 15);
OrderInfo o2("2", 16);
OrderInfo o3("3", SCT_OSC_FILLED);
OrderInfo o4("4", 17);
OrderInfo o5("5", SCT_OSC_FILLED);
OrderInfo o6("6", 18);
add(o1);
add(o1);
add(o2);
add(o3);
add(o4);
add(o5);
add(o6);
for (auto v : MasterOrders)
std::cout << v.key() << "\n";
}
void add(OrderInfo ordInfo) {
// Add to MasterOrders (if not already in list)
bool alreadyInList = false;
std::list <OrderInfo> ::iterator orderIter = MasterOrders.begin();
while (orderIter != MasterOrders.end())
{
OrderInfo oi = *orderIter;
alreadyInList = ordInfo.key() == oi.key();
if (alreadyInList) break;
advance(ordInfo, orderIter);
}
if (!alreadyInList) MasterOrders.push_front(ordInfo);
}
void advance(OrderInfo ordInfo, std::list <OrderInfo> ::iterator& orderIter) {
bool iterate = true;
if (ordInfo.statusCode == SCT_OSC_FILLED) {
orderIter = MasterOrders.erase(orderIter++); // https://stackoverflow.com/a/5265561/270143
iterate = false;
}
if (iterate) orderIter++;
}
int main()
{
testList();
return 0;
}
update: I forgot to state the actual goal
my goal is to just delete SCT_OSC_FILLED ordInfos from within the advance() method (that part is important) and leave the rest. My actual project code does more than what is shown, these names of functions are just made up for this example.. there is more code in them not directly related to manipulating the list (but related to processing OrderInfo) in my actual project. My goal is to leave one copy of o1 in the list as well as o2, o4 and o6 - removing o3 and o5 because they have an SCT_OSC_FILLED OrderInfo.statusCode
So the problem is nothing to do with deletion from a list. Your logic is simply wrong given the stated goal.
You want to delete all SCT_OSC_FILLED items from the list when adding an item but the code you write deletes all items from the list when you add an item with SCT_OSC_FILLED. You are simply testing the wrong thing.
Change this
void advance(OrderInfo ordInfo, std::list <OrderInfo> ::iterator& orderIter) {
bool iterate = true;
if (ordInfo.statusCode == SCT_OSC_FILLED) {
orderIter = MasterOrders.erase(orderIter++);
iterate = false;
}
if (iterate) orderIter++;
}
to this
void advance(OrderInfo ordInfo, std::list <OrderInfo> ::iterator& orderIter) {
bool iterate = true;
if (orderIter->statusCode == SCT_OSC_FILLED) {
orderIter = MasterOrders.erase(orderIter);
iterate = false;
}
if (iterate) orderIter++;
}
Once you make that change you can see that the ordInfo parameter is unused. Add bit more cleanup and you end up with this much simpler function
void advance(std::list <OrderInfo> ::iterator& orderIter) {
if (orderIter->statusCode == SCT_OSC_FILLED) {
orderIter = MasterOrders.erase(orderIter);
}
else {
orderIter++;
}
}
According to cppreference.com, "References and iterators to the erased elements are invalidated." You should get an iterator to the next element, before deleting the current element.
In the same page, cppreference gives an example:
// Erase all even numbers (C++11 and later)
for (std::list<int>::iterator it = c.begin(); it != c.end(); ) {
if (*it % 2 == 0) {
it = c.erase(it);
} else {
++it;
}
}
erase returns an iterator to the next element (end() if the removed element was the last), so "it = c.erase(it);" makes "it" to point to the next element, and there is no need for incrementing the iterator (with ++).
So you could have something like:
void advance(OrderInfo ordInfo, std::list <OrderInfo> ::iterator& orderIter) {
if (ordInfo.statusCode == SCT_OSC_FILLED) {
orderIter = MasterOrders.erase(orderIter);
} else {
orderIter++;
}
}
Related
I have a class that has this kind of structure:
class myClass(){
public:
myClass(){//empty constructor}
void insertRecursively(string word) {
myClass* node = this;
for (int i = 0; i < word.length(); i++) {
if (node->map.find(word.at(i)) == node->map.end()) {
node->map[word.at(i)] = new myClass();
}
node = node->map[word.at(i)];
}
node->isEnd = true;
}
private:
unordered_map<char, myClass*> map = {};
bool isEnd = false;
}
I tried write destructor in this way but it gives me error 'std::bad_alloc':
~myClass() {
clear(map);
}
void clear(unordered_map<char, myClass*> map) {
for (auto& pair : map) {
if (pair.second != nullptr) {
clear(pair.second->map);
}
delete pair.second;
}
}
From what I known so far, I allocated memory on heap by using new keyword, so I should create destructor for myClass. I need to do this recursively because map contains pointers to other myClass pointers.
I've researched several hours and still cannot figure it out.
Can anyone help me to spot the problem that will cause 'std::bad_alloc' ?
My entire code:
class Trie {
public:
Trie() {
}
void insert(string word) {
Trie* node = this;
for (int i = 0; i < word.length(); i++) {
if (node->map.find(word.at(i)) == node->map.end()) {
node->map[word.at(i)] = new Trie();
}
node = node->map[word.at(i)];
}
node->isEnd = true;
}
bool search(string word) {
Trie* node = this;
for (int i = 0; i < word.length(); i++) {
if (node->map.find(word.at(i)) == node->map.end()) {
return false;
} else {
node = node->map[word.at(i)];
}
}
return node->isEnd;
}
bool startsWith(string prefix) {
Trie* node = this;
for (int i = 0; i < prefix.length(); i++) {
if (node->map.find(prefix.at(i)) == node->map.end()) {
return false;
} else {
node = node->map[prefix.at(i)];
}
}
return true;
}
~Trie() {
clear(map);
}
void clear(unordered_map<char, Trie*> map) {
for (auto& pair : map) {
if (pair.second != nullptr) {
clear(pair.second->map);
}
delete pair.second;
}
}
private:
unordered_map<char, Trie*> map = {};
bool isEnd = false;
};
I draw your attention to the following lines of code.
unordered_map<char, myClass*> map = {};
void clear(unordered_map<char, myClass> map)
Trimming a few characters...
unordered_map<char, myClass*> map
void clear(unordered_map<char, myClass> map)
Observe that you declared the local item map over char and myClass*, but you declared clear() as operating on a map over char and myClass, as opposed to a map over char and myClass*.
The * makes a difference.
Either you start to use smart pointers or you need to bring up a concept of ownership for your myClass instances created with new.
The latter could be:
Instances aof myClass are owned by another instance of myClass (lets call it owner) where &a appears in owner.map (You are doing this already).
Whenever an instance owner becomes destroyed, it has to free all instances it (directly) owns:
~myClass() {
for(const auto& pair : map){
delete pair.second;
}
}
Your Trie::clear() method deletes the same Trie twice.
~Trie() {
clear(map);
}
void clear(unordered_map<char, Trie*> map) {
for (auto& pair : map) {
if (pair.second != nullptr) {
clear(pair.second->map);
}
delete pair.second;
}
}
The destructor ~Trie calls clear(pair.second->map) and then calls delete pair.second which is the same as clear(pair.second->map) again since delete calls the destructor of the pointed-at data. Calling clear() twice on the same map means that the second call is trying to delete already deleted data, which causes the crash. Calling delete on a pointer does not change the value of the pointer, which is why the nullptr check does nothing.
Since the destructor calls clear(), the clear() method does not need to explicitly call itself recursively. Just delete the map pointers.
~Trie() {
clear(map);
}
void clear(unordered_map<char, Trie*> map) {
for (auto& pair : map) {
delete pair.second;
}
}
By the way, calling delete nullptr is fine since it is required to do nothing.
I am using Visual Studio 2017 as instructed for this project.
I have received four errors for my program that I have been researching for a couple of days and no matter what I have done, I cannot get these errors to disappear.
I have tried making multiple constructors in BDictionary, I have checked all function names and there are no typos, and I have gone over this code with other people and compared their versions to see why it is getting these errors. I am only worried about getting it to run at this point as I can figure out what functions do not work from there. Any and all help would be appreciated.
bagtestmain.cpp -This file cannot be changed
#include <string>
#include <sstream>
#include "ABag.h"
#include "BDictionary.h"
using namespace std;
const size_t DICTIONARY_SIZE = 20;
void PauseScreen(); //Used to pause screen output
/*
* Tests BDictionary with int and string objects only.
*/
int main(int argc, char** argv) {
cout << "Student Name -- CSIS 215 Programming Assignment 1 -- Bag Dictionary" << endl << endl;
BDictionary<int, string> myIntStrDict(DICTIONARY_SIZE);
//Error C2259 'BDictionary<int,std::string>': cannot instantiate abstract class 27
//Error C2664 'BDictionary<int,std::string>::BDictionary(const BDictionary<int,std::string> &)': cannot convert argument 1 from 'const size_t' to 'const BDictionary<int,std::string> &' 27
BDictionary<string, int> myStrIntDict(DICTIONARY_SIZE);
//Error C2259 'BDictionary<std::string,int>': cannot instantiate abstract class 28
//Error C2664 'BDictionary<std::string,int>::BDictionary(const BDictionary<std::string,int> &)': cannot convert argument 1 from 'const size_t' to 'const BDictionary<std::string,int> &' 28
I included the two errors that are with each line.
ABag.h
#ifndef ABAG_H
#define ABAG_H
#include "book.h"
#include "bagADT.h"
#include <string>
#include <stdlib.h>
template <typename E>
class ABag : public Bag<E> {
private:
int position, capacity;
const int max = 10;
E* data;
public:
// constructors/destructor
ABag()
{
max = defaultSize;
capacity = position = 0;
data = new E[max];
}
~ABag()
{
delete[] data;
}
// Insert a new item into the bag -- return false if fails and true if
// successful
bool addItem(const E& item)
{
if (bagCapacity < 10)
{
position++;
data[position]; //incorrect
capacity++;
return true;
}
else
{
return false;
}
}
// Looks for 'item' in the bag and if found updates 'item' with the
// bag value and returns true. Otherwise 'item' is left unchanged
// and the method returns false.
bool remove(E& item)
{
if (position == -1) // check if bag is full
{
return false;
}
else
{
for (int i = position; i >= 0; i--) // loop through each object top to bottom
{
if (data[i] == item) // if the values are equal, then return the item
{
item = data[i];
position--;
for (int j = i; j <= position; j++)
{
data[j] = data[j + 1]; // shift down the objects on top
}
}
}
return true;
}
}
// Removes the top record from the bag, puts it in returnValue, and
// returns true if the bag is not empty. If the bag is empty the
// function returns false and returnValue remains unchanged.
bool removeTop(E& returnValue)
{
if (inspectTop(returnValue))
{
return false;
}
position--; //return
return true;
}
// Finds the record using returnValue and if the record is found updates
// returnValue based on the contents of the bag and returns true. If the
// record is not found the function returns false. Works just like remove()
// except that the found record is not removed from the bag.
bool find(E& returnValue) const
{
if (position == -1)
{
return false;
}
else
{
for (int i = position; i >= 0; i--) // loops through objects and returns value
{
if (data[i] == returnValue)
{
returnValue = data[i]; // returns the KVpair found
return true;
}
}
}
return false;
}
// Inspect the top of the bag. If the bag is empty return
// false and leave 'item' unchanged; otherwise, return true and update
// 'item' with the contents of the bag.
bool inspectTop(E& item) const
{
if (position == -1)
{
return false;
}
else
{
item = data[position];
return true;
}
}
// empties the bag
void emptyBag()
{
position = -1; // Moves the position down to -1, in a sense removing the elements
}
// use the += operator to add an item to the bag
bool operator+=(const E& addend)
{
return addItem(addend); // No need to rewrite addItem, just call it from here
}
// get the size of the bag
int size() const
{
return position + 1; // returns the actual size
}
// get the capacity of the bag
int bagCapacity() const
{
return capacity;
}
// bag methods: addItem, remove, operator+=, size, etc.
};
#endif /* ABAG_H */
BDictionary.h
#ifndef BDICTIONARY_H
#define BDICTIONARY_H
#include "ABag.h"
#include "dictionaryADT.h"
#include "kvpair.h"
#include "book.h"
#include <string>
using namespace std;
template <typename Key, typename E>
class BDictionary : public Dictionary<int, string>{
public:
// constructors/destructor
BDictionary()
{
max = defaultSize;
capacity = -1;
position = -1;
dictionary = new E[max];
} // Default constructor
BDictionary(const Key& k, const Key& e)
{
max = defaultSize;
capacity = position = 0;
}
~BDictionary()
{
delete[] data.
} // Base destructor
// Reinitialize dictionary
void clear()
{
dictionary->emptyBag(); // Calls ABag's emptyBag
}
// Insert a record
// k: The key for the record being inserted.
// e: The record being inserted.
// Return true if insert is successful and false otherwise
bool insert(const Key& k, const E& e)
{
KVpair<int, string> item(k, e);
if (dictionary->addItem(item))
{
return true;
}
else
{
return false;
}
}
// Looks for a record using the key and if found does the following:
// - updates the E& rtnVal
// - removes the record from the dictionary
// - returns true
// If the record is not found the function returns false.
bool remove(const Key& k, const E& rtnVal)
{
KVpair<int, string> item(k, rtnVal);
if (dictionary->remove(item))
{
return true;
}
else
{
return false;
}
}
// Takes an arbitrary record from the dictionary and does the following:
// - updates the E& returnValue
// - removes the record from the dictionary
// - returns true
// If the dictionary is empty the function returns false.
bool removeAny(E& returnValue)
{
/*Key* k;
k = new Key();
KVpair<Key, E> fill(*k, returnValue);
if (dictionary->removeTop(fill)) // Use of removeTop since we can delete ANY arbitrary element
{
returnValue = fill.value();
delete k;
return true;
}
else
{
delete k;
return false;
}*/
KVpair<int, string> item(0, "");
if (dictionary->removeTop(item))
{
//cout << "REMOVEANY: Removed key " << item.key() << ", which contained the value " << item.value() << endl;
return true;
}
else
{
return false;
}
}
// Looks for a record using the key and if found does the following:
// - updates the E& returnValue
// - returns true
// If the record is not found the function returns false.
bool find(const Key& k, E& returnValue) const
{
KVpair<int, string> item(k, returnValue);
if (dictionary->find(item))
{
//std::cout << "FIND: The value at key " << item.key() << " is " << item.value() << endl;
return true;
}
else
{
return false;
}
}
// Return the number of records in the dictionary.
int size()
{
return dictionary->size(); // calls size function from ABag
}
// methods: clear, insert, remove, removeAny, find, size, etc.
private:
//Pointer to a ABag object. You'll need to instantiate the bag in your constructor:
// dictionary = new ABag<KVpair<Key, E>>(size) or something similar depending on how
// you've implemented your ABag constructor(s).
//This pointer gives you access to the bag which stores your data and provides the
//functions you need to build your dictionary.
ABag<KVpair<int, string>>* dictionary;
int capacity, position;
const size_t max;
};
#endif /* BDICTIONARY_H */
I am trying to use std::vector in a class to realize a muti-way tree.
Each time when I want to add a child in on of the member, I use a function addMember. I'm using VS2017 to debug this program. In this function scope the parent's children vector has actually add the elements by push_back(), but after exiting the function, the address of vector will change and the elements I have added will disappear.
Here's my code:
#include <iostream>
#include<string>
#include<vector>
using namespace std;
class member {
public:
string name;
member* parent;
vector<member*> children;
member(string m_name,member* m_parent):name(m_name),parent(m_parent){}
};
class familyTree {
private:
member ancestor;
public:
member* getAncestor() { return &ancestor; }
familyTree(member& m_ancestor):ancestor(m_ancestor){}
member* searchMember(string name,member* node,bool& flag);
void addMember(string name, int children_number,vector<string>& children_name);
};
member* familyTree::searchMember(string name, member* node,bool& flag) {
member* find = NULL;
if (node) {
if (node->name == name)
find = node;
else {
if (!flag) {
for (auto iter = node->children.begin(); iter != node->children.end(); iter++) {
find = searchMember(name, *iter, flag);
if (flag)
break;
}
}
}
}
return find;
}
void familyTree::addMember(string name,int children_number,vector<string>& children_name) {
bool flag = false;
member* parent = searchMember(name, getAncestor(), flag);
for (auto i : children_name) {
member* child = new member(i,parent);
parent->children.push_back(child);
}
}
I suspect that there is something wrong with the familyTree::searchMember function. Here was what you posted:
member* familyTree::searchMember(string name, member* node, bool& flag) {
member* find = NULL;
if (node) {
if (node->name == name)
find = node;
else {
if (!flag) {
for (auto iter = node->children.begin(); iter != node->children.end(); iter++) {
find = searchMember(name, *iter, flag);
if (flag)
break;
}
}
}
}
return find;
}
Notice that there was no method to set the flag = true once you have found the correct node.
The 2nd if statement should be:
if (node->name == name) {
find = node;
flag = true;
}
Otherwise within your for loop,
for (auto iter = node->children.begin(); iter != node->children.end(); iter++) {
find = searchMember(name, *iter, flag);
if (flag)
break;
}
even if there was a successful search, the for loop does not break and so it continues on, and the next child you search is guaranteed to not be a match.
Note that the way your searchMember function is structured results in NULL being returned if you search through a node that does not have a matching name and does not have any children because the for loop is skipped (no children to iterate through). So you end up with a lot of NULL pointers being assigned to parent in addMember.
I'm trying to store the numbers in my driver class by arranging them with insert and attach functions from the implementation file. Insert function is supposed to move the number to the left or a smaller position and attach moves to the right or higher position in the data array.This is the implementation one:
#include "Lab1A.h"
#include <iostream>
#include <cassert>
#include <algorithm>
sequence::sequence() {
used = 0;
current_index = 0;
}
// MUTATOR MEMBER FUNCTIONS
//Postcondition: The first item in the sequence becomes the current item
void sequence::start() {
current_index = 0;
//Precondition: is_item returns true.
//Postcondition: If the current item was already the last item in the
//sequence, then there is no longer any current item. Otherwise, the new
//current item is the item immediately after the original current item.
}
void sequence::advance() {
if (is_item()) {
current_index++;
}
}
//Precondition: size( ) < CAPACITY.
//Postcondition: A new copy of entry has been inserted in the sequence
//before the current item. If there was no current item, then the new entry
//has been inserted at the front of the sequence (position 0). In either //case, the newly inserted item is now the current item of the sequence.
void sequence::insert(const value_type& entry) {
if (size() < CAPACITY) {
data[used] = data[used - 1];
data[used] = entry;
data[current_index] = entry;
used++;
}
if (is_item() == false) {
data[used] = entry;
data[used] = data[used + 1];
}
}
//Precondition: size( ) < CAPACITY.
//Postcondition: A new copy of entry has been inserted in the sequence //after the current item. If there was no current item, then the new entry //has been attached to the end of the sequence. In either case, the newly
//inserted item is now the current item of the sequence.
void sequence::attach(const value_type& entry) {
if (size() < CAPACITY) {
data[used] = data[used + 1];
data[used] = entry;
data[current_index] = entry;
used++;
}
if (is_item() == false) {
data[used] = entry;
data[used] = data[used + 1];
}
}
//Precondition: is_item returns true.
//Postcondition: The current item has been removed from the sequence, and //the item after this (if there is one) is now the new current item.
void sequence::remove_current() {
int i;
if (is_item()) {
current_index--;
data[i] = data[current_index];
}
}
// ACCESSOR MEMBER FUNCTIONS
//Postcondition: The value returned is the number of items in the
//sequence.
int sequence::size() const {
return used;
}
//Postcondition: A true return value indicates that there is a valid
//"current" item that may be retrieved by invoking the current
//member function below. A false return value indicates that
//there is no valid current item.
bool sequence::is_item() const {
return (current_index < used);
}
//Precondition: is_item( ) returns true.
//Postcondition: The item returned is the current item in the sequence.
sequence::value_type sequence::current() const {
return data[current_index];
}
void sequence::print() {
for (int j = 0; j < used; j++) {
cout << data[j] << " ";
}
}
Driver file:
#include <iostream>
#include <cstdlib>
#include "Lab1Aimplementation.cpp"
using namespace std;
int main()
{
sequence numbers;
numbers.insert(21);
numbers.attach(33);
numbers.insert(22);
numbers.print();
return 0;
}
I'm trying to get this output: 21 22 33
Instead I get: 22 33 22
Possible declaration of sequence as OP didn't attach one:
class sequence
{
using index_type = int;
using value_type = size_t;
static const index_type CAPACITY = 1024;
value_type data[CAPACITY];
index_type used;
index_type current_index;
public:
sequence();
void start();
void advance();
void insert(const value_type& entry);
void attach(const value_type& entry);
void remove_current();
int size() const;
bool is_item() const;
value_type current() const;
void print();
};
I'm using here these assumptions:
current_index can be represented by an iterator; when no element is available, it points to past-the-end.
insert is supposed to insert before the current item, attach is supposed to insert after the current item.
Both insertion methods, judging by the expected result (21 22 33) are such that after being called, the current item always refers to the newly inserted item.
This said, if you just wrap around a list, each function is basically a one liner, you just need to know that the list class does. Bonus: O(1) insertion and removal, capacity constraint virtually removed.
The OP resets current_index to 0, so it is reasonable to assume that it could point anywhere in the array. Therefore, it is not possible to achieve insertion just by swapping elements, you need to move a whole block of data. Try this out here.
#include <iostream>
#include <sstream>
#include <list>
#include <cassert>
template <typename T>
class sequence
{
private:
std::list<T> _l;
typename std::list<T>::iterator _i;
public:
using value_type = T;
using size_type = typename std::list<T>::size_type;
size_type size() const {
return _l.size();
}
T current() const {
assert(is_current_valid());
return *_i;
}
size_type current_index() const {
return _i - _l.begin();
}
void increase_current() {
if (is_current_valid()) {
++_i;
}
}
void decrease_current() {
if (_i != _l.begin()) {
--_i;
}
}
void reset_current() {
_i = _l.begin();
}
bool is_current_valid() const {
// "is_item"
return _i != _l.end();
}
void remove_current() {
assert(is_current_valid());
// _i takes the next current element, eventually end()
_i = _l.erase(_i);
}
void insert_before(const value_type &entry) {
// _i is always the newly inserted element
_i = _l.insert(_i, entry);
}
void insert_after(const value_type &entry) {
// _i is always the newly inserted element
assert(is_current_valid());
_i = _l.insert(++_i, entry);
}
friend std::ostream &operator<<(std::ostream &os, sequence const &s) {
for (auto it = s._l.begin(); it != s._l.end(); ++it) {
if (it != s._l.begin()) {
os << " " << *it;
} else {
os << *it;
}
}
return os;
}
sequence() : _l(), _i(_l.end()) {}
};
int main() {
sequence<std::size_t> numbers;
numbers.insert_before(21); // insert 21, then points to 21
numbers.insert_after(33); // 33 after 21, then points to 33
numbers.insert_before(22); // 22 before 21, then points to 22
std::cout << numbers << std::endl;
// Programmatically check if the result is the requested one
const std::string expected = "21 22 33";
std::stringstream output;
output << numbers;
if (output.str() != expected) {
std::cerr << "Error!" << std::endl;
return 1;
}
return 0;
}
Found the answer at http://www.cplusplus.com/forum/beginner/141458/. This code works, however I don't understand the logic behind what is I guess a backwards loop.
void sequence::attach(const value_type& entry) {// value_type is the declared typedef double data
int i;
if(!is_item()) // is_item checks if there's any number in the array
current_index = used - 1; // used keeps track of how many numbers are stored
for (i = used; i > current_index; --i)
data[i]=data[i-1];
data[current_index+1] = entry;
++current_index;
++used;
}
void sequence::insert(const value_type& entry){
int i;
if(!is_item())
current_index = used;
for (i = used; i > current_index; --i)
data[i]=data[i-1];
data[current_index] = entry;
++current_index;
++used;
}
I was doing an exercise in an online judge:
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and set.
get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
set(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.
I basically use std::list and std::unordered_map and works good in small input case. But OJ give a Time Limit Exceeded on the input: cache size is 2048 and 20000+ get & set operations.
Time Limit Exceeded version:
class LRUCache {
public:
LRUCache(int capacity):cacheSize(capacity) {
}
int get(int key) {
auto it = mapping.find(key);
if(it == mapping.end())
return -1;
itemList.splice(itemList.begin(),itemList,it->second);
//mapping[key] == it->second still holds
return it->second->second;
}
void set(int key, int value) {
auto it = mapping.find(key);
if(it != mapping.end()) {
itemList.splice(itemList.begin(),itemList,it->second);
it->second->second = value;
} else {
itemList.push_front(make_pair(key,value));
mapping.insert(make_pair(key,itemList.begin()));
}
if(itemList.size() > cacheSize) {
mapping.erase(itemList.back().first);
itemList.pop_back();
}
}
private:
int cacheSize;
list<pair<int,int> > itemList;
unordered_map<int,list<pair<int,int> >::iterator> mapping;
};
Then I thought why not erase element before insert one, so I modify set function and OJ accept!
Accept version:
void set(int key, int value) {
auto it = mapping.find(key);
if(it != mapping.end()) {
itemList.splice(itemList.begin(),itemList,it->second);
it->second->second = value;
} else {
if(itemList.size() == cacheSize) {
mapping.erase(itemList.back().first);
itemList.pop_back();
}
itemList.push_front(make_pair(key,value));
mapping.insert(make_pair(key,itemList.begin()));
}
}
I wonder what makes such a different?
The reason is that the OJ you use uses a C++ compiler which has std::list::size with linear complexity. In C++11 they require it to be constant, but in C++98 it could be up to linear, and many implementations actually have it linear.
See complexity here on the C++98 tab: http://www.cplusplus.com/reference/list/list/size/
I located the OJ that you were using, and managed to get TLE with your code, but managed to get it accepted with a small modification, which just tracks the size of the list instead of calling size()
class LRUCache {
public:
LRUCache(int capacity):cacheSize(capacity) {
listSize = 0;
}
int get(int key) {
auto it = mapping.find(key);
if(it == mapping.end())
return -1;
itemList.splice(itemList.begin(),itemList,it->second);
//mapping[key] == it->second still holds
return it->second->second;
}
void set(int key, int value) {
auto it = mapping.find(key);
if(it != mapping.end()) {
itemList.splice(itemList.begin(),itemList,it->second);
it->second->second = value;
} else {
itemList.push_front(make_pair(key,value));
++ listSize;
mapping.insert(make_pair(key,itemList.begin()));
}
if(listSize > cacheSize) {
mapping.erase(itemList.back().first);
-- listSize;
itemList.pop_back();
}
}
private:
int cacheSize;
int listSize;
list<pair<int,int> > itemList;
unordered_map<int,list<pair<int,int> >::iterator> mapping;
};