Struct - remove one of the elements - c

Let's say I have the following struct:
struct object {
char *name;
struct object *next;
};
Using that I can easily create a table as a described in K&R C book. And remove all the objects by that:
object *object_destroy(object *obj)
{
if (obj != NULL) {
object_destroy(obj->next);
free(obj->name);
free(obj);
obj = NULL;
}
}
But what I want is to remove one of the objects in table, saving all the others. I really have no idea how to implement that, because just free() that object is won't work: the objects after it will be permamently lost. I can't obj_to_remove = obj_to_remove->next too, because after that I will lose all the objects before that object.
So can you point me out what I am missing? Thanks.

You are effectively attempting to delete a single node from a singly linked list (as opposed to a doubly-linked-list, or a circular list, also known as a ring buffer).
Please refer to the references below for more information on the concept of a singly linked list. In short, you:
Start at the first node in the list (the head node).
Cycle through the list one node at a time, keeping a pointer to the previously accessed node, until you find the node you want to delete.
Point the previous node in the list to the one ahead of the current/found node.
3.1. If there is no node afterwards (we are at the end of the list), set to NULL.
3.2. If the head/first node was the one found, set the head node to the second node in the list.
Delete the elements inside the node if the node has been found.
Delete the current node itself.
The second reference will be very helpful to you, as it covers building a list from scratch, adding items to the end of the list (appending), inserting items at an arbitrary point in the list, deleting individual items, and clearing out the whole list.
References
Deleting from a Linked List, Accessed 2014-04-22, <https://www.cs.bu.edu/teaching/c/linked-list/delete/>
Singly linked list - insert, remove, add, count source code, Accessed 2014-04-22, <http://www.cprogramming.com/snippets/source-code/singly-linked-list-insert-remove-add-count>

