Doubly linked list delete last C - c

Im working on a implementation of a doubly linked list. I want the linked list to be limited by some length. When the list becomes longer then delete the last node. Im having some problems here. I want to define tail so that i dont have to search for the end. Here is the implementation i am working on it will allow for a length of 4 then start deleting the last node.
/* Doubly Linked List implementation */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct Node {
char command[1024];
struct Node* next;
struct Node* prev;
};
struct Node* head; //global pointing to head
struct Node* tail; //global pointing to tail
//Creates a new Node and returns pointer to it.
struct Node* GetNewNode(char *line) {
struct Node* newCommand = (struct Node*)malloc(sizeof(struct Node));
int i = 0;
while(line[i] != '\0'){
newCommand->command[i] = line[i];
i++;
}
newCommand->prev = NULL;
newCommand->next = NULL;
return newCommand;
}
//Inserts a Node at head of doubly linked list
void InsertAtHead(char *line) {
struct Node* newCommand = GetNewNode(line);
if(head == NULL) {
head = newCommand;
tail = newCommand;
return;
}
head->prev = newCommand;
newCommand->next = head;
head = newCommand;
}
//Use tail to delete the last node
void deleteLast(){
struct Node* temp = tail;
tail = temp->prev;
free(tail->next);
tail->next = NULL;
}
//Print in reverse orer
void Print() {
struct Node* temp = tail;
while(temp != NULL) {
printf("%s \n",temp->command);
temp = temp->prev;
}
}
int main() {
int numNodes = 0;
char line[1024];
head = NULL;
tail = NULL; // empty list. set head/tail as NULL.
printf("next node: ");
while (fgets(line, 1024, stdin)) {
line[strlen(line)-1] = '\0';
if(numNodes == 4){
numNodes -= 1;
deleteLast();
}
InsertAtHead(line);Print();
numNodes += 1;
printf("next node: ");
}
Print();
}
it seems to be deleting the last node but printing some weird symbols after. Im guessing that its a problem with how i free() but i cant figure it out.
note some of this code is taken from https://gist.github.com/mycodeschool/7429492

Your code looks fine, hoewever, there is an error in copying the command to the node:
while(line[i] != '\0'){
newCommand->command[i] = line[i];
i++;
}
should be:
while(line[i] != '\0'){
newCommand->command[i] = line[i];
i++;
}
newCommand->command[i] = '\0';
(You forgot to terminate the copied command.)
Note also that you must check not to go beyond available space, e.g.:
while(i<1023 && line[i] != '\0'){
newCommand->command[i] = line[i];
i++;
}
newCommand->command[i] = '\0';

Related

Remove adjacent duplicates in linked list in C

