I am writing a "insertBefore" function. I am checking to see if it is firstly the start of the function (This works). I then am trying to have my previous iterator point to the desired location by going through the entire list until its next is equal to the iterator passed into the function (i.e the place I want to insert in front of).
Now previous is where I want it I am trying to make its next point to the new node (the one I passed into the function) and the new node point to the old iterator after it.
Ive tried swaping the iterators instead.
template <typename T, typename InputIterator>
void SLinkedList<T, InputIterator>::insertBefore(InputIterator & t_position, T t_element)
{
//Q1
SListNode<T>* temp = t_position.get();
SListNode<T>* previous = m_head.get();
std::unique_ptr<SListNode<T>> newNode = std::make_unique<SListNode<T>>(t_element, this);
//If position is the head node
if (t_position.get() == m_head.get())
{
insertFirst(t_element);
}
else
{
while (previous != temp)
{
previous = previous->next().get();
}
if (previous == temp)
{
previous->setNext(newNode);
newNode->setNext(temp->next());
}
}
This should insert the new number I passed to insert itself into the list before the iterated position.
I suspect
previous->setNext(newNode);
is causing the issue. unique_ptr<> is moveable but not copyable. So, assuming your function setNext() did move the newNode, calling newNode->setNext() is ill formed.
And if you didn't move it (e,g - const unique_ptr<> &), then the address is being deleted once the unique_ptr goes out of scope (accessing invalid address).
You use a local variable
std::unique_ptr<SListNode<T>> newNode;
as soon as you go out of scope of void insertBefore (end of the function), this newNode will get deleted. As such, the whole list will become in an inconsistent state, and if setNext moved the value, then newNode is invalid after the first line:
previous->setNext(newNode);
newNode->setNext(temp->next());
Related
I've pinpointed my issue to this specific function, it's the helper function for my binary tree. Before this function call there is a node but instead of growing it seemingly just replaces that node. When I look at my code in my head it all makes sense but I can't figure out what I'm doing wrong.
Here is the function that calls add:
void BSTree::Insert(Client &newClient) {
if (isEmpty())
{
Node *newNode = new Node(newClient);
this->root = newNode;
}
else
add(this->root, newClient);
}
and here is my add() function:
BSTree::Node* BSTree::add(Node *node, Client &newClient) // helper function for Insert()
{
if (node == nullptr)
{
Node *newNode = new Node(newClient);
//node = newNode; // already tried adding this in
return newNode;
}
if (newClient.clientID < node->pClient->clientID)
return node->left = add(node->left, newClient); // already tried just returning add()
else
return node->right = add(node->right, newClient);
}
Since this is your question, I will explain what your code is doing. Imagine you have a mature binary tree already and you are adding a node to your tree. By the time you reach this line
return node->left = add(node->left, newClient);
Three separate instructions are carried out:
newClient is added to the left branch of node by add().
the left child of node is set to the return value of add().
the right hand side (RHS) of the assignment is returned by the parent function.
The issue is with number 2. If the tree you are adding to is mature already, changing left child of nodes as you're traversing the tree will cause the override effect that you're observing. In fact, the problem goes beyond overwriting leaves. Since you use the new keyword, the overwritten nodes still have allocated heap space, are never deleted and cause a memory leak.
Here are some thoughts to get you on the right direction:
Your insert() function ensures that the first time you call add(), you are not passing nullptr as the first argument. Take advantage of that and ensure nullptr is never passed into add() function by checking for nullptr before you do the recursive call. Change the return type of add() to void. You no longer need to check node is nullptr. Here's some pseudocode to guide you
void add(node, val)
if val < node.val
if node.left exists
add(node.left, val)
else
make a new object and set node.left to that object
else
if node.right exists
add(node.right, val)
else
make a new object and set node.right to that object
There is a problem with your logic. First of all, there is the insert() method which you should write like this for better understanding:
void BSTree::Insert(const Client &newClient) // use const to prevent modification
{
if (isEmpty()) { root = new Node(newClient); }
else { add(this->root, newClient); }
}
This way you are creating a new object at root directly with the help of 'root' pointer in BSTree.
Now, about the add() method. The 'node' you are passing as a parameter is a copy of the pointer variable, so the actual pointer value is not changed. See this:
BSTree::Node* BSTree::add(Node *node, Client &newClient) //logical error
You need to pass the Node* by reference like this using 'Node* &node':
BSTree::Node* BSTree::add(Node* &node, const Client &newClient)
Why is you binary tree overwriting the roots of its leaves? Answer:
Your recursive call with return statement is totally wrong.
return node->left = add(node->left, newClient);
The add(node->left, newClient) always returns the address of the leaves, and you are returning this value. It goes for recursive calls until it reaches the leaves place.
Conclusion: Since, there are a lot of bugs, I would suggest you re-write logic again carefully.
I hope this helps! :-)
I am trying to add some elements to the front of an queue that already has other items.My code works fine but it adds the item in front everytime. How can I modify it to add the next item after the front added one. This is what i got:
void Queue::addtoFront(string first, string last){
Node *temp = new Node(first, last, NULL);
temp->next = head;
head = temp;
}
Note: I'm not sure what you are trying to achive, but you could write a 'insertAfter' member function. This function takes a new element and inserts it immediately after a given element in the linked list.
To do so you must first set up the 'next' link of the new element, then insert the new element immediately after the requested element.
void Queue::insert(Node *newElement, Node *insertAfter)
{
_ASSERT(insertAfter != nullptr);
_ASSERT(newElement != nullptr);
// set the 'next' of the new element to the 'next' of the insertAfter
newElement->next = insertAfter->next;
// now insert the new element immediately after the 'insertAfter'
insertAfter->next = newElement;
}
// Taken from the original post...
// Of course, "addToFront" is no longer the correct name for this function,
// since it does no longer add the element to the front
void Queue::addToFront(...)
{
if (head==nullptr) {
// see original post above
} else {
// insert new element at the 2nd position in the queue,
// immediately behind the head
insert(temp, head);
}
}
For further study you could read about linked lists and have a look at the std::list interface. And you might want to improve your code with shared_ptr/unique_ptr instead of using Node*. In modern C++ you would almost never use
Node *element=new Node();
This is just bad code most of the time. Managing pointers and object lifetime by yourself is an
invitation to all sorts of nasty problems (memory leaks, access violations,
shallow vs. deep copy problems, failure to free objects esp. after exceptions etc...). Using managed or smart pointers make your life a lot easier:
shared_ptr<Node> element(new Node());
// or even better
shared_ptr<Node> element=std::make_shared<Node>();
Note: "managed/smart pointers" have nothing to do with C#/C++ managed code. The name simply says that there is a class (shared_ptr) that does some kind of automatic management and takes some of the burdon of you.
I am trying to insert nodes in a list based on the value of a data member. Basically, if the member isVip evaluates to true, that node gets precedence, and should be inserted ahead of any regular node (but behind any existing VIP nodes). Regular nodes simply get added at the end of the list.
I'm pretty sure I have a good idea of how to use two pointers to step through the list and insert elements for n > 2 where n is the number of current list members, but I'm sort of conceptually stuck for the case when there's only one node.
Here is my working version of code below:
void SelfStorageList::rentLocker(Locker e) {
int count = 0;
LockerNode *p = head;
if (isEmpty()) {
head = new LockerNode(e);
tail = head;
}
for(;p!=0;count++, p=p->next) {
if(count == 1) {
if (e.isVip) {
if(p->objLocker.isVip) {
LockerNode*p = new LockerNode(e, p->next);
}
}
}
}
As you can see, I'm checking to see if the passed in object is VIP, and then whether the current one is. Here, I've hit some trouble. Assuming both are VIP, will this line:
LockerNode*p = new LockerNode(e, p->next);
put the passed in locker object in the correct place (i.e. after the current VIP one). If so, would:
LockerNode*p = new LockerNode(e, p);
equivalently place it before? Is the use or absence of the 'next' member of the node what defines the placement location, or is it something entirely different?
Hope someone can clear my doubts, and sorry if it seem a foolish question! Thanks!
Simply iterate over the list while the next node have isVip set (current->next->isVip). After the iteration, the last node visited will be the last with isVip set, and you should insert the new node after that one.
It can be implemented in fewer lines, without the explicit isEmpty check, and without any counter. Even less than that if you use a standard container instead.
!=I am currently working on the following erase recursive bool function that thakes list and int as arguments and return true if the int was found and deleted and false if it was not found in the list. It seems to work, but the problem is that it deletes the next int number in the list, and not the current:
typedef struct E_Type * List;
struct E_Type
{
int data;
List next = 0;
};
bool erase(const List & l, int data){
List current = l;
if (current == 0)
{
return false;
}
else if (current->data == data)
{
List deleteNode = new E_Type;
deleteNode = current->next;//probably this causes the error, but how can I point it to the current without crashing the program
current->next = deleteNode->next;
delete deleteNode;
return true;
}
else if (current->data != data)
{
return erase(current->next, data);
}
}
There are two basic type of lists:
single-linked lists (each node knows its next node) and
double-linked lists (each node knows its next as well as its previous node).
If, like in your case, one has a single-linked list, you must not check the CURRENT node for equality to 'data', because at that point it is too late to change the next pointer of the last node. So you always have to check the NEXT pointer for equality, like this:
bool erase(const List & l, int data)
{
List current = l;
if (current == 0)
return false;
// special case: node to be deleted is the first one
if (current->data == data)
{
delete current;
return true;
}
if (current->next && current->next->data == data) // next exists and must be erased
{
List deleteNode = current->next; // Step 1: save ptr to next
current->next = deleteNode->next; // Step 2: reassign current->next ptr
delete deleteNode; // Step 3: delete the node
return true;
}
return erase(current->next, data);
}
Note: I spared your last 'else if' condition. The 'else' because the previous if had a return in it, and the 'if' since its condition was just the negation of the previous 'if', which - if the program comes this far - would always hold.
Regards
The only node you're considering is the current one, so you must have a provision for modifying l:
if (current->data == data)
{
l = current->next;
delete current;
return true;
}
Here are some pointers.
An iterative approach
When you're iterating over your list, maintaining a pointer to the current element is not enough. You also need to maintain a pointer to the previous element, since you will need to fix up previous->next if you delete the current element.
On top of that, deleting the first element of the list will require special handling.
A recursive approach
Write a recursive function that will take a pointer to the head of the list, find & delete the required element, and return a pointer to the new head of the list. To do this, you need to:
Define and implement the base case. Handling one-element lists seems like a natural candidate.
Define the recursion. There are two cases: either the head of the list is the element you're looking for, or it isn't. Figure out what you need to do in both cases, and take it from there.
If you have a list:
A --> B --> C --> D
And you want to delete C, you have to:
Store C in a temp variable
Change B->next=C->next
delete C.
So you need to find B to be able to modify it.
You should certainly not create any new instance of E_type.
Your condition
else if (current->data == data)
will stop on the node which has the value data. You then go on to delete the node after this node in your code.
If you want to keep the rest of the code same, then that line should be :
else if ((current->next)->data == data)
with an extra check, in case the first element is the only element in the list.
A simpler way would be to keep a pointer that points to the element before the current element, and then deleting the node which is referred by that pointer.
You will need to change the next pointer of the preceding entry. So everything is find, but you have to check current->next->data against data, not current->data.
Be sure to check for NULL-pointers in case current is the last entry in the list!
When you delete a node from a list, you need to point the previous node to the next one. Since you have a singly linked list, there are 2 options:
Maintain a pointer to previous node in your erase function. When encountering desired node, link previous node to current->next and delete current node. Needs special treatment for the first node in the list.
When you encounter desired node, copy the content of the current->next into current, then delete current->next. This way you don't need an extra parameter in your function. Needs special treatment for the last node in the list.
The nature of pointers being NULL in C++ seems to feel arbitrary. I'm sure there's a method to it that I'm missing, but the following makes sense to me, but doesn't seem to work. I have the following method for adding a node to a linked list:
LLNode *ll; // set to NULL in constructor.
void addToLL(Elem *e)
{
LLNode *current = ll;
while(true)
{
// edge case of an empty list.
if (ll == NULL)
{
ll = new LLNode(e);
break;
}
else if (current == NULL)
{
current = new LLNode(e);
break;
}
else {
current = current->next;
}
}
}
When adding a 2nd node to the list, the case for current == NULL does not get caught, so it tries to call current = current->next and crashes do to accessing invalid memory. Why would this be the case? A LLNode has a pointer to an Elem, and a pointer called next to another LLNode.
You probably didn't set the next pointer to NULL in the LLNode constructor.
Objects of the basic types in C++ (pointer types, numeric types, etc.) have indeterminate initial values: they don't get initialized by default. You need to explicitly initialize such objects before you use them.
For this sort of thing you need a pointer to a pointer in order to strip away a lot of the needless exceptions in your implementation:
LLNode *ll = NULL;
void addToLL(Elem *e)
{
LLNode** current = ≪
// While the current pointer to pointer is mapped to something,
// step through the linked list.
while (*current)
current = &(*current->next);
// At this point current is pointing to a NULL pointer and can
// be assigned to.
*current = new LLNode(e);
}
The reason pointers are NULL is because that evaluates to false and allows you to do simple checks such as while (*current) without a lot of overhead. In the CPU this usually ends up being implemented as a test-if-zero operation.
Pointers are only NULL if initialized as such. In C they are often undefined unless properly initialized and referencing an uninitialized pointer is recipe for disaster. You'll want to ensure any pointers you define are always initialized to something valid before using them.
1) You say that ll is set to NULL in the constructor. But what constructor? There's no class definition here. Is ll a global variable? And are you sure that the constructor for LLNode sets the next pointer to NULL?
2) The condition
if (ll == NULL)
can and should be checked outside of the loop, as ll is not modified inside the loop.
3) current is a local stack variable, so assigning to it will have no effect once the function exits. In particular, current = new LLNode(e) is a memory leak.
4) To add a node to the linked list, you must find the last node of the existing list, and modify its next pointer. Something like this would work:
// ll is a field representing the first node in your existing linked list.
if (ll == NULL) {
ll = new LLNode(e);
}
else {
current = ll;
while (current->next != NULL) {
current = current->next;
}
current->next = new LLNode(e);
}
EDIT: Modified the above based on your comment that ll is a class member.
The first thing I see in your code is that current is local, gets allocated with new but is never actually attached to the list.
Surely your code should be
else if( current->next == NULL )
{
current->next = new LLNode( e );
break;
}
LLNode must of course initialise next to NULL in its constructor.
Of course your list has O(N) insertion time, and if this is anything other than an exercise you should be almost certainly be using standard library containers.
You should also probably move the edge case out of the loop.