You have the following 1->2->3->4->5 and you want to remove the element 3. The final result will be 1->2->4->5.
To do this you just need to do the following steps:
1- If the element is in the middle of the list:
Traverse the table, for each element you save previous element and the element itself.
When the element equals the one you want to delete you have cur_el = 3 and prev_el = 2. (cur_el and prev_el are pointers to the elements 3 and 2)
Now just make prev_el->next = cur_el->next (don't forget to keep a pointer to the prev_el.
Finally just free the cur_el.
2- If the element is the first of the list (let's say you want to delete 1):
- Set the first list element as cur_el->next (here cur_el points to 1, the first element in the list)
free the cur_el
3- If the element is in the end of the list (in this case 5):
- go to the last element, get the penultimate element (let's call the pointer to this one prev_el)
set prev_el->next = null
free cur_el
Also, notice that removing an element from the list has a complexity of O(n), where n is the number of elements of the list.
If you just insert and remove from the begin or from the end you can reach a O(1).

In the question, the memory structure is referred to as a 'table'. Perhaps it would be better to call it a 'linked-list'.
int object_destroy(
object **head, /* The head of the linked-list is required in order to maintain proper list nodes linkage. */
object *obj
)
{
int rCode=0;
/* Unlink the node to be destroyed. */
if(*head == obj) /* Is the obj to destroy the head list node? */
*head = obj->next;
else
{
object *temp = *head;
/* Find the parent list node (to the one that will be destroyed). */
while(temp)
{
if(temp->next == obj)
break;
temp=temp->next;
}
/* Not found? */
if(NULL == temp)
{
rCode=ENOENT;
fprintf(stderr, "obj node not found in list\n");
goto CLEANUP;
}
/* Unlink the node, and patch the list so that remaining nodes are not lost. */
temp->next = temp->next->next;
}
/* Free the node to be destroyed. */
if(obj->name)
free(obj->name);
free(obj);
CLEANUP:
return(rCode);
}

Related

Deleting a structure from a doubly linked list based on its contents (in C)

My program is a basic C interface that allows the user to enter, print forwards, print backwards and delete MP3 records from a list. The list is implemented as a doubly linked list of MP3 structures in the C language.
All my functions except Delete work fine. Delete takes in a pointer to the head node of the list and a character string signifying which artist's records you would like deleted. In my main I record the user input and have verified it is being recorded correctly. Then I pass the user input and the head reference into the following function to delete said MP3 records. However, my program builds and executes fine but does not actually delete any records when the delete function is called. Any help is appreciated.
For clarity, I have looked at multiple questions on stack regarding deleting nodes from a DLL, however they all are regarding simple DLL holding integer values and don't seem to translate well to my scenario. I apologize if this is considered a duplicate question, if so, please point me to the question I am duplicating. Thanks again for any and all assistance. Below is my function
void deleteMP3(struct MP3* head_ref, char* artist)
{
//Declaring a temp struct to hold the node that needs to be deleted
struct MP3* temp;
//Check if the head node contains the artist to be deleted
if(head_ref->artist == artist)
{
//Set temp to the current head ref so it can be deleted
temp = head_ref;
//Set head_ref to the next node in the list
head_ref = head_ref->next;
//Free the memory associated with the MP3 to be deleted
free(temp->artist);
free(temp->title);
free(temp->date);
free(temp);
}
//Traverse the list checking each MP3's artist field
while(head_ref != NULL)
{
//Check the artist of the current MP3 against the input. Delete it if it needs to be deleted
if(head_ref->artist == artist)
{
//Set temp to the current MP3
temp = head_ref;
//Check if the MP3 is the last MP3. If not, change the field of the next node in the list
if(head_ref->next != NULL)
{
//Sets the previous field of the next node in the list to the previous field of the node to be deleted
head_ref->next->prev = head_ref->prev;
}
//Change the next pointer of the previous MP3 in the list to the MP3 following the one to be deleted
head_ref->prev->next = head_ref->next;
//Free the memory
free(temp->artist);
free(temp->title);
free(temp->date);
free(temp);
}
//Traverse forward
head_ref = head_ref->next;
}
}
There are some issues in the code, but the following two are the most critical ones:
1.) use strcmp to compare strings. head_ref->artist == artist compares the pointers, not the contents, and it's usually unlikely that you pass in the same pointer to which the DLL elements point to.
2.) if the head is deleted, you need to pass back the "new" head to the caller of deleteMP3; otherwise the variable passed to deleteMP3 will still hold a pointer to the (deleted) node. So change void deleteMP3(struct MP3* head_ref, char* artist) to struct MP3 *deleteMP3(struct MP3* head_ref, char* artist) and return the actual head of the DLL (whether changed or not).

Check if pointer is assigned

I am coding a spell checker which loads a dictionary into memeory and checks a given text for misspelled words. To implement this i would like to use a hash table. To handle collisions i'll use linked lists. Every word from the dictionary gets added to the beginning of the linked list of the corresponding hash.
I created an array of pointers to a struct called node. Here's my code for this:
typedef struct node
{
char word[LENGHT + 1];
struct node* next;
}
node;
node* table[HASHTABLE_SIZE];
My question is: Is it possible to check if table[x] is already pointing to a node in order to know if node.next should point to the rest of a linked list or should be NULL because its the first element in the linked list?
Since you are creating a hash table of size HASH_TABLE_SIZE which means there are HASH_TABLE_SIZE no. of liked list.Initially all linked list's head will be pointing to NULL. Inorder to know whether the table at index x is already having some element you just need to check whether the head at index x in the hash table in NULL or not.
if(table[x])
//head is already created for the linked list having x as index
else
//head is NULL append the first node in this linked list at index x.And make this node new head
if (table[0] == NULL)
// Create memory & allocate everything
Note that you can also do
if (table[0]->next == NULL)
// assign next
Or even in C
if (!table[0]) // If table[0] does not exist

how to check if a linked list is empty

I'm new to C and have a question.
How would I check if a linked list is empty?
I have a struct _node
typedef struct _node{
int data;
struct _node *next;
}node;
If I have initialized node *list, but didn't do anything to it (i.e didn't assign list->data a value), how would I check if it's empty?
I tried if (node == NULL){break} but didn't work.
Thanks for the help!
Intro:
There are usually two ways to use linked lists: with a root element and without.
Without a root, your list pointer is NULL when the list is empty:
node *list;
...
if (list == NULL) { /* empty list */ }
With root, there is always one element. But it can be used in two ways:
Either simply to provide a pointer to the first element.
node *root;
...
if (root->next == NULL) { /* empty list */ }
Or to have the last element link back to the root to form a cycle. This concept has the advantage that the "next" element is never NULL and you thus don't need to check for it. In this case, the list is empty if the root points back to itself.
node *root;
...
if (root->next == root) { /* empty list */ }
Answer:
Now, according to your description, you have allocated a node. This either means you want the "root" approach (second or third example). But if you want to go with the first variant, you must not allocate the node, since it doesn't hold data.
For the "root" approach, you indeed have one (and only one) node that doesn't hold data. But for simple linked lists, all nodes must contain data.
I'd keep it simple.
Check the head pointer - if it is NULL, there's no entry in the list.
int isEmpty( node * list )
{
if( !list )
return 1;
return 0;
}
Sometimes when you create a variable, C will zero out all the data for you. Other times, the variable will contain garbage. You need to learn the rules that govern this, but as a beginner, you should simply be certain always to initialize your variables explicitly.
For a linked list, when you append a node to a list, you should always be certain to set a NULL into the next pointer, so that the list will be properly terminated.
Your linked list should have a "head" pointer. If the head is set to NULL then you have a zero-length linked list; if the head has any value other than NULL you must have at least one node in the linked list, so the list must have length of at least 1.
So, as long as you have been careful about initializing your head pointer to NULL you can trivially tell whether the list is empty by checking to see if the head pointer is NULL.
But your question was really about the data member variable. There will always be some sort of value in there. As I said, sometimes C will set it to zero for you, but other times it will be unpredictable garbage (a value that could be any integer value). You need to initialize that to a sensible default when you allocate a new node. For example, the valid values of data might all be greater than zero, so you might set data to zero to indicate that it is currently empty.
You can write a simple loop that traverses your linked list and checks each data member, and return true if it finds any data that is not zero (or whatever special value flags that it is unused). It should return false if it finds only default data or if the list is zero length.
Its better to have a header node when building your linked list. Then you can add nodes to build the list. Let that list variable be your header node. Then to check if your linked list is empty, use this:
typedef struct _node{
int data;
struct _node *next;
}*node;
and then instantiate
node list;
and to check the linked list is empty use
if((list->next)==null)
{
break;
}