As the title mentioned, I have to remove adjacent duplicates in linked list such that if input is 'google', output should be 'le'. I'm supposed to code it in C. I've written 70% of the code, except that I don't know how to continuously loop till all adjacent duplicates are removed. I'm removing adjacent duplicates in remove_adjacent_duplicates() function, and since I don't know how to put terminating condition in loop, I've merely used if-else loop. But my code in remove_adjacent_duplicates() function might contain mistakes, so please rectify it if any and please give solution to looping till all adjacent duplicates are removed. Here's my code-
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct node //node creation
{
char data;
struct node *next;
};
void remove_adjacent_duplicates(struct node** head_ref)
{
struct node* current = *head_ref;
struct node* cnext = NULL; //the one next to current one
int flag=0;
cnext = current->next; //storing next
//printf("%c %c %d\n",current->data,cnext->data,flag);
if(cnext->data==current->data)
{
flag=1;
while(cnext->data==current->data)
{
cnext=cnext->next;
}
current=cnext;
cnext = current->next; //storing next
}
else
{
current=current->next;
cnext = current->next; //storing next
}
//printf("%c %c %d\n",current->data,cnext->data,flag);
if(flag) *head_ref = current;
}
void push(struct node** head_ref, char new_data)
{
struct node* new_node = (struct node*)malloc(sizeof(struct node));
new_node->data = new_data;
new_node->next = *head_ref;
*head_ref = new_node;
}
void printList(struct node* head)
{
if (head == NULL)
{
printf("NULL\n\n");
return;
}
printf("%c->",head->data);
printList(head->next);
}
int main()
{
char s[100];
int i;
struct node* a = NULL;
printf("Enter string: ");
scanf("%s",s);
for(i=strlen(s)-1;i>-1;i--){
push(&a, s[i]); //last in first out, so in reverse g is last but first to come out
}
printf("\nConverting string to linked list: \n");
printList(a);
//printf("%c",current->data); prints first letter of a
remove_adjacent_duplicates(&a);
printList(a);
return 0;
}
You could use recursion. That way you can check whether before the recursive call or after the recursive call there is something to remove:
void remove_adjacent_duplicates(struct node** head_ref)
{
struct node* current = *head_ref;
if (current == NULL || current->next == NULL) return;
int isEqual = current->data == current->next->data;
remove_adjacent_duplicates(&current->next);
if (current->next != NULL && current->data == current->next->data) {
// Duplicates! Remove pair
*head_ref = current->next->next;
free(current->next);
free(current);
} else if (isEqual) {
// Continue ongoing removal
*head_ref = current->next;
free(current);
}
}
A few issues ...
The first element of list (e.g. head) can never be a duplicate
The code leaks memory when removing a dup because it doesn't do free
The code only removes the first element.
The code uses next, cur, but not previous, so the algorithm needs refactoring.
Casting the return of malloc is bad. See: Do I cast the result of malloc?
scanf is problematic. %s can overrun the end of the array. Better to use (e.g.) %99s [or better yet: fgets].
Here is the refactored code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// node definition
struct node {
char data;
struct node *next;
};
void
remove_adjacent_duplicates(struct node **head_ref)
{
struct node *prev = *head_ref;
struct node *cur;
struct node *next;
// NOTE: first node can _never_ be a dup
if (prev != NULL)
cur = prev->next;
else
cur = NULL;
for (; cur != NULL; cur = next) {
// remember this in case we remove cur to prevent "use after free" bug
// when advancing cur
next = cur->next;
// remove duplicate
if (cur->data == prev->data) {
prev->next = next;
free(cur);
continue;
}
// point to last non-dup node
prev = cur;
}
}
void
push(struct node **head_ref, char new_data)
{
// NOTE/BUG: casting the result of malloc is bad
#if 0
struct node *new_node = (struct node *) malloc(sizeof(struct node));
#else
struct node *new_node = malloc(sizeof(*new_node));
#endif
new_node->data = new_data;
new_node->next = *head_ref;
*head_ref = new_node;
}
void
printList(struct node *head)
{
if (head == NULL) {
printf("NULL\n\n");
return;
}
printf("%c->", head->data);
printList(head->next);
}
int
main(void)
{
char s[100];
int i;
struct node *a = NULL;
printf("Enter string: ");
// NOTE/BUG: scanf is bad -- it can overrun the end of s
#if 0
scanf("%s", s);
#else
scanf("%99s", s);
#endif
// last in first out, so in reverse g is last but first to come out
for (i = strlen(s) - 1; i > -1; i--) {
push(&a, s[i]);
}
printf("\nConverting string to linked list: \n");
printList(a);
// printf("%c",current->data); prints first letter of a
remove_adjacent_duplicates(&a);
printList(a);
return 0;
}
In the above code, I've used cpp conditionals to denote old vs. new code:
#if 0
// old code
#else
// new code
#endif
#if 1
// new code
#endif
Note: this can be cleaned up by running the file through unifdef -k
UPDATE:
From the OP: "[ .. ] 'google', output should be 'le'.". Looks like both nodes are removed, and the effect compounds. –
Oka
Yes, it's much more complex. But, here is a version that removes all duplicates. I had some trouble myself, so I left in the debugging code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if DEBUG
#define dbgprt(_fmt...) \
printf(_fmt)
#else
#define dbgprt(_fmt...) \
do { } while (0)
#endif
// node definition
struct node {
char data;
#if DEBUG
int seq;
#endif
struct node *next;
};
#if DEBUG
int seq = 0;
#endif
void
remove_adjacent_duplicates(struct node **head_ref)
{
struct node *prev = *head_ref;
struct node *cur;
struct node *next;
// NOTE: first node can _never_ be a dup
if (prev != NULL)
cur = prev->next;
else
cur = NULL;
for (; cur != NULL; cur = next) {
// remember this in case we remove cur to prevent "use after free" bug
// when advancing cur
next = cur->next;
// remove duplicate
if (cur->data == prev->data) {
prev->next = next;
free(cur);
continue;
}
// point to last non-dup node
prev = cur;
}
}
void
remove_all_duplicates(struct node **head_ref)
{
struct node *oldhead = *head_ref;
struct node *addprev = NULL;
struct node *addnext;
// loop through all candidate nodes
for (struct node *addcur = oldhead; addcur != NULL; addcur = addnext) {
int dupflg = 0;
// start of search for duplicates to the right of the candidate
struct node *prevdup = NULL;
struct node *dupcur = addcur->next;
// find all duplicates to the right [towards tail] of candidate node
while (1) {
// find first duplicate to the right of candidate [if one exists]
struct node *dupnext = NULL;
for (; dupcur != NULL; dupcur = dupnext) {
dupnext = dupcur->next;
if (dupcur->data == addcur->data) {
dupflg = 1;
break;
}
prevdup = dupcur;
}
// no more duplicates to the right of current candidate
if (dupcur == NULL)
break;
// remove a duplicate on the right
if (prevdup != NULL)
prevdup->next = dupnext;
else
addcur->next = dupnext;
free(dupcur);
}
addnext = addcur->next;
// remove candidate because it's a dup
if (dupflg) {
if (addprev != NULL)
addprev->next = addnext;
else
oldhead = addnext;
free(addcur);
continue;
}
// remember last valid non-dup node
addprev = addcur;
}
*head_ref = oldhead;
}
void
push(struct node **head_ref, char new_data)
{
// NOTE/BUG: casting the result of malloc is bad
#if 0
struct node *new_node = (struct node *) malloc(sizeof(struct node));
#else
struct node *new_node = malloc(sizeof(*new_node));
#endif
new_node->data = new_data;
#if DEBUG
new_node->seq = seq++;
#endif
new_node->next = *head_ref;
*head_ref = new_node;
}
void
printList(struct node *head)
{
if (head == NULL) {
printf("NULL\n\n");
return;
}
#if DEBUG
printf("%c%d->", head->data, head->seq);
#else
printf("%c->", head->data);
#endif
printList(head->next);
}
int
main(int argc,char **argv)
{
char s[100];
int opt_a = 0;
int i;
struct node *a = NULL;
--argc;
++argv;
for (; argc > 0; --argc, ++argv) {
char *cp = *argv;
if (*cp != '-')
break;
switch (cp[1]) {
case 'a':
opt_a = ! opt_a;
break;
}
}
printf("Enter string: ");
// NOTE/BUG: scanf is bad -- it can overrun the end of s
#if 0
scanf("%s", s);
#else
fflush(stdout);
if (fgets(s,sizeof(s),stdin) == NULL)
s[0] = 0;
s[strcspn(s,"\n")] = 0;
#endif
// last in first out, so in reverse g is last but first to come out
for (i = strlen(s) - 1; i > -1; i--)
push(&a, s[i]);
printf("\nConverting string to linked list: \n");
printList(a);
// printf("%c",current->data); prints first letter of a
if (opt_a)
remove_adjacent_duplicates(&a);
else
remove_all_duplicates(&a);
printList(a);
return 0;
}
UPDATE:
As for your code I can't understand it properly especially those # statements since I'm just an average coder in C with no advanced knowledge. –
New
The # lines aren't really advanced coding. They are C preprocessor (i.e. cpp) directives, similar to #define, #ifdef, #ifndef, and #endif. See the compiler manpage and/or man cpp.
They include/eliminate code at compile time in a separate first stage of the compilation process (i.e. the cpp stage).
Otherwise, the code is well commented to explain the intent of what the code is doing.
Side note: When I was first learning to code, in addition to school assignments, I was looking at some complex OS kernel code [in assembly language]. I just kept going over it, sometimes adding my own comments, until I did understand it. I learned more by reading and understanding such code than I did from most assignments.
At the bottom of my answer: What is the error in this code that checks if the linklist is a palindrome or not? is a list of resources I recommend.
Here is a cleaned up version of the my code above that eliminates the conditional cpp directives:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// node definition
struct node {
char data;
struct node *next;
};
void
remove_adjacent_duplicates(struct node **head_ref)
{
struct node *prev = *head_ref;
struct node *cur;
struct node *next;
// NOTE: first node can _never_ be a dup
if (prev != NULL)
cur = prev->next;
else
cur = NULL;
for (; cur != NULL; cur = next) {
// remember this in case we remove cur to prevent "use after free" bug
// when advancing cur
next = cur->next;
// remove duplicate
if (cur->data == prev->data) {
prev->next = next;
free(cur);
continue;
}
// point to last non-dup node
prev = cur;
}
}
void
remove_all_duplicates(struct node **head_ref)
{
struct node *oldhead = *head_ref;
struct node *addprev = NULL;
struct node *addnext;
// loop through all candidate nodes
for (struct node *addcur = oldhead; addcur != NULL; addcur = addnext) {
int dupflg = 0;
// start of search for duplicates to the right of the candidate
struct node *prevdup = NULL;
struct node *dupcur = addcur->next;
// find all duplicates to the right [towards tail] of candidate node
while (1) {
// find first duplicate to the right of candidate [if one exists]
struct node *dupnext = NULL;
for (; dupcur != NULL; dupcur = dupnext) {
dupnext = dupcur->next;
if (dupcur->data == addcur->data) {
dupflg = 1;
break;
}
prevdup = dupcur;
}
// no more duplicates to the right of current candidate
if (dupcur == NULL)
break;
// remove a duplicate on the right
if (prevdup != NULL)
prevdup->next = dupnext;
else
addcur->next = dupnext;
free(dupcur);
}
addnext = addcur->next;
// remove candidate because it's a dup
if (dupflg) {
if (addprev != NULL)
addprev->next = addnext;
else
oldhead = addnext;
free(addcur);
continue;
}
// remember last valid non-dup node
addprev = addcur;
}
*head_ref = oldhead;
}
void
push(struct node **head_ref, char new_data)
{
struct node *new_node = malloc(sizeof(*new_node));
new_node->data = new_data;
new_node->next = *head_ref;
*head_ref = new_node;
}
void
printList(struct node *head)
{
if (head == NULL) {
printf("NULL\n\n");
return;
}
printf("%c->", head->data);
printList(head->next);
}
int
main(int argc,char **argv)
{
char s[100];
int opt_a = 0;
int i;
struct node *a = NULL;
--argc;
++argv;
for (; argc > 0; --argc, ++argv) {
char *cp = *argv;
if (*cp != '-')
break;
switch (cp[1]) {
case 'a':
opt_a = ! opt_a;
break;
}
}
printf("Enter string: ");
fflush(stdout);
if (fgets(s,sizeof(s),stdin) == NULL)
s[0] = 0;
s[strcspn(s,"\n")] = 0;
// last in first out, so in reverse g is last but first to come out
for (i = strlen(s) - 1; i > -1; i--)
push(&a, s[i]);
printf("\nConverting string to linked list: \n");
printList(a);
// printf("%c",current->data); prints first letter of a
if (opt_a)
remove_adjacent_duplicates(&a);
else
remove_all_duplicates(&a);
printList(a);
return 0;
}

