I'm trying to remove duplicates from linked list, but I have a bug in the code, I can't figure it out... So it seems there is a logical bug when for example found a duplicated node, the deletion is not correct?
ListNode *deleteDuplicates(ListNode *head) {
if (head == nullptr)
return head;
bool visited[255];
memset(visited, false, sizeof(visited));
ListNode * t = head;
ListNode *p = head;
while (t)
{
if (!visited[t->val])
{
visited[t->val] = true;
}
else{
ListNode *temp = t;
p->next = temp->next;
delete temp;
}
p = t;
t = t->next;
}
return head;
}
The problem is that when you find a duplicate you delete temp which actually equals to t. Then later, when you call t = t->next you're referring to a deleted pointer which is of course illegal.
A simple solution would be to change the while loop to look like:
while (t)
{
ListNode *temp = NULL; // added
if (!visited[t->val])
{
visited[t->val] = true;
}
else{
temp = t; // changed
p->next = temp->next;
// delete temp; // commented out (moved below)
}
p = t;
t = t->next;
if (temp) { // Added - delete temp folder in case it was set
delete temp;
}
}
Your code invokes undefined behavior. When you delete the object pointed by temp, it disqualifies the internal fields of the object, including the next field. Since t points to the same object as temp, t's fields also become invalid. Therefore, the assignment t = t->next will produce non-deterministic result.
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 have poorly implemented a doubly linked list and I ran into to an
issue of memory leak.
The biggest issue it that i need to delete the
node which I dynamically allocated memory to in heap.
It enter a infinite loop whenever remove function of double_linked_list class is
called.
I tried debugging but it did'nt work.
class double_node{
public:
// Data members
ll data;
double_node * next;
double_node * prev;
// Functions
double_node(){
this->next = nullptr;
this->prev = nullptr;
this->data = LLONG_MIN;
}
double_node(ll data, double_node * next, double_node * prev){
this->data = data;
this->next = next;
this->prev = prev;
}
// WARNING : Memory Leak
// I don't know why defining a destructor gives an error
~double_node(){
cerr << "~double_node() is called\n";
if(this != nullptr){
delete this;
}
}
};
class double_linked_list{
public:
double_node * head = new double_node;
double_node * tail = new double_node;
double_linked_list(){
// Head part
head->data = LLONG_MIN;
head->next = tail;
head->prev = nullptr;
// Tail part
tail->data = LLONG_MIN;
tail->next = nullptr;
tail->prev = head;
}
void append(ll data){
if(head->data == LLONG_MIN){
head->data = data;
}
else if(tail->data == LLONG_MIN){
tail->data = data;
}
else{
double_node * temp = tail;
double_node * last = new double_node;
// Setting up last
last->data = data;
temp->next = last;
last->prev = temp;
last->next = nullptr;
tail = last;
}
}
void remove(ll data){
double_node * temp = head;
while(temp != nullptr and temp->data != data){
temp = temp->next;
}
if(temp->data == data){
double_node * y = temp->prev;
double_node * x = temp;
double_node * z = temp->next;
if (z != nullptr){
z->prev = y;
}
if(y != nullptr){
y->next = z;
}
if(x == head){
head = z;
}
if(temp != nullptr){
x->next = nullptr;
x->prev = nullptr;
delete x;
}
}
}
};
How to delete this in C++ class (so that it doesn't enter infinte loop)
By not deleting this. Never do that in a destructor. And there's hardly ever a reason to do that in any function.
Just remove ~double_node entirely. The node doesn't own any resources, so it doesn't need a user defined destructor.
I ran into to an issue of memory leak.
You'll need to define ~double_linked_list that loops over the nodes and deletes them. You will also need to follow the rule of 5.
Your delete this here is recursively calling itself.
~double_node(){
cerr << "~double_node() is called\n";
if(this != nullptr){
delete this;
}
}
You can just remove this destructor.
As described in http://www.cplusplus.com/reference/new/operator%20delete/
An expression with the delete operator, first calls the appropriate
destructor (for class types), and then calls a deallocation function.
You can use delete this in some cases, but you should know what you're doing. Here are some tips about this:
https://isocpp.org/wiki/faq/freestore-mgmt#delete-this
Additionally, you should check if temp is null after you exit the while loop in the remove function. Otherwise it will segfault when trying to remove something it can't find.
I have been learning C++ for the last couple of months, and after going through an online course I have started doing some challenges with using raw pointers. I have successfully created class that can initiate and iterate through linked list, with lots of struggle I managed to create method that deletes its element(s), but I am struggling to write a method that would remove all the duplicates.
I have a method such as
void linked_list::remove_dups(){
Node *p = first;
Node *g = first;
while(p!=NULL){
int x = 0;
g = new Node;
g = first;
Node *remove = first;
while (g!=NULL){
if(p->data == g->data){
x++;
}
if(x>1){
remove = new Node;
remove = p;
p = p->next;
remove->next = p->next;
delete remove;
x--;
}
g = g->next;
}
p = p->next;
}
}
where Node is standard
struct Node{
int data;
struct Node *next;
};
first is private member
Node *first;
Can anyone point out what is wrong in this function and explain please? I initiated list with pointers that is
int a[] = {3,5,5,7,65,5,65,65,4,4,15};
and it removed most of the duplicates, but not all. If I add more though, I have EXC_BAD_ACCESS error.
Really appreciate any help!
The main issue is:
p = p->next;
remove->next = p->next;
delete remove;
You try to set remove->next to point to the new next element. But remove is going to be deleted. So the element before remove will still point to something deleted. So, after the delete, you'll have a broken list that contains pointer to deleted elements. Next loop iteration, it will break.
Below is the code I edited as I went. Please note that
remove = new Node;
remove = p;
is not only useless, it leaks memory. You can directly do remove = p;
Just draft code as I went. Not fixed. Proper would be keeping a prev pointer or something.
void linked_list::remove_dups()
{
Node *p = first;
Node *g = first;
while(p!=NULL)
{
// int x = 0; remove this
// g = new Node; remove this
g = first;
Node *remove = first;
while (g != NULL)
{
if(p->data == g->data) // test directly, no x
{
// remove = new Node; remove this
remove = p;
p = p->next;
// remove->next = p->next; You are going to delete this, no point modifying it
delete remove;
}
g = g->next;
}
p = p->next;
}
}
Something I wrote for my custom doubly-linked list that will remove duplicates. Not necessarily the most efficient but it gets the job done.
void unique()
{
std::map<int, int> hash;
Node<T>* temp = this->head;
while(temp)
{
hash[temp->data]++;
if(hash[temp->data] > 1)
{
Node<T>* next = temp->next;
Node<T>* prev = temp->prev;
delete temp;
temp = nullptr;
if(!next)
{
temp = prev;
temp->next = nullptr;
hash[temp->data]--;
break;
}
next->prev = prev;
prev->next = next;
temp = next;
hash[temp->data]--;
}
else
{
temp = temp->next;
}
}
}
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 trying to incorporate push/pop into a linked list and I can't seem to get it to work. When I run my test function, I set my linked list to zero and I try to push on values but the list keeps getting returned with no values in it. Could anyone possibly tell me what I'm doing wrong?
if (top == NULL){
current = top;
current->next = NULL; //NULL->next : will cause segfault
}
if top is NULL, you set current = top [which is NULL], and then you access current->next, which will cause a segfault, you are trying to access NULL..
EDIT: follow up to comments:
your if statement seems redundant, you should probably only need to set: current->next = head; and head = current; [in addition to the current allocation]
Instead of
if (top == NULL){
current = top;
current->next = NULL;
}
you want
if (top == NULL){
top = current;
current->next = NULL;
}
And of course, after this, you have to make sure that you actually set head to top again.
Now that you've made this change, it should be clear that both cases do the same thing -- so no case distinction is actually necessary. So the function can be simplified to
void push(Data * newPushData){
LinkNode * current = new LinkNode(newPushData);
current->next = head;
head = current;
}
The top variable is local variable for push(...) function. You can use head instead, and I'd rather modify the if statement.
I think that function should look like this:
void push(Data * newPushData){
LinkNode * current = new LinkNode(newPushData);
if (head != NULL){
current->next = head;
head = current;
}
else{
head = current;
current->next = NULL; // if you haven't done it in LinkNode constructor
}
}
can you please specify the attributes of the linked list class ? [ is there slightly chance you are doing something wrong]
Instead of you , I'd do :
void push(Data * newPushData){
if (head == NULL)
head->data = newPushData
tail = head ;
else // regular situation
{
Node * node = new Node() ;
tail->next = node;
node->data = newPushData;
node->next = NULL ;
tail = node ;
}
}
In a linked list you have got to maintain the head pointer point on the head of the list , maintain that the tail pointer is point on the tail of the list ,
You must take care of the 2 cases of enlarging the list.
the best way for learning is to illustrate an insertion on a blank linked list.
Take care
S
void push(Data * newPushData)
{
if( head != NULL )
{
LinkNode current = new LinkNode(newPushData);
current->next = head;
head = current;
}
else
{
head = new LinkNode(newPushData);
}
}
Try this code...
void push(data * newpushdata){
if(head !=null){
linkednode current = new linkednode(newpushdata);
current->next = head;
head = current;
}
else {
head = new linkednode(newpushdata);
}
}
that is my working solution for a Stack containing int elements, but maybe it's better to create a void pushStack using Stack **S instead of Stack *S.
in pop(Stack **S) i created a sentinel, so if the stack is empty -1 is returned.:
typedef struct StackT {
int val;
struct StackT *next;
} Stack;
int isStackEmpty (Stack *S) {
if (S == NULL)
return 1;
else
return 0;
}
int *pop(Stack **S) {
Stack *tmp = *S;
int i = -1;
if (isStackEmpty(tmp) == 0) {
i = tmp->val;
*S = tmp->next;
}
return i;
}
Stack *pushStack (Stack *S, int x) {
Stack *node = (Stack *) malloc (sizeof (Stack));
node->val = x;
node->next = S;
return node;
}
you can call pop and stack easly:
Stack *S = NULL;
int x = somevalue;
int y;
S = pushStack(S, x);
y = pop(&S);