More than 1 linked list - Adding elements

I need to make a program that has (at most) 50 linked lists. Basically, my program generates some messages and based on a indicator that comes in the front of my string, I need to put the message in the right linked list.
I don't know if it is clear enough but I will try to show part of my code (the important part). The function I made to add a new element (on the top) of the linked list is the following:
void InsertLL (News p, char M[]) {
char * text = malloc(strlen(M)+1);
strcpy(text, M);
News s,t;
t = malloc(sizeof(struct List));
t-> Text = text;
s = p;
p = t;
p-> next = s;
}
My struct List (the type of the elements of my lists) contains a char pointer (called text) and a pointer to the next element of the list.
Simulating my program, suppose that I received a message that needs to be put in the linked list where the begin is pointed by the pointer p[0]. So I create a new element (forget the case that the list is empty, I already made this one) and add in the top of my list using the function I've shown.
Now, suppose that I received another message that needs to be put in the next pointer p[1]. If I print p[0] -> Text, I get the text of p[1]->Text.
I mean, if I add a new element in the list pointed by p[i], all the previous texts p[i] -> Texts gets the new text of this new element. I have no idea what am I doing wrong.
I don't know if it is enough to help me, if more information is needed, just tell me.
Problem with your code is you are not maintaining the list of nodes. you are overwriting the same node again and again.
Algo:
If no node then create one.
If nodes are present then navigate to end node.(You can use tail
pointer for quicker access).
Append the node at the end.
Creating Node:
Declare a node type pointer.
Allocate memory to it.
Update the content of the node.
Set the next pointer of the node to null.
Add this node to end of the list.
ex. inserting node 3 in the list of node 1 and node 2.
This is the general approach that you can use
typedef struct node{
int val; //you can use your text here
struct node* next;
}NODE;
struct node* head=0;
int addNode(int v){
if(head==0){ //checking for empty node.
struct node* n=malloc(sizeof(NODE));
n->val=v;
head=n;
}
else{
struct node* temp=head;
while(temp->next != 0) //Navigating till end
{
temp=temp->next;
}
struct node* n=malloc(sizeof(NODE)); //allocating memory
n->val=v; //you need to use strcpy for string here.
temp->next=n; //adjusting pointers
n->next=0;
}
}
You can check this demo created by me for Double linked List http://ideone.com/s6TtUX

