I have written a program that can can add char characters to the start of a doubly linked list. Now once I have this list, the aim of my program is to remove certain char character(s) from the list entirely. For example (using curly brackets only for representative purposes): if list consists of { a, b, b, a, c }, then my program can remove all "b" from the list to make it { a, a, c}. Moreover, if my list is {b, a, c, a} or {a, c, a, b} and if I want to remove "b" then the program works fine for both cases and gives me {a, c, a}.
But there's a number of issues (for all cases assume I want to remove "b"):
if my list is {b, a, b, a, c} ("b" at front and somewhere in middle), I get segmentation fault (I think it has to do with using cursor in the while loop, but I don't know why exactly and how to fix it)
if my list is {a, b, b, a, c, b} ("b" in middle and at last) then output gives me weird symbols (I'm assuming its a memory fault, don't know why)
Here is the code I am using:
#include<stdio.h>
#include<stdlib.h>
struct list
{
int data;
struct list* next;
struct list* prev;
};
struct list* head; // global variable - pointer to head node of list.
struct list* last; // global variable - pointer to last node of list
//Creates a new list and returns pointer to it.
struct list* GetNewNode(char x)
{
struct list* newNode
= malloc(sizeof(struct list));
newNode->data = x;
newNode->prev = NULL;
newNode->next = NULL;
return newNode;
}
//Inserts a list at head of doubly linked list
void InsertAtHead(char x)
{
struct list* newNode = GetNewNode(x);
if(head == NULL)
{
head = newNode;
return;
}
head->prev = newNode;
newNode->next = head;
head = newNode;
struct list* temp = head;
while (temp->next != NULL) temp = temp->next;
last = temp;
}
void remove_element (char character)
{
struct list * cursor, *previous, *store_el;
//int boolean = 0;
if (head == NULL) return;
else
{
cursor = head;
while(cursor != NULL)
{
if (cursor->data == character)
{
if (cursor->prev == NULL)
{
// printf("deleting from front\n");
previous = head;
head = head->next;
head->prev = NULL;
//boolean = 1;
//free(previous);
}
if (cursor->next == NULL)
{
//printf("deleting from back\n");
previous = last;
last = last->prev;
last->next = NULL;
//boolean = 1;
//free(previous);
}
else
{
// printf("deleting from middle\n");
previous = cursor;
cursor = cursor->next;
cursor->prev = previous->prev;
store_el = previous->prev;
store_el->next = cursor;
cursor = head;
}
free(previous);
//printf("head data = %c\n", cursor->data);
}
cursor = cursor->next;
}
}
}
//Prints all the elements in linked list in forward traversal order
void Print()
{
struct list* temp = head;
printf("Forward: ");
while(temp != NULL)
{
printf("%c ",temp->data);
temp = temp->next;
}
printf("\n");
}
int main()
{
char character;
/*Driver code to test the implementation*/
head = NULL; // empty list. set head as NULL.
// Calling an Insert and printing list before and after deletion of character
InsertAtHead('c');
InsertAtHead('a');
InsertAtHead('b');
InsertAtHead('b');
InsertAtHead('a');
Print();
printf("After deletion:\n");
remove_element ('b');
Print();
}
/*
*I changed the name of your variable 'last' to 'tail'
*I removed the code at the end of your InsertAtHead function
*I added "tail = newNode;"
*I changed the name of your variable 'previous' to 'garbage'
*I removed your variable 'store_el' completely.
*I could have changed the whole code in your remove element function because the 3 cases are unnecessary but anyway.
*/
//Inserts a list at head of doubly linked list
void InsertAtHead(char x){
struct list* newNode = GetNewNode(x);
if (head == NULL){
head = newNode;
tail = newNode;
return;
}
head->prev = newNode;
newNode->next = head;
head = newNode;
}
void remove_element (char character){
struct list * cursor, *garbage;
cursor = head;
while(cursor != NULL){
if (cursor->data == character){
garbage = cursor;
if (cursor->prev == NULL){
head = head->next;
If (head!=NULL) head->prev = NULL;
cursor=head;
}else if (cursor->next == NULL){
tail = tail->prev;
tail->next = NULL;
cursor=NULL;
}else{
garbage->prev->next = garbage->next;
garbage->next->prev = garbage->prev;
cursor=cursor->next;
}
free(garbage);
} else cursor=cursor->next;
}
}
Try it now.
The problem with your code was that you were using the memory you freed.
/*
*cursor and previous point to the same memory address
*you free the memory that the variable previous points so the cursor points to that freed memory
*when you save the next address to the cursor using that freed memory you create an undefined behaviour (your code may work or may not)
*/
cursor = head;
while(cursor != NULL)
{
if (cursor->data == character)
{
if (cursor->prev == NULL)
{
previous = head;
head = head->next;
head->prev = NULL;
free(previous);
...
cursor=cursor->next;
The Improved Code:
#include<stdio.h>
#include<stdlib.h>
//The type of your variable data was wrong. I changed it to char
struct list{
char data;
struct list *prev, *next
};
void remove_element (char character){
struct list * cursor, *garbage;
cursor = head;
while(cursor != NULL){
if (cursor->data == character){
garbage = cursor;
if (garbage->prev!=NULL ) garbage->prev->next = garbage->next;
if (garbage->next!=NULL ) garbage->next->prev = garbage->prev;
cursor=cursor->next;
if (head==garbage) head=cursor;
//Basically the tail variable has no use for your current program.
//if (tail==garbage) tail=garbage->prev;
free(garbage);
} else cursor=cursor->next;
}
}
Try to update the cursor
cursor = head;
after you delete from the front.
Ellothere I think the problem in your program is you are assigning the address of the node in previous but you are not freeing it side by side.
if (cursor->prev == NULL)
{
// printf("deleting from front\n");
previous = head;
head = head->next;
head->prev = NULL;
//boolean = 1;
//free(previous);
}
if (cursor->next == NULL)
{
//printf("deleting from back\n");
previous = last;
last = last->prev;
last->next = NULL;
//boolean = 1;
//free(previous);
}
Here you are storing node but freeing it at the end and then there are 2 if statements here then what is happening is both 'b' in {a b b a c} are next to each other so first previous variable is storing the address of first 'b' then it is storing the address of next 'b' and it is just freeing that 'b' and that one 'b' remains there . In short you should free the node side- by - side. I did this small change and it works fine .
if (cursor->prev == NULL)
{
// printf("deleting from front\n");
previous = head;
head = head->next;
head->prev = NULL;
free(previous);
//boolean = 1;
//free(previous);
}
if (cursor->next == NULL)
{
//printf("deleting from back\n");
previous = last;
last = last->prev;
last->next = NULL;
free(previous);
//boolean = 1;
//free(previous);
}
else
{
// printf("deleting from middle\n");
previous = cursor;
cursor = cursor->next;
cursor->prev = previous->prev;
store_el = previous->prev;
store_el->next = cursor;
cursor = head;
}
I have just added a free function in both if statements.
Related
I really need help with a problem about linked lists in C.
I need to create a function where I have to read the nodes in the list and, for each node, I have to find occurrences. If the occurrences of the value are equals or greater than a variable value, this nodes must be deleted.
Example:
1->3->8->5->6->8->3->8->9
#of occurrences >= 3
So all the nodes with value 8 must be deleted.
Modified list:
1->3->5->6->3->9
Thank you so much.
Oh sorry my bad.
Yes I tried some solutions, but still didn't find one that works.
For delete all occurrences of a value I did this:
void deleteOccurrences(List *head, int val){
Lista *temp = *testa, *prev;
while(temp != NULL && temp->val == val){
*head = temp->next;
free(temp);
temp = *head;
}
while(temp != NULL){
while (temp != NULL && temp->val != val){
prev = temp;
temp = temp->next;
}
if(temp == NULL)
return;
prev->next = temp->next;
free(temp);
temp = prev->next;
}
}
and for count occurrences I did:
bool countOccurrences(List head, int val, int occur){
int count = 0;
while(head != NULL){
if(head->val == val)
count++;
head = testa->next;
}
if(count >= occur)
return true;
return false;
}
Then the function I'm trying to using is something like this:
void manageList(List head){
while(head != NULL){
int val = head->val;
if(countOccurences(head, val, 3))
deleteOccurrences(&head, val);
head = head->next;
}
}
This is the main:
int main(){
List head;
head = NULL;
head = insert(head,9);
head = insert(head,8);
head = insert(head,3);
head = insert(head,8);
head = insert(head,6);
head = insert(head,5);
head = insert(head,8);
head = insert(head,3);
head = insert(head,1);
manageList(head);
return 0;
}
where insert() function is just an insert at the beginning of the list.
This is the definition of the node:
typedef struct El{
int val;
struct El *next;
}ElemList;
typedef ElemList *List;
When I compile and run this I get a segmentation fault error.
If I try to run just the deleteOccurrences() function or the countOccurrences() function, they work as expected.
The problem is in this function manageList() that I don't understand how to read the list and in the same time find the occurrences and delete nodes.
void manageList(List *head){
ElemList *cur = *head;
while(cur != NULL){
int val = cur->val;
if(countOccurences(cur, val, 3)){
deleteOccurrences(cur, val);
cur = *head;
}else
head = head->next;
}
}
Im new to c programming. I wanted to create a linked list from a given file and then randomly get a node from linked list then delete that node.
So the code works great but for the position 0 in linked list does not work.
Please help me
here's the code:
typedef struct node{
int *name;
struct node *next;
}node;
delete node:
void deleteNode(node **head_ref, int position){
if(*head_ref == NULL){
return;
}
node * temp = *head_ref;
if(position == 0)
{
*head_ref = (*head_ref)->next;
return;
}
int h;
for(h=0 ; temp!=NULL && h<position-1 ; h++){
temp = temp->next;
}
if(temp == NULL || temp->next == NULL)
return;
node * next = temp->next->next;
free(temp->next);
temp->next = next;}
getting random node:
void RandomFromList(node *head){
// IF list is empty
if (head == NULL){
return -1;
}
word = head->name;
// Iterate from the (k+1)th element to nth element
node *current = head;
int n;
for (n=2; current!=NULL; n++)
{
// change result with probability 1/n
if (rand() % n == 0)
word = current->name;
// Move to next node
current = current->next;
}
sprintf(words , "%s" , word);
deleteNode(&head , search(head , word));
printf("Randomly selected key is %s\n", words);}
and the file Reader:
node* fileReader(FILE *file){
node *t = malloc(sizeof(node));
char TopicName[20];
int fileRead = fscanf(file,"%s",TopicName);
if(fileRead != EOF){
t->name = strdup(TopicName);
tedad++;
t->next = fileReader(file);
}
if(fileRead == EOF) {
return NULL;
}
return t;}
EDIT:
When the code run's and when the position randomly got 0 the 0 position of linked list doesn't delete and continues with that node in linked list.
EDIT2:I changed my delete node and it works well without any problem, thank you guys!
node* deleteNode(node* head, unsigned i){
node* next;
if(head == NULL)
return head;
next = head->next;
return i == 0
? (free(head), next)
: (head->next = delete_at_index(next, i - 1), head);
}
The major logical problem I see with your delete function is that it is void, i.e. it returns nothing. This is fine if the node being deleted is in the middle (or end) of the list, because the head does not change. But for the case of deleting the head, the caller might expect that his reference would then point to the next node (or null, if a list of one element) after making the call. Consider this code:
node* deleteNode (node *head_ref, int position)
{
// passing in a NULL list returns NULL
if (head_ref == NULL) {
{
return NULL;
}
// deleting the first element returns the second element as the new head
node* temp = head_ref;
if (position == 0)
{
node* ret = temp->next;
free(head_ref);
return ret;
}
// otherwise walk down the list to one before the deletion position
for (int h=0; temp != NULL && h < position-1; h++) {
temp = temp->next;
}
// if found, delete the node at the desired position
if (temp != NULL && temp->next == NULL) {
node* next = temp->next->next;
free(temp->next);
temp->next = next;
}
// for cases other than deleting the head, just return the current
// (unmodified) head
return head_ref;
}
This isn't related to your problem, but don't forget to free the memory:
node * temp = *head_ref;
if(position == 0)
{
*head_ref = temp->next;
free(temp); // <--------
return;
}
Also, you already have a pointer (temp) to *head_ref, it looks cleaner to me to just use that pointer instead of dereferencing head_ref again.
void deleteNode(node **head_ref, int pos){
node *del;
for ( ; *head_ref; head_ref = &(*head_ref)->next) {
if (pos-- <= 0) break;
}
if (!*head_ref) return; // Reached end of list: nothing found
del = *head_ref;
*head_ref = del->next;
free(del);
return;
}
If you want to keep deleteNode void, then the problem is with your RandomFromList function. You are just changing the * head that exists in the function body not the pointer you passed to the function, so it's still pointing to the previous node.
It's because that pointers are passed by value (copied) like other things in C.
Try making RandomFromList return the head pointer.
P.s. I think you also have some memory leaks in the delete function.
I am working on doubly linked list in c, I have a doubly linked tepm2 with 20 nodes and I want to delete the node whose word user insert.
struct node {
struct node *prev;
char word[100];
int repeatTime;
struct node *next;
} *h, *temp, *temp1, *temp2;
Each node has unique word.
printf("\n Enter word to delete : ");
scanf("%s", &word);
Delete(word);
int delete(char data[200]) { //unable to delete
if (h == NULL)
return;
temp2 = next = previous = h;
while (temp2->next != NULL) {
if ((strcmp(temp2->word, data) == 0)) {
if (temp2->prev == NULL) {
h = temp2->next;
free(temp2);
return;
} else if (temp2->prev == NULL) {
previous->next = temp2;
free(temp2);
previous->next = NULL;
return;
} else {
previous->next = temp2->next;
next->prev = temp2->next;
}
}
temp2 = temp->next;
}
}
I am been unable to delete the specific node that word user enter
Try this:
int delete(const char *data)
{
struct node *temp = h;
if (h == NULL) return;
while (temp->next != NULL)
{
if (strcmp(temp->word, data) == 0)
{
if (temp->prev != NULL)
{
temp->prev->next = temp->next;
}
if (temp->next != NULL)
{
temp->next->prev = temp->prev;
}
if (h == temp)
{
h = temp->next;
}
free(temp);
return;
}
temp = temp->next;
}
}
First of all I don't think this is right temp2 = next = previous = h;
Now all you have to do is find the node that you want to delete by traversing and than link it's prev node to it's next node i.e. (temp2->prev)->next = next and (temp2->next)->prev = prev and free it.
Now the real issue lies with
1. The first node that has other nodes after it
2. last node that has other nodes preceding it
3. only node
You can simplify all three by converting them into the former problem i.e. node in the middle problem which we've just solved.
For simplifying you can just make the head and tail both NULL.
I have an issue where I am trying to delete an entry from a linked list but it causes a segmentation fault no matter where I try to delete the item from (head, middle, or tail). I'm not sure where the issue lies.
void
add_to_list(struct linked_list *list, int x)
{
struct node *n = malloc(sizeof *n);
n->data = x;
n->next = NULL;
if (list->head == NULL)
list->head = n;
if (list->tail != NULL)
list->tail->next = n;
list->tail = n;
}
void
remove_from_list(struct linked_list *list, int position)
{
struct node *current_node = list->head;
struct node *previous_node = NULL;
int i;
for (i = 0; i < position; i++) {
previous_node = current_node;
current_node = current_node->next;
}
if (position == 0) { // removing the head means we have to
// update the head pointer
list->head = list->head->next;
} else {
previous_node->next = current_node->next;
}
free(current_node);
if (list->tail == current_node) // remove the last element means
// updating the tail pointer
list->tail = previous_node;
}
int
main(void)
{
struct linked_list list = { .head = NULL, .tail = NULL };
add_to_list(&list, 'h');
add_to_list(&list, 'e');
add_to_list(&list, 'l');
add_to_list(&list, 'l');
add_to_list(&list, 'o');
remove_from_list(&list, 'e');
add_to_list(&list, 's');
print_list_rec(&list); // print_nodes_rec(list.head)
free_list(&list);
return 0;
}
The call
remove_from_list(&list, 'e');
specifies 'e' as a position in the list. The ascii value of 'e' is 101; you have 5 items in the list.
remove_from_list iterates through the list position times without checking whether it has reached the end.
You need to change this to either have the caller pass the index they want to remove or, better, change the second argument to be the item value to search for and modify the for loop in remove_from_list to exit when it finds this value.
void remove_from_list(struct linked_list *list, int data)
{
struct node *current_node = list->head;
struct node *previous_node = NULL;
while (current_node != NULL) {
if (current_node->data == data) {
break;
}
previous_node = current_node;
current_node = current_node->next;
}
if (previous_node == NULL) { // removing the head means we have to
In either case, it'd be safer if remove_from_list also guarded against reading beyond the end of its list
It appears that you are not checking whether you've reached the end of the list, and you might be trying to free a NULL pointer.
was implementing a singular linked list in C.
struct node
{
int data;
struct node *next;
};
struct list_el {
int val;
struct list_el * next;
};
typedef struct list_el item;
void main() {
item * curr, * head,*track;
int i;
head = NULL;
for(i=1;i<=10;i++) {
curr = (item *)malloc(sizeof(item));
curr->val = i;
curr->next=0;
if(head!=NULL)
head->next = curr;
head = curr;
}
curr = curr-10;
while(curr) {
printf("%d\n", curr->val);
curr = curr->next ;
}
}
As there are 10 elements in the list, so to make the pointer point to the first element, I tried decreasing curr (pointer to struct) by 10, but this got me half way through the list, the values printed were 5,6,7,8,9,10.
The size of the struct is 4, whereas the size of the pointer is 2, it seems the pointer is decreased by 2*10=20 bytes instead of 40, is this normal? (as I read that pointer increments/decrements according to the size of its type)
You cannot use pointer arithmetic on a linked list: the items are allocated separately (with malloc) and so they will not be necessarily adjacent in memory. That approach would only work with an array.
There are several problems.
First of all, the following insertion code isn't correct:
if(head!=NULL) head->next = curr;
head = curr;
Basically, the element pointed to by head is irrevocably lost.
Secondly, the behaviour of the following code is undefined:
curr = curr-10;
You cannot move across several malloc()ed blocks using pointer arithmetic.
Once you fix the insertion logic, it will become possible to traverse the list like so:
for (curr = head; curr != NULL; curr = curr->next) {
....
}
Your code curr = curr-10 will not bring you back to the head of the linklist.
As Viruzzo pointed out in a comment, you cannot use pointer arithmetic on elements of a linked list. As the word "linked" implies, there are only pointers linking the items together, they're not required to be located at adjacent addresses.
The pointer arithmetic will simply decrease the pointer by a fixed number of bytes, it will not follow pointers. Your list, being singly-linked, doesn't even have previous-element pointers to follow.
curr = curr-10; is wrong. It does not perform the operation that you think it does!
To print the contents of your linked list, you need to start from the head and go through each and every node until you hit NULL (assuming its not a circular list).
void display()
{
NODE * current = head;
if (current == NULL) {
printf("Empty list \n");
return;
}
while(current != NULL) {
printf("%d ", current->data);
current = current->next;
}
printf("\n");
return;
}
And to add new node in the front, you can use the following code snippet.
void addfront(int data)
{
NODE *newnode = NULL;
if ((newnode = malloc(sizeof(NODE))) != NULL) {
newnode->data = data;
newnode->next = NULL;
} else {
printf("Couldn't allocate space for new element \n");
return;
}
if (head == NULL) {
// empty list
head = newnode;
tail = newnode;
} else {
newnode->next = head;
head = newnode;
}
return;
}
To add new node at the rear, you can use the following code snippet.
void addrear(int data)
{
NODE * newnode = NULL;
if ((newnode = (NODE *) malloc(sizeof(NODE))) != NULL) {
newnode->data = data;
newnode->next = NULL;
} else {
printf("unalbe to allocate memory to the new element - %d \n", data);
return;
}
if (tail == NULL) {
assert(head == NULL && tail == NULL);
head = tail = newnode;
} else {
tail->next = newnode;
tail = newnode;
}
return;
}
All the above mentioned code snippet assumes, you have head and tail as global variables.
Hope this helps!