I'm trying to make a remove function (without the use of recursion), that passes in the value of the node I want to remove.
My current problem is that my destructor seems to be making the program crash (getting a runtime error), which is probably because I'm not deleting the root node properly in my remove function.
The code I'm trying to use to remove the root is here:
bool BST::remove_root (int val)
{
if (val == root_->val)
{
if (root_->left == NULL && root_->right != NULL)
{
Node* temp = root_->right;
delete root_;
root_ = NULL;
size_--;
root_ = temp;
return true;
}
else if (root_->right == NULL && root_->left != NULL)
{
Node* temp = root_->left;
delete root_;
root_ = NULL;
size_--;
root_ = temp;
return true;
}
else
{
Node *curr = root_->right, *child = root_->left;
delete root_;
root_ = NULL;
size_--;
root_ = curr;
Node* temp = curr;
while (temp->left != NULL)
temp = temp->left;
temp->left = child;
return true;
}
}
}
In my code, just using val by itself is the value being passed in to the remove function (the one I want to remove). When I dereference things like root_->val, I'm accessing the val in my BST class.
I don't really understand why this wouldn't be able to continuously delete the root node (like I said I think it's the destructor making the program crash), but I feel like it could be invalid pointers?
Related
I need to implement a doubly linked list in c++ for a small animation running on console. The linkedlist stores clouds and then they move through the console and as each cloud hits the end of screen, it needs to be deleted from linked list. As the cloud hits the end, it has a variable called alive which is set to false so it can be deleted.
I can't upload the full game code, but I have recreated the problem in dummy code by creating sample clouds where some of them have alive = true and alive = false. I have also updated the previous and next nodes of the cloud to be deleted but I still get an error:
Exception thrown: read access violation. temp was 0xFFFFFFFFFFFFFFFF.
Code below (include statements removed for simplicity)
Test.cpp
int main() {
Cloud* a = new Cloud('a');
a->alive = false;
Node* a1 = new Node(a);
Cloud* b = new Cloud('b');
b->alive = false;
Node* b1 = new Node(b);
LinkedList list;
list.Insert(a);
list.Insert(b);
Node* temp = list.head;
while (temp != nullptr) {
if (temp->data->alive == false) list.Delete(temp); // throws exception after deleting a single node.
temp = temp->next;
}
return 0;
}
LinkedList.cpp delete function
void LinkedList::Delete(Node* del) {
if (del == head) {
OutputDebugStringA("Cloud in head");
Node* temp = head;
head = head->next;
head->prev = nullptr;
delete temp;
return;
}
else {
Node* temp = head;
while (temp != tail->next) {
if (temp == del) {
if (temp->next != nullptr) {
OutputDebugStringA("Cloud in mid");
temp->prev->next = temp->next;
temp->next->prev = temp->prev;
break;
}
else {
OutputDebugStringA("cloud at tail");
tail = temp->prev;
tail->next = nullptr;
break;
}
}
temp = temp->next;
}
delete temp;
temp = nullptr;
}
}
Node.cpp
#include "Node.h"
#include <iostream>
using namespace std;
Node::Node() {
this->data = nullptr;
}
Node::Node(Cloud* data) {
this->data = data;
}
Someone please point out where am I going wrong. Thanks
if (temp->data->alive == false) list.Delete(temp); // throws exception after deleting a single node.
temp = temp->next;
Here, temp gets passed into the Delete() method. Afterwards temp gets set to temp->next.
In Delete():
delete temp;
temp = nullptr;
The object referenced by the passed-in temp pointer (here, this temp, by the virtue of the preceding logic, is the same pointer that gets passed in) gets deleted.
After returning, temp->next references a deleted object.
This is at least one confirmed instance of undefined behavior in the shown code. This may or may not be the only bug.
As it's been pointed out to you in comments, this overall Delete() logic is fundamentally flawed. It should not involve any kind of iteration, for a doubly-linked list. You will end up fixing this bug while rewriting Delete() from scratch (which includes rethinking how Delete() itself gets called, because after it returns temp is no longer usable for anything).
As #John Zwinck and #Sam Varshavchik pointed out that the implementation of delete method was flawed and temp became useless after the Delete function returned.
I fixed it by using another temp pointer and fixing the delete method to be O(1).
Delete Method
void LinkedList::Delete(Node* del) {
if (del == head) {
head = head->next;
head->prev = nullptr;
}
else if (del == tail) {
tail = del->prev;
tail->next = nullptr;
}
else {
del->prev->next = del->next;
del->next->prev = del->prev;
}
delete del;
}
Node deletion
Node* temp = cloud_list.head;
Node* next;
while (temp != nullptr) {
next = temp->next;
if (temp->data->alive == false) {
cloud_list.Delete(temp);
}
temp = next;
}
The deletion now works fine.
I am trying to delete a node from a linked list using this function:
void del_node(int del_data)
{
node* temp = NULL;
node* trail = NULL;
node* del_ptr = NULL;
temp = head;
trail = head;
while (temp != NULL && temp->data != del_data)
{
trail = temp;
temp = temp->next;
}
if (temp != NULL) {
del_ptr = temp;
temp = temp->next;
trail->next = temp;
delete(del_ptr);
}
}
It seems like it deletes it fine until i print the linked list using this:
void print()
{
node* temp = NULL;
temp = head;
while (temp != NULL)
{
cout << temp->data << " ";
temp = temp->next;
}
cout << endl;
}
and it starts outputting seemingly random numbers, can anybody help me with this, really confused as this code comes from a tutorial.
Your algorithm doesn't manage the head pointer correctly whatsoever. Any changes that ultimately should modify the head pointer don't, and that's a huge problem. A pointer to pointer algorithm not only solves this problem, it also delivers a considerably more succinct solution:
void del_node(int del_data)
{
struct node **pp = &head;
while (*pp && (*pp)->data != del_data)
pp = &(*pp)->next;
if (*pp)
{
node *tmp = *pp;
*pp = tmp->next;
delete tmp;
}
}
This will work for any list condition including:
An empty list. i.e. head is null.
A single-node list. If the value matches head->data it will properly delete and reset the node pointer.
A multi-node list. The first matching node will be removed, and it will properly fix up the head node pointer if that was the matching location.
All of the above, in cases where there is no matching node, the list remains unchanged.
Fulfilling all of that in such a short algorithm + implementation is beneficial.
I'll comment on your code inline:
void del_node(int del_data)
{
node* temp = NULL;
node* trail = NULL;
node* del_ptr = NULL;
temp = head;
trail = head;
// This is fine, but recommend you use nullptr instead of NULL.
// This will find the first instance of data matches del_data,
// But if you are trying to delete all instances of del_data,
// You'll need to do this a little differently.
while (temp != NULL && temp->data != del_data)
{
trail = temp;
temp = temp->next;
}
// This if is fine, but see previous comment about using nullptr
// instead of NULL.
if (temp != NULL) {
del_ptr = temp;
temp = temp->next;
// Problematic: What if trail is null?
trail->next = temp;
delete(del_ptr);
}
}
Your code isn't bad. I wouldn't have written exactly like this, but I'm going to replace your if-statement:
if (temp != nullptr) {
// If trail is nullptr, then we're deleting from the head
if (trail == nullptr) {
head = temp->next;
}
else {
trail->next = temp->next;
}
delete(temp);
}
There's no need for the temporary. Just point around temp as you see in the if-else block and then delete temp.
I'm having difficulty deleting a node in my binary search tree. The delete function is part of my Node class, and my findMin function is as well. Below is my delete function...
/**********************************************
* Delete
**********************************************/
node* node::Delete(node *root, string stuff)
{
//node *temp;
if (root == NULL) // Searches for value in tree
return NULL;
if (stuff < root->val) // String is in left subtree
root->left = Delete(root->left, stuff);
else if (stuff > root->val) // String is in right subtree
root->right = Delete(root->right, stuff);
else
{ // No children
if ((root->left == NULL) && (root->right == NULL))
{
delete(root);
root = NULL;
}
else if ((root->right == NULL) && (root->left != NULL)) // One left child node
{
node *temp = root;
root = root->left;
delete temp;
temp = NULL;
}
else if ((root->left == NULL) && (root->right!= NULL)) // One right child node
{
node *temp = root;
root = root->right;
delete temp;
temp = NULL;
}
else // Two children
{
node *temp = findMin(root->right); // Finds smallest value in right subtree
root->val = temp->val;
root->right = Delete(root->right, temp->val);
}
}
return root;
}
Below is my Destructor, which is giving me a SIGABRT (I'm using Xcode)
/**********************************************
* Destructor
**********************************************/
node::~node()
{
if (left != NULL) delete left;
if (right != NULL) delete right;
}
What my code is actually doing is not only deleting the node I intend to delete, but its child node. What could I be doing wrong? Is it an error with memory allocation? Is it an error with how I set the value to the child node?
You need to null your pointers to left and right before deleting a node.
You call:
node *temp = root;
root = root->left;
delete temp;
temp = NULL;
When you "delete temp" you are deleting a node which still points to root->left and root->right and your destructor insures they are also removed. You should instead do something like this:
node *temp = root;
root = root->left;
temp->left = NULL;
temp->right = NULL;
delete temp;
temp = NULL;
Also in your destructor you don't need to check if they are equal to null since delete already preforms this check.
Please help. What is wrong with my remove function below. Cant seem to figure it out
I seem to be getting an error:
The class declaration seems pretty fine. The main issue is to have this remove function to work
void binaryTree::Remove(int) {
if (node != NULL)
{
Node* tmptr = node;
int rootdata = node->data;
/int rSubtree;
}
{
// Case 0- no child
if (node->lChild == NULL && node->rChild == NULL)
{
node = NULL;
//parent- //set the parent of the node to NULL
delete node;
}
// has one child
else if (node->lChild == NULL && node->rChild != NULL)
{
node = node->rChild;
node->rChild = NULL;
delete node;
}
else if (node->lChild != NULL && node->rChild == NULL)
{
node = node->lChild;`enter code here`
node->lChild = NULL;
delete node;
}
}
Try swapping node = NULL with delete node; Otherwise you're trying to delete NULL.
Hello My problem with this code is on my 2nd else loop; i never enter it and therefore i never make new nodes for my list. can anyone help me see what i am missing?
bool List::Insert(int data)
{
Node* P = new Node;
if(P==NULL)
{
return false;
}
else
{
P ->info = data;
P ->next = NULL;
if(Head == NULL)
{
Head = P;
}
else
{
Node* lastNode;
for(lastNode = Head; lastNode ->next != NULL; lastNode = lastNode ->next)
{
lastNode ->next = P;
}
}
return true;
}
}
This:
Node* lastNode;
for(lastNode = Head; lastNode ->next != NULL; lastNode = lastNode ->next)
{
lastNode ->next = P;
}
is dead wrong. It will change the next pointer, for every single node currently in the list, to point to your new node. You need to only change the pointer in the last node:
Node* lastNode = Head;
while (lastNode->next != NULL)
lastNode = lastNode->next;
lastNode->next = P;
You may also, for efficiency, want to maintain a separate Tail pointer (in addition to your Head) so that you can simply replace that whole operation with:
Tail->next = P;
Tail = P;
That way, you won't have to traverse the entire list every time you want to append a new node. Your code then becomes something like (without the traversal, and with updating the tail pointer as well):
// Prepare new node.
Node *P = new Node;
P->info = data;
P->next = NULL;
// If list empty, set head and tail to new node, otherwise
// append it.
if (Head == NULL) {
Head = P;
Tail = P;
} else {
Tail->next = P;
Tail = P;
}
I'll stop short of criticising the fact that your Insert method doesn't actually insert but rather appends. My near-anal-retentive nitpicking nature is unlikely to endear me to you:-)
lastNode ->next = P; // this must go after the for