Find value occurrences and delete nodes in linked list

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;
}
}

search and delete a node from linked list

I am writing a simple dictionary program using linked list. I want to search a word in the dictionary and delete it. I've wrote the code, but I think It's more time consuming as I'm running the loop two times, 1st to search the node and note down the position and 2nd to delete it.
struct node{
char word[20];
char meaning[5][100];
struct node *next;
};
void del(struct node *head, char *word)
{
int found = 0, position = 0, i;
struct node *temp = head;
while(temp != NULL)
{
if(strcmp(temp->word, word) == 0)
{
found = 1;
break;
}
temp = temp->next;
position++;
}
if(found == 1)
{
temp = head;
if(position == 0)
{
head = temp->next;
free(temp);
}
for(i = 0; i < position-1; i++)
temp = temp->next;
struct node *temp2 = temp->next;
temp->next = temp2->next;
free(temp2);
printf("Word deleted..\n");
}
else printf("Word not found!\n");
}
is there any alternate way to optimize the program?
You just have to merge the two cycles together like this, here's a code example.
struct node{
char word[20];
char meaning[5][100];
struct node *next;
};
struct node *del(struct node *head, char *word)
{int found = 0, position = 0, i;
struct node *temp = head;
struct node *prev = NULL;
/*You should avoid breaks because they decrease legibility*/
while(temp != NULL)
{
if(strcmp(temp->word, word) == 0)
{
if(prev == NULL){ /*If the node is the head*/
head = head->next;
free(temp);
return head;
}else{
prev->next = temp->next;
free(temp);
return head;
}
}
prev = temp;
temp = temp->next;
}
}

