I am trying to insert pointers into a list but every time I try to print the list, or check whats in the list it says its empty. This means that my insertion is incorrect, but I don't understand why, my following classes are:
namespace {
template <typename T>
pair < node<T>*, bool> addElement (const T& elem, btree<T>* bt) {
class list < node<T>* >::iterator itr = bt->level().begin();
if (bt->level().empty()) {
node <T>*n = new node<T>(elem, bt->max());
cout << n->getItem() << endl;
bt->addElem(itr, n);
return make_pair(n, true);
}
for (; itr != bt->level().end(); ++itr) {
if (elem < (*itr)->getItem()) {
node <T>* n = new node<T> (elem, bt->max());
(*itr)->previous()->addNext(n);
n->addPrev((*itr)->previous());
n->addNext(*itr);
(*itr)->addPrev(n);
bt->addElem(itr, n);
return make_pair(n, true);
} else if (elem == (*itr)->getItem()) return make_pair(*itr, false);
}
// other stuff + return statement
}
addElem does the following:
void addElem (std::_List_iterator<node<T>*>& itr, node <T>* n) {
neighbours.insert(itr, n);
if (neighbours.empty()) cout << "wa?";
}
where btree class consists of:
size_t maxNodeElems;
list < node<T>*> neighbours;
the other things like addPrev() and previous() are just getters and setters. Anyways, I ran a test file on it that pretty much constructs a btree, and calls an insert function which directly calls this addElement function. But whenever I try to print the list inside the btree, it says its empty and seg faults. I don't understand why it's not storing.
Any help would be appreciated!
NOTE: the "Wa?" keeps printing
It seems you are trying to keep a sorted list, that's why you have:
// find the place to insert elem
for (; itr != bt->level().end(); ++itr) {
if (elem < (*itr)->getItem()) {
// insert...
}
}
But if the list is empty (initial state), itr will be probably equal bt->level().end() in the first place, so you will never insert anything...
You also should consider the case when you are trying to insert element for which (elem < (*itr)->getItem()) is always false (the new greatest element in the list). You need to handle that case as well.
Related
I have a list, I am tring to find a list item by value in one method, in another method to modify this item.
Here is the simplified code:
using namespace std;
//find iterator by value
list<int>::iterator get_iterator(list<int> li,int value) {
list<int>::iterator it;
for(it = li.begin();it != li.end();it++) {
if(*it == value) {
return it;
}
}
return li.end();
}
int main() {
list<int> num_list;
for(int i = 0;i < 20;i++) {
num_list.push_back(i);
}
// print the list
for(list<int>::iterator it = num_list.begin();it != num_list.end();it++) {
cout << *it << endl;
}
list<int>::iterator target_it = get_iterator(num_list,10);
// cout << *target_it <<endl;
*target_it = 100; // try to modify the original list(num_list) by iterator.
// print the list again
for(list<int>::iterator it = num_list.begin();it != num_list.end();it++) {
cout << *it << endl;
}
}
The original list has not been modified, is there any chance to make it?
Thanks in advance
list<int>::iterator get_iterator(list<int> li,int value) gets the list by value, so it is a copy of the original list.
After returning the iterator, the copy list gets destroyed and so the iterator is invalid. Using it invokes undefined behaviour.
You want to pass the list by reference
list<int>::iterator get_iterator(list<int> &li,int value)
//^^
Your idea is actually right. However your issue comes from the function get_iterator. In your current implementation, you're taking the parameter li by value, which internally create a copy. Thus the iterator you get belong to a copy of the list in the function main, and not from that list.
You can fix your issue by taking the list by reference by replacing the type of parameter li from list<int> li to list<int> &li.
You can either make it a global variable or pass it to the function by reference (prefixing the variable name with &)
struct BTreeNode {
bool is_leaf=true;
std::vector<int> elements;
std::vector<BTreeNode*> children;
BTreeNode() {}
BTreeNode (std::vector<int> v) {
this->elements = v;
}
};
void traverse(BTreeNode* root) {
for(int i = 0; i < (int)root->children.size(); ++i){
traverse(root->children[i]);
cout << root->elements[i] << endl;
}
traverse(root->children[root->children.size() -1]);
}
My method somehow segfaults. How do we write a correct inOrder Traversal for B-Tree?
It's probably the last traverse call when you are at a leaf. I don't think that this traverse is needed.
Assuming BTreeNode is a generic definition of your b-tree node, whereas T1 is the type of the keys and T2 is the type of the values in the tree, and sortedKeys is the list you are after, you can use the following recursive method. The idea is very similar to an inOrder traversal in binary search tree, first visit the left-most child, then visit the key- then continue, Since the number of children in B-tree is always one bigger than the number of keys, a check is needed before visiting the key [code is in c#, but can easily be converted to any other language, the purpose is to show the algorithm only].
public void InOrderTraversal(BTreeNode<T1, T2> node, List<KeyValuePair<T1, T2>> sortedKeys)
{
if (node != null)
{
for (int i = 0; i < node.Children.Count; i++)
{
InOrderTraversal(node.Children[i], sortedKeys);
if (i < node.KeyValues.Count)
sortedKeys.Add(node.KeyValues[i]);
}
}
}
When I run the following I miss the last data in the list and get the previous data. When I add a counter and try to subtract by 1 I crash. Any help on this would be much appreciated.
template <typename T>
Iterator<T> Iterator<T>::operator--()
{
ptr = ptr->backward;
return *this;
}
template <typename T>
Iterator<T> DoublyLinkedList<T>::end() const
{
Iterator<T> iObj;
iObj.ptr = this->last;
iObj.capacity = this->count;
return iObj;
}
int main() {
DoublyLinkedList<int> *d = new DoublyLinkedList<int>;
for (int i = 2; i <= 20; i += 2) {
d->insertLast(i);
}
//Get an Iterator which points at the end of the list
Iterator<int> iter = d->end();
--iter;
//Test that it does point to the first
checkTest("testIteratorsDecrement #1", 20, *iter);
//Test that our Iterator can move forward;
--iter;
checkTest("testIteratorsDecrement #2", 18, *iter);
//move it some more
for (int i = 0; i < 7; i++) {
--iter;
}
checkTest("testIteratorsDecrement #3", 4, *iter);
--iter;
checkTest("testIteratorsDecrement #4", 2, *iter);
delete d;
return 0;
}
I try to fix it by doing the following but it crashes. count is a protected int.
template <typename T>
Iterator<T> DoublyLinkedList<T>::end() const
{
Iterator<T> iObj;
iObj.ptr = this->last + (count -1);
iObj.capacity = this->count;
return iObj;
}
Usually end() would return a sentinel value which can not be dereferenced. It looks like you are returning a pointer to the last entry, that is the source of your off-by-one error.
As the implementor you can choose your sentinel value but it should not be a valid entry in the list.
As an aside: I don't see a good reason for having a capacity member in an iterator. How is it kept up-to-date?
Unlike arrays or vector, list does not store its elements one-by-one in memory.
Your first element may be at some begin address, but the next element can be begin+5, or begin-10 or something else.
Basically, this means you can't do arithmetic operations with pointers to elements of your list.
If you want some kind end element, I'd suggest making the last element of your list to point to NULLPTR
I'm working on a project which requires that I create a template class for an Accumulator which returns weather or not the list passed to it is in order. The order is ascending.
I am probably overthinking the problem but I cannot seem to figure out how to do a greater than/ less than check on both primitive data types and strings. I'll clarify:
The process goes like this:
A list/vector is declared and stuff is stored in it.
Then a secondary Accumulator called apply is called.
This Accumulator(apply) iterates through the list calling the .put() method of the InOrder Accumulator on every value in the list. The values could be of type double, long, short, etc or string.
I have tried setting an arbitrary lower bound, setting it equal to the first element in the list and then doing checks based on that start point but this offers mixed results because it does not work on the strings.
I was thinking of checking typeid or something so that for strings I could then call the .size() method and compare that way. Were as for primitives I would simply use the > or < operator. But that would defeat the point of the template function. Any help would be greatly appreciated.
I'll post the Code were the function is called, the code for the Apply Accumulator, and my Code for InOrder. Let me know if anything else is required.
My InOrder:
template<typename T>
class InOrder
{
public:
InOrder(){}
~InOrder(){}
void put(T item)
{
_count++;
if(_count == 1)
{
_lowbound = item;
}
if(_count!=0 && _count!=1 && item<_lowbound)
{
_order = false;
}
if(_count!=0 && _count!=1 && item>_lowbound)
{
_order = true;
}
_count++;
}
bool get()
{
return _order;
}
private:
T _lowbound;
int _count = 0;
bool _order;
};
Apply Accumulator:
template<typename A, typename I>
void apply(A & anAccumulator, I begin, I end)
{
for (I iter = begin; iter != end; ++iter)
{
anAccumulator.put( *iter);
}
}
Code where InOrder is Called:
{
// Read a list of doubles into a List and check their order
cout << "apply InOrder to a List of doubles\n";
double sentinel = -1.23;
List<double> dList;
fillList(sentinel, dList);
InOrder<double> dblInOrder;
apply(dblInOrder, begin(dList), end(dList));
cout << "The doubles in dList are ";
if (!dblInOrder.get())
cout << "NOT ";
cout << "in order\n\n";
}
{
// Read a list of strings into a List and check their order
cout << "apply InOrder to a List of strings\n";
string strSent = "end";
List<string> sList;
fillList(strSent, sList);
InOrder<string> strInOrder;
apply(strInOrder, begin(sList), end(sList));
cout << "The strings in sList are ";
if (!strInOrder.get())
cout << "NOT ";
cout << "in order\n\n";
}
I should note that the the items put into the list are processed in the reverse order.
Eg: if I type my list in as [a,b,c] or [1,2,3] the first value/string to be processed will be c/3 and then so on for there to b/2 and a,1
Your mistake is that you do not stop when you detect that order is wrong:
_order = false; // here is you have to stop and skip all other items
This won't work:
if(_count!=0 && _count!=1 && item<_lowbound)
{
_order = false;
}
if(_count!=0 && _count!=1 && item>_lowbound)
{
_order = true;
}
because should be:
if(_count!=0 && _count!=1 && item<_lowbound)
{
_order = false;
}
Delete the second part, and add:
InOrder() : _order(true) {}
To your constructor.
I am new in programming c++, so please don't be angry with me if my source code is not exactly brilliant.
I have to write a programm to handle with nodes and edges in a graph for my studies.
I have 2 std::lists in my source code. The first one is to store general Nodes and the other one for saving the kind class of my nodes called ArticleNodes. In general, all elements are pointers to the created objects.
To figure out whether one object is the same in the other list I save the memory address and compare it to the elements on the second list. If there is a match the second element will be deleted.
Now I'd like to delete one element in both lists:
void Graph::deleteNode(unsigned int nodeNumber)
{
list<Node*>::iterator it = m_nodes.begin();
ArticleNode* pCurrentArticleNode;
for(unsigned int i=1; i<nodeNumber; i++) { it++; }
Node* pCurrentNode = (*it);
for (list<ArticleNode*>::iterator itArticle = m_articlenode.begin(); itArticle != m_articlenode.end(); itArticle++)
{
pCurrentArticleNode = (*itArticle);
if(pCurrentNode==pCurrentArticleNode) { m_articlenode.remove(pCurrentArticleNode); }
}
m_nodes.remove(pCurrentNode);
delete pCurrentNode;
delete pCurrentArticleNode;
}
I can compile this, but when I call the function, my programm just exits with return 1.
Actually, I figured out that the remove-command in the if-clause is the problem. Why does that not work??
You should use algorithms more than doing everything manually:
void Graph::deleteNode(unsigned int nodeNumber)
{
assert (nodeNumber < m_nodes.size());
auto it = std::next( m_nodes.begin(), nodeNumber - 1 );
auto itArticle = std::find( m_articlenode.begin(), m_articlenode.end(), *it );
if( itArticle != m_articlenode.end() )
m_articlenode.erase( itArticle );
delete *it;
m_nodes.erase(it);
}
Btw your code deletes the same object twice.
When you remove an element from std::list object with remove( ) method, all iterators pointing to that elements become invalid. In your case, after you remove an element from the list m_articlenode, the iterator object itArticle becomes invalid. And when you increment that iterator, you get an undefined behavior.
Pay attention that the method remove( ) deletes all of the items in the list with given value. So you don't need the for-loop at all. Here is the fixed version of your function:
void Graph::deleteNode(unsigned int nodeNumber)
{
list<Node*>::iterator it = m_nodes.begin();
for(unsigned int i=1; i<nodeNumber; i++) { it++; }
Node* pCurrentNode = (*it);
m_articlenode.remove(pCurrentNode);
m_nodes.remove(pCurrentNode);
delete pCurrentNode;
}
You have to use the erase method of std::list in order to remove an element from your list while iterating over it.
This should do the trick:
void Graph::deleteNode(unsigned int nodeNumber)
{
list<Node*>::iterator it = m_nodes.begin();
ArticleNode* pCurrentArticleNode;
for(unsigned int i=1; i<nodeNumber; i++) { it++; }
Node* pCurrentNode = (*it);
list<ArticleNode*>::iterator itArticle = m_articlenode.begin();
while(itArticle != m_articlenode.end()) {
pCurrentArticleNode = (*itArticle);
if(pCurrentNode==pCurrentArticleNode) {
m_articlenode.erase(itArticle++);
} else {
itArticle++;
}
}
m_nodes.remove(pCurrentNode);
delete pCurrentNode;
delete pCurrentArticleNode;
}
Simple issue in your code is that if you go into the if condition is met and it's body executed, you should come out of the loop. std::remove invalidates the iterator ann you will get issues in next iteration so do:
for (list<ArticleNode*>::iterator itArticle = m_articlenode.begin(); itArticle != m_articlenode.end(); itArticle++)
{
pCurrentArticleNode = (*itArticle);
if(pCurrentNode==pCurrentArticleNode)
{
m_articlenode.remove(pCurrentArticleNode);
break;
}
}
In general, there are other issues in the code. As a first step I would suggest using shared_ptr for managing your Nodes and have list of shared_ptr instead of list of pointers.
I am new in programming c++, so please don't be angry with me if my
source code is not exactly brilliant.
We all start somewhere.
Now I'd like to delete one element in both lists:
OK. Quick question. Why are you deleting article node if it is already deleted (via base Node)? I'm assuming for now node is not duplicated in the list:
My solution below... I've passed the lists as arguments. See comments:
#include <list>
#include <algorithm>
struct Node
{
virtual ~Node(){} //For dyna cast to work...
};
struct ArticleNode : Node
{
};
void deleteNode(std::list<ArticleNode*>& articleList, std::list<Node*>& m_nodes, unsigned int nodeNumber)
{
using namespace std;
if (m_nodes.size() > nodeNumber)
{
auto it = m_nodes.begin();
// Advance advances our iterator by N. No need for your for loop - less risk...
std::advance(it,nodeNumber);
Node* currentNode = *it;
//Casting is bad here, but hey, lets assume if type is wrong, we only erase
// it from Node...(Your call)?
ArticleNode* currentArticleNode = dynamic_cast<ArticleNode*>(currentNode);
if (currentArticleNode)
{
//Use find here.... KISS
auto foundPos = std::find(articleList.begin(), articleList.end(), currentArticleNode);
if (foundPos != articleList.end())
{
//No need to delete currentArticleNode, as we're deleting it already...
articleList.erase(foundPos);
}
//Assuming only one item for now...
}
//Else our node was obviously not the right type, and cannot exist in articleNodes...
m_nodes.erase(it);
delete currentNode;
}
else
{
std::cout << "No such node: " << nodeNumber << std::endl;
}
}