Copy a linked list

typedef struct Node
{
int data;
Node *next;
Node *other;
};
Node *pHead;
pHead is a singly linked list. The next field points to the next element in the list. The other field may point to any other element (could be one of the previous nodes or one of the nodes ahead) in the list or NULL.
How does one write a copy function that duplicates the linked list and its connectivity? None of the elements (next and other) in the new list should point to any element in the old list.
Create a new node for every node in the old list, copy the corresponding data and make the next pointer of the nodes in the new list point to their successor in the new list, forgetting the other pointer for time being. At the time of creating a new node remember the mapping of node address something like:
Old_list New_list
-------------------
0x123 0x345 [ addresses of the first node]
0xabc 0xdef [ addresses of the second node]
...
In the second pass pass for every node in the new list consider its other pointer and find its corresponding node in the new list from the map and use it as the other pointer of this node (node in the new list).
Came across this. Hope it helps!
Citing one solution from this link, below.
1) Create the copy of 1 and insert it between 1 & 2, create the copy of 2 and insert it between 2 & 3.. Continue in this fashion, add the copy of N to Nth node
2) Now copy the arbitrary link in this fashion
if original->arbitrary is not NULL
original->next->arbitrary = original->arbitrary->next; /*TRAVERSE TWO NODES*/
else
original->next->arbitrary=NULL;
This works because original->next is nothing but copy of original and Original->arbitrary->next is nothing but copy of arbitrary.
3) Now restore the original and copy linked lists in this fashion in a single loop.
original->next = original->next->next;
copy->next = copy->next->next;
4) Make sure that last element of original->next is NULL.
Sample code, Time Complexity O(N), Space Complexity O(1)
pNode copy_list(pNode head) {
// pre-condition: node->other either points into the list or NULL
if (!head) return NULL;
pNode node = head, copied = NULL, cnode = NULL;
for ( ; node; node = node->next->next) {
// make copy
cnode = newnode(node->next, node->data);
cnode->other = node->other;
if (node == head)
copied = cnode;
// insert the copy between originals
node->next = cnode;
// node -> cnode -> (orig)node->next
}
for (node = head; node && node->next;
node = node->next->next /* only original nodes */)
if (node->other)
node->next->other = node->other->next;
else
node->next->other = NULL;
// restore lists
node = head; cnode = copied;
for ( ; cnode && cnode->next; node = node->next, cnode = cnode->next) {
node->next = node->next->next;
cnode->next = cnode->next->next;
}
node->next = NULL;
return copied;
}
Complete program is at http://gist.github.com/349630
I like the solution of Codaddict, but this would be my answer:
iterate over the linked list.
a. store the data in an array (position i for the i'th node of course)
b. replace data with i to create an id (this way you'll definitely know which node you are talking about)
create the 2nd linked list the size of the first (ignore the other pointer for now)
*. maybe use a temporary array to find each node quickly
iterate over the first linked list.
a. find out which id other points to (which is in that nodes data)
b. recreate this link in the 2nd linked list (the temporary array could really help here)
iterate over both linked list simultaneously and replace the ids in data with the stored data
Of course you could collapse some processing and iterating here. But this would roughly be what I would do/think of.

Resources