Search for an element in a LinkedList

I'm currently creating a linked list of strings that user enters. As of right now I have my linked list working (I just have to free the memory). However, I'm trying to detect for commas in the user input. If there is a comma make the linked list print out a new line, and just ignore the commas.
Any advice?
For example:
Enter a string:
hello,world,how,are,you
The output is currently:
hello,world,how,are,you
The output should be:
hello
world
how
are
you
Here's my current code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
struct Word
{
char* data;
struct Word* next;
};
struct Word* ptr = NULL;
void insert(char c)
{
struct Word* temp = (struct Word*)malloc(sizeof(struct Word));
temp->data = c;
temp->next = NULL;
if (ptr) {
struct Word* temp1 = ptr;
while(temp1->next != NULL) {
temp1 = temp1->next;
}
temp1->next = temp;
} else {
ptr = temp;
}
}
void print() {
struct Word *temp;
temp = ptr;
while(temp != NULL) {
printf("%c", temp->data);
temp = temp->next;
}
printf("\n");
}
int main(int argc, char *argv[])
{
int c;
printf("enter a string\n");
while (((c=getchar())!=EOF) && c!='\n') {
insert((char)c);
}
print(); /*print the list*/
return 0;
}
To print every word in the new line you just have to modify your print statement to check for , character in the linked list.
void print() {
struct Word *temp;
temp = ptr;
char c;
while(temp != NULL) {
if (temp->data == ',') {
printf("\n");
temp = temp->next;
} else {
printf("%c", temp->data);
temp = temp->next;
}
}
printf("\n");
}
This will check if there is a , in the linked list and print a \n to print newline character and move to the next node.
Also you should free your linked list after the program is complete to avoid memory leaks.
void freeData(struct Word* head)
{
struct Word* tmp;
while (head != NULL)
{
tmp = head;
head = head->next;
free(tmp);
}
}
Code link
Just try it out.

