I try to delete a word (char* value) from a linked list but I am getting an exception when I compare my word to the links list node word.
EXC_BAD_ACCESS (code=1, address=0xa00000002)
I'm not sure why this is happening, I would appreciate to get any solution to the problem.
LinkedList* DeleteWordElement(LinkedList* head, char* value){
LinkedList *previous=head, *current=head->next;
if (head == NULL)
return head;
if (head->data == value)
{
LinkedList *temp = head;
head = head->next;
free(temp);
return head;
}
while (previous!=NULL)
{
if (previous->data == value) // Exception
break;
current = previous;
previous = previous->next;
}
if (previous != NULL)
current->next = previous->next;
free(previous);
return head;
}
Before doing:
LinkedList *previous=head, *current=head->next;
you need to check if the head is NULL. Then you should use strcmp, instead of ==, to compare the C-string value:
strcmp compares the actual C-string content, while using == between
two C-string is asking if these two char pointers point to the same
position. (source)
Finally, you do not need to keep the previous pointer, the current->next pointer is enough:
LinkedList* DeleteWordElement(LinkedList* head, char* value){
if (head == NULL)
return head;
if (strcmp(value, head->data) == 0){
LinkedList *next = head->next;
free(head);
return next;
}
LinkedList *current=head;
while (current->next != NULL){
if (strcmp(value, current->next->data) == 0){
LinkedList *next = current->next;
current->next = current->next->next;
free(next);
break;
}
current = current->next;
}
return head;
}
The code can be shorten if you use the recursive version, albeit at the cost of performance:
LinkedList* DeleteWordElement(LinkedList* current, char* value){
if(current != NULL){
if (strcmp(value, current->data) == 0){
LinkedList *result = current->next;
free(current);
return result;
}
current->next = DeleteWordElement(current->next, data);
}
return current;
}
As has already been pointed in the other answer and in the comments section, the expression
current=head->next
will cause undefined behavior when head == NULL.
Another problem is that the expression
head->data == value
does not compare the actual string contents, but instead it compares the pointers themselves (i.e. the memory addresses). This is not what you want, because the pointers will probably always be different, even when the string contents are identical. In order to compare the actual string contents, you must use the function strcmp instead.
The other answer already contains a solution to the problem. However, I would like to provide an alternate solution, which is shorter and more efficient for the computer, but is maybe harder to understand for a programmer, because it uses double pointers (i.e. pointers to pointers):
LinkedList* DeleteWordElement( LinkedList *head, char* value )
{
LinkedList **pp = &head, *p;
while ( (p=*pp) != NULL )
{
if ( strcmp( p->data, value ) == 0 )
{
//found node, so unlink and remove it
*pp = p->next;
free( p );
break;
}
pp = &p->next;
}
return head;
}
As you can see, this solution only requires a single if statement, in contrast to the code in your answer which requires 4 if statements and the other answer, which requires 3 if statements. These additional if statements are not necessary when using double pointers, because the same code can handle all cases. Therefore, you don't need to introduce additional code paths for every single case. As a consequence, this solution also has no need for code duplication.
It is also worth mentioning that the function signature
LinkedList* DeleteWordElement(LinkedList* head, char* value)
is a bit inefficient. The return value is the new head, so the code that calls the function must update the list head based on the return value. It would be simpler if the code that calls the function simply passes the address of the list head pointer, so the function can update the list head itself, when necessary. That way, the code which calls the function won't have to do any additional work.
In order to accomplish this, you can change the function signature to the following:
void DeleteWordElement( LinkedList** pp_head, char* value )
Because the address of a pointer is a pointer to a pointer (i.e. a double pointer), you must now use ** instead of only *.
Also, now that you are no longer using the return value of the function, you may want to use it for something else. For example, you may want the function to return whether the value was found or not. Therefore, you may want to change the signature to the following:
bool DeleteWordElement( LinkedList** pp_head, char* value )
Now you can make the function return true when the value was found, otherwise return false to indicate that the value was not found. Note that you must #include <stdbool.h> to have access to bool, true and false.
Although we have made the function more powerful by adding the boolean return value, it is just as simple as what we had before.
bool DeleteWordElement( LinkedList **pp_head, char* value )
{
LinkedList **pp = pp_head, *p;
while ( (p=*pp) != NULL )
{
if ( strcmp( p->data, value ) == 0 )
{
//found node, so unlink and remove it, and then return true
*pp = p->next;
free( p );
return true;
}
pp = &p->next;
}
//return false because we did not find any matching node in the list
return false;
}
Related
I was trying to remove the duplicate element in a sorted linked list using recursion concept.
I wanna see how to remove the elements in a sorted linked list. I made a code in which if head->data == head->next->data than head->next should be freed until we get the different element.
Now I have made so many changes I am confused how I am supposed to do it. It is deleting every value that is duplicate and only leaving the one that was only appeared in the entire code only once.
Please also tell me why this code is doing this and also what can wrong with code and if any optimal way possible to do the same thing.
(I am only providing the deleteduplicate function if there is a need to provide the whole code like print the list or insert in the list I will edit it if told).
Thanks.
Node *deleteDuplicates(Node *head) {
if (head == NULL || head->next == NULL) {
return head;
}
if (head->data == head->next->data) {
struct Node *x = head->next;
head->next = head->next->next;
free(x);
return deleteDuplicates(head);
} else
return deleteDuplicates(head->next);
}
Input: 11 11 11 13 13 20
Output: 20
Expected output: 11 13 20
It is deleting every value that is duplicate and only leaving the one value that was only appeared in the entire code only once.
No. It is deleting only duplicate values but you always return pointer to the last node.
if(head==NULL ||head->next==NULL){
return head;
}
You don't need to return the new head, since only duplicates are going to be removed, there is no way head is going to change.
There is no need for recursion in this function. Just iterate in a loop either removing the next element or skipping to the next element:
Node *deleteDuplicates(Node *head) {
if (head != NULL) {
Node *p = head;
while (p->next) {
if (p->next->data == p->data) {
Node *x = p->next;
p->next = x->next;
free(x);
} else {
p = p->next;
}
}
}
return head;
}
You could fix your recursive function, but it should be modified to not return the head node as this prevents tail recursion, therefore requiring a potentially huge amount of stack space. A sufficiently long list would cause a Stack overflow.
Here is a modified recursive function:
void deleteDuplicates(Node *head) {
if (head != NULL && head->next != NULL) {
if (head->data == head->next->data) {
struct Node *x = head->next;
head->next = x->next;
free(x);
deleteDuplicates(head);
} else {
deleteDuplicates(head->next);
}
}
}
The problem in your code is you store the return value of deleteDuplicates into your head pointer, but the function returns the pointer to the last node in the list, not the head node.
int Count(struct node* head, int searchFor) {
struct node* current = head;
int count = 0;
while (current != NULL) {
if (current->data == searchFor)
count++;
current = current->next;
}
return count;
}
Why there is need to declare current struct node? We would get the same result if we use head that is passed in the argument list.
int Count(struct node* head, int searchFor) {
int count = 0;
while(head != NULL) {
if (head->data == searchFor) {
count ++;
}
head = head->next;
}
return count;
}
What is the concept behind this? Is this just for clean code or some other reason?
It's generally done so as to preserve the original value (head) passed to the function since current is being modified and you'll lose the original pointer permanently if you modify head directly. But in your case, it makes no difference whether you use an extra temporary current or not as head is not needed any further in Count().
It's matter of taste, really. I'd personally prefer using version with current as I feel it's more readable with "current" than modifying "head" directly.
Of course it's the "same", they're "pointing" to the same location at the beginning.
struct node* current = head;
But after you change the current item to the next they stop pointing to the same.
current = current->next;
After this your current is not the same as head anymore, and if you need to restart you still have a reference to head in head pointer.
The point is what you expect when you read "head", you expect that it's pointing to the head item, right? If you change it'll make your code confuse for other programmers who end up maintaining it.
What I would suggest is to rename your parameter to current if you don't need to use head in this function anymore (but it's always good to have a pointer to head in some place).
int Count(struct node* current, int searchFor) {
int count = 0;
while(current != NULL) {
if (current->data == searchFor) {
count ++;
}
current = current->next;
}
return count;
}
For the code you showed there is only one concept behind this: to make the code more readable.:)
When you see that head is changed then you have to be more attentive and to be sure that the code is correct. This takes more time that to understand the code.:)
Nevertheless the first code snippet has a drawback. Variable current is declare in the code block where it is not used. I would write the code the following way
int Count( const struct node* head, int searchFor )
{
int count = 0;
for ( const struct node* current = head; current != NULL; current = current->next )
{
if ( current->data == searchFor ) count++;
}
return count;
}
i have a linked list like this :
1,jhon,19
2,sara,18
3,tom,20
4,jack,22
i have been trying forever to delete an element based on their id (witch is the first number). But in order to do that i need to delete this element from any position. so i came up with this code and i was wondering if it's correct :
temp1=head;
if(head!=NULL && head->id==givenID) // if the element is in the first position
{
temp = head;
head = head->next;
free(temp);
}
else if(head!=NULL && head->id!=givenID){// search for the element in the middle
do{
temp2=head;
head = head->next;
}while(head->id !=givenID && head->next !=NULL);
if(head->next !=NULL && head->id==givenID){// if the element is in the middle
temp2->next=head->next;
free(head);
head=temp1;
}
else if(head->next ==NULL && head->id==givenID){// if the element is in the last position
temp->next=NULL;
free(head);
head=temp1;
}
}
Thank you
This code is too complex, because it has unnecessary branches. You can unify your code by using a pointer to pointer.
The idea is to point your pointer to pointer to the head of the list, then to the next pointer of the initial element of the list, then to the next pointer of the second element of the list, and so on. The beauty of this approach is that no matter where you are in your list, the operation on a pointer to pointer remains the same!
Here is how it looks in code:
// Point your pointer to pointer to the head of the list
struct node **pptr = &head;
while (*pptr != NULL) {
// Dereference pptr to get the pointer to current node
node *current = *pptr;
// Check if the id of this node matches what we're looking for
if (current->id == givenID) {
// Here is the "magic": assign the next pointer of the current node
// to whatever is pointed to by pptr.
// It could be a head, or a next of some node.
*pptr = current->next;
free(current);
break;
}
pptr = &(current->next);
}
That's it! Since the pointer to pointer does not differentiate between head and other nodes, there is no additional checking going on.
Consider using a sentry node. All special cases disappear when you do.
This is how node erasure look in a linked list with sentry:
Iterator Erase( List* lst, Iterator here )
{
Iterator nxt = here->next;
Link( here->prev, here->next );
free( here );
lst->size -= 1;
return nxt;
}
with Link being no more complicated than
void Link( Iterator n1, Iterator n2 )
{
n1->next = n2;
n2->prev = n1;
}
All the other core functions, like insert etc. are similarly trivial.
this is the case i am working on
[11] -> [12] -> [13] -> NULL
I am trying to delete the elements from the liked list above(example) but I keep getting segfault and on running GDB doesnot help much. I am not looking for an answer but and explanation on where I am going wrong logically.
here is the code
int
List:: remove( int val )
{
ListNode *headNode = _head;
ListNode *tempNode = NULL;
if(headNode->_value == val){
tempNode = headNode->_next;
delete headNode;
_head = tempNode;
}
else
{
while(headNode->_value != val){
tempNode = headNode;
headNode = headNode->_next;
}
tempNode->_next = headNode->_next;
delete headNode;
}
}
You're not accounting for the following conditions:
The list may be empty; i.e. _head is NULL;
The value may not be in the list at all.
Your function is declared to return int, but makes no such return
Assuming the rest of your code is correct (and that is a big assumption), I'm all-but-certain this is what you're trying to do:
void List::remove( int val )
{
ListNode *headNode = _head;
ListNode *tempNode = NULL;
while (headNode && headNode->_value != val)
{
tempNode = headNode;
headNode = headNode->next;
}
if (headNode)
{
if (tempNode)
tempNode->next = headNode->next;
else
_head = headNode->next;
delete headNode;
}
}
Alternatively, if so inclined this can get (arguably) simpler utilizing a pointer-to-pointer to traverse the pointers in the list, not just their values. It is worth investigating how the following works, which still covers all the bases described previously, but does so using the actual pointers in the list nodes themselves, including _head, by-address rather than by-value, thereby eliminating the need for a walk-behind temporary pointer:
void List::remove( int val )
{
ListNode **pp = &_head;
while (*pp && (*pp)->_value != val)
pp = &(*pp)->next;
if (*pp)
{
ListNode *p = *pp;
*pp = p->next;
delete p;
}
}
In your remove method you are assuming there are always elements in your list. - What if it is empty?
What if the value isn't in the list? You need to handle this case as well.
You're headed in the right direction - there are just a few cases that you haven't considered that can lead you to seg fault.
Example of forward traversal with deletion (forward-only linked list):
// Start from the beginning (head), then while the current isn't null,
// move to the next node.
for (ListNode* current = head; current != null; current = current->next) {
// Check the next item if there is one, and remove it if it matches the value.
// We check the next one because you can't delete the current node in a
// forward only linked list (can in a doubly-linked list however)
if (current->_next != nullptr && current->_value == value) {
// Make this item point to the next next item
// (Since we're gonna delete the next item)
current->_next = current->_next->next;
// Delete the next item.
delete current->_next;
}
}
I am trying to write a singly-linked list in C. So far, I just get segmentation faults.
I am probably setting the pointers wrong, but I just couldn't figure how to do it correctly.
The list should be used for "processors" sorted from highest priority (at the beginning of the list) to lowest priority (at the end of the list). Head should point to the first element, but somehow I am doing it wrong.
First of all here is the code:
struct process {
int id;
int priority;
struct process *next;
}
struct process *head = NULL;
void insert(int id, int priority) {
struct process * element = (struct process *) malloc(sizeof(struct process));
element->id = id;
element->priority = priority;
while(head->next->priority >= priority)
head = head->next;
element->next = head->next;
head->next = element;
// I put here a printf to result, which leads to segmenatition fault
// printf("%d %d\n", element->id, element->priority);
}
/* This function should return and remove element with the highest priority */
int pop() {
struct process * element = head->next;
if(element == NULL)
return -1;
head->next = element->next;
free(element);
return element->id;
}
/* This function should remove a element with a given id */
void popId(int id) {
struct process *ptr = head;
struct process *tmp = NULL;
while(prt != NULL) {
if(ptr->id == id) {
ptr->next = ptr->next->next;
tmp = ptr->next;
} else {
prt = ptr->next;
}
}
free(tmp);
}
Unfortunately, I could not try out pop() and popId() due to the segmentation fault.
May anyone tell me what I am doing wrong?
EDIT: Now, I edited the insert function. It looks like this:
void insert(int id, int priority) {
struct process * element = (struct process *) malloc(sizeof(struct process));
struct process * temp = head;
element->id = id;
element->priority = priority;
if(head == NULL) {
head = element; // edited due to Dukeling
element->next = NULL;
} else {
while(temp->next != NULL && temp->next->priority >= priority)
temp = temp->next;
element->next = head->next;
head->next = element;
}
// I put here a printf to result, which leads to segmenatition fault
// printf("%d %d\n", element->id, element->priority);
}
But I still get segmentation fault for pop() and popId(). What did I miss here?
You don't check if head is NULL in insert.
You actually don't check if head is NULL in any function. You should, unless you want to put some dummy element on head, to simplify the code.
For insert:
About these lines:
while(head->next->priority >= priority)
head = head->next;
If head is NULL, that's not going to work. This may not actually be a problem if head can never be NULL for whichever reason (e.g. it has a dummy element as gruszczy mentioned).
You're changing head, thus you're getting rid of the first few elements every time you insert. You probably need a temp variable.
You need to also have a NULL check in case you reach the end of the list.
So, we get:
struct process *temp = head;
while (temp->next != NULL && temp->next->priority >= priority)
temp = temp->next;
For pop:
If the first element isn't a dummy element, then you should be returning the ID of head, not head->next (and you were trying to return a value of an already freed variable - this is undefined behaviour).
if (head == NULL)
return -1;
int id = head->id;
struct process *temp = head;
head = head->next;
free(temp);
return id;
For popId:
You're checking ptr's ID, but, if it's the one we're looking for, you're removing the next element rather than ptr. You should be checking the next one's ID.
head == NULL would again need to be a special case.
The free should be in the if-statement. If it isn't, you need to cater for it not being found or finding multiple elements with the same ID.
You should break out of the loop in the if-statement if there can only be one element with that ID, or you want to only remove the first such element.
I'll leave it to you to fix, but here's a version using double-pointers.
void popId(int id)
{
struct process **ptr = &head;
while (*ptr != NULL)
{
if ((*ptr)->id == id)
{
struct process *temp = *ptr;
*ptr = (*ptr)->next;
free(temp);
}
else
{
prt = &(*ptr)->next;
}
}
}
Note that the above code doesn't break out of the loop in the if-statement. This can be added if you're guaranteed to only have one element with some given ID in the list, or you want to just delete the first such element.
Your not checking your pointers before accessing their values for dereference. This will automatically lead to undefined behavior if the pointer is invalid (NULL or indeterminate). With each implementation below, note we don't access data via dereference unless the pointer is first-known as valid:
Implementation: insert()
void insert(int id, int priority)
{
struct process **pp = &head;
struct process *element = malloc(sizeof(*element);
element->id = id;
element->priority = priority;
while (*pp && (*pp)->priority >= priority)
pp = &(*pp)->next;
element->next = *pp;
*pp = element;
}
Implementation: pop()
Your pop() function appears to be designed to return the popped value. While this isn't entirely uncommon it has the undesirable side-effect of having no mechanism for communicating to the caller that the queue is empty without a sentinel-value of some sort (such as (-1) in your case. This is the primary reason most queues have a top(), pop(), and isempty() functional interface. Regardless, assuming (-1) is acceptable as an error condition:
int pop()
{
struct process *tmp = head;
int res = -1;
if (head)
{
head = head->next;
res = tmp->id;
free(tmp);
}
return res;
}
Implementation: popId()
Once again, looking for a specific node can be accomplished with a pointer-to-pointer in a fairly succinct algorithm, with automatic updating done for you due to using the actual physical pointers rather than just their values:
void popId(int id)
{
struct process ** pp = &head, *tmp = NULL;
while (*pp && (*pp)->id != id)
pp = &(*pp)->next;
if (*pp)
{
tmp = *pp;
*pp = tmp->next;
free(tmp);
}
}
I strongly advise stepping through each of these with a debugger to see how they work, particularly the insert() method, which has quite a lot going on under the covers for what is seemingly a small amount of code.
Best of luck