Insertion Sort functions doesn't list all the elements after sorting

This is my insertion sort function :
Student *sort(Student* node)
{
if (node == NULL || !node->next)
return node;
Student *sorted = NULL;
while (node != NULL)
{
Student *head = node;
Student **tail = &sorted;
node = node->next;
while (!(*tail == NULL || head->id < (*tail)->id))
{
tail = &(*tail)->next;
}
head->next = *tail;
*tail = head;
}
return sorted;
}
So if this is supposed to sort 3.3, 3.1 and 3.8 , it sorts them such as :
3.3
3.8
I don't know what happens to the first element. If I give it a larger set of text to sort, it misses almost half of the text.
Been trying to figure out why it does this. I'm pretty sure it's the problem with my sorting function.
write function. This function is supposed to simply write the sorted result to a file :
void write(Student* node) {
Student * curr = NULL;
curr = node;
FILE *ptr = fopen("student_out.txt", "w");
if (curr == NULL)
{
printf("Nothing to list. \n");
exit(1);
}
int i=0;
while(curr !=NULL) {
fprintf(ptr,"%d, %s, %s, %s, %.2f\n", curr->id, curr->firstname, curr->lastname, curr->major, curr->gpa);
curr = curr -> next;
}
return;
}
Seems to be working, the problem could be with the print out. Example code to test the case mentioned above, converted to be C89 compatible (since I'm using Visual Studio). I would have swapped the names head and node, but I left them as is to match the original example. I also changed the compare to stop when (*tail)->id > head->id (using <= in the compare instead of <) so that the original order of "equal" nodes is preserved.
#include <stdio.h>
typedef struct Student_{
struct Student_ *next;
double id;
}Student;
Student *sort(Student* node)
{
Student *sorted = NULL;
Student **tail;
Student *head;
if (node == NULL || !node->next)
return node;
while (node != NULL)
{
head = node;
node = node->next;
tail = &sorted;
while (*tail != NULL && (*tail)->id <= head->id)
tail = &(*tail)->next;
head->next = *tail;
*tail = head;
}
return sorted;
}
int main()
{
Student a[3] = {{&a[1],3.3},{&a[2],3.1},{NULL,3.8}};
Student *b;
b = sort(a);
while(b){
printf("%3.1lf ", b->id);
b = b->next;
}
printf("\n");
return 0;
}

Resources