How does head connected to tail when deleting the list - c

I have a function which creates a list based on a given array,
this is the function:
typedef struct Item
{
int num;
struct Item* next;
}*PItem;
int main()
{
int Arr[N] = { 3, 4, 1, 0, 8 }, i;
PItem list = NULL, tail = NULL;
CreateListFromArray(&list, &tail, Arr);
}
void CreateListFromArray(PItem* head, PItem* tail, int *Arr)
{
int i;
PItem temp;
for (i = 0; i<N; i++)
{
temp = (PItem)malloc(sizeof(struct Item));
if (temp == NULL)
{
DeleteList(head);
Error_Msg("Memmory!");
}
temp->num = Arr[i];
temp->next = NULL;
if (*head == NULL)
*head = temp;
else
(*tail)->next = temp;
*tail = temp;
}
}
I understand that if List is empty, then head's null is initialized to the first allocated temp (arr[0]). But after that, for these arrays arr[1],..,arr[N], I update only the tail, meaning that all the tails from arr[1] to arr[N] are connected. but how does the head (arr[0]) POINTS/connected to arr[1]?
I ask this because, when I try to print the list, I use temp = head, and advance head until temp is null, but when I advance head, how does it know that it has to advance to arr[1]?
Here's the full code: http://pastebin.com/VPCfMU4X

After the first iteration of the loop, head and tail point to the same element which contains arr[0]. After the second iteration, (*tail)->next (which is the same as (*head)->next) point to the new element that contains arr[1], and tail is moved up to this value. Subsequent iterations keep appending to the end of the list.
So after one iteration, you have this:
head tail
| |
v v
---------------
| 3 | NULL |
---------------
After the second iteration, you have this:
head tail
| |
v v
--------------- ---------------
| 3 | .-----|--->| 4 | NULL |
--------------- ---------------
And the third:
head tail
| |
v v
--------------- --------------- ---------------
| 3 | .-----|--->| 4 | .-----|--->| 1 | NULL |
--------------- --------------- ---------------

Related

Problem with implementing Delete at Position function in Doubly Linked Lists

I'm trying to code out the functions for Doubly Linked Lists and am running into problems implementing the Delete at Position function.
It works as expected for the deletion of nodes in the first and last positions.
However, for the deletion of intermediate positions, it deletes it as expected the first time (at least on the surface, that is how it appears), the next time, however, it enters a garbage value for the intermediate position.
My Delete at Position function:
void delete_pos(struct node **p, int pos)
{
struct node *q;
q=*p;
int i=1;
while((q->next!=NULL)&&(i<pos))
{
q=q->next;
i++;
}
if(q==NULL) //empty list or invalid position
printf("Invalid Position\n");
else //position found
{
if(q->prev==NULL) //1st position
{
if(q->next==NULL) //only 1 node
*p=NULL;
else //more than 1 node
{
q->next->prev=NULL;
*p=q->next;
}
}
else if(q->next!=NULL) //intermediate position
{
printf("Yes\n"); //debugging check to make sure that it's going to the right condition
q->prev->next=q->next;
}
else //last position
q->prev->next=NULL; //
free(q);
}
}
Example of the problem I have with the output:
1<->2<->3<->4<->5<->NULL
is my Doubly Linked List.
If I call Delete at Position function and enter an intermediate value:
Enter position
2
1<->3<->4<->5<->NULL
It appears to works as expected the first time.
However, the next time I call:
Enter the position..
2
1<->6564704<->4<->5<->NULL
In another attempt it behaved weirdly as it didn't produce garbage value, it just didn't work the second time, the output:
1<->3<->4<->5<->NULL
Enter the position..
2
1<->4<->5<->NULL
Enter the position..
2
1<->4<->5<->NULL
Enter the position..
2
1<->4<->5<->NULL
My debugging attempt:
Through a print statement, I made sure that its' going into the intermediate condition :
else if(q->next!=NULL) //intermediate position
{
printf("Yes\n"); //debugging check to make sure that it's going to the right condition
q->prev->next=q->next;
}
And it was, however I cannot figure how the statement:
q->prev->next=q->next;
is wrong for intermediate position node deletion in a doubly linked list, as that is the only thing that stands out that could be going wrong.
Thank you for your time for reading this!
Whenever deleting from Linked Lists, always start from right and move towards left.
Suppose here p is the node you want to delete, then assume your list as
1-2-3-4-5
Now suppose you want to delete the node 3 then let p be that node.
Now do,
p-> right{4} -> left{3 at the moment} = p -> left{2 at the moment}
p-> left{2} -> right{3 at the moment} = p-> right{4}
p->left = NULL
p->right = NULL
With this your deletion will be done, you can additionally free the memory of p if you want to.
This might seem tricky at first so I recommend you to try it on paper, follow the 3 steps above and your deletion will work.
When deleting an intermediate node you do
q->prev->next=q->next;
which means that you make the node before q point to the node after q.
That is fine but since it is a doubly linked list you also need to make the node after q point to the node before q.
Something like:
q->prev->next=q->next;
q->next->prev=q->prev;
Further explanation:
If we draw your list like:
---------- ---------- ----------
| | | | | |
| next|------>| next|------>| next|
| NB | | Q | | NA |
|prev |<------|prev |<------|prev |
| | | | | |
---------- ---------- ----------
then you do
q->prev->next=q->next;
which change the list into
--------------
---------- / ---------- \ ----------
| | / | | \-->| |
| next|--/ | next|------>| next|
| NB | | Q | | NA |
|prev |<------|prev |<------|prev |
| | | | | |
---------- ---------- ----------
But that is only half of the pointers. You also need
q->next->prev=q->prev;
to get
--------------
---------- / ---------- \ ----------
| | / | | \-->| |
| next|--/ | next|------>| next|
| NB | | Q | | NA |
|prev |<------|prev | ---|prev |
| |<-\ | | / | |
---------- \ ---------- / ----------
---------------
Now you can delete Q to get
--------------
---------- / \ ----------
| | / \-->| |
| next|--/ | next|
| NB | | NA |
|prev | ---|prev |
| |<-\ / | |
---------- \ / ----------
---------------
The function invokes undefined behavior when initially the expression *p is a null pointer that is when the function deals with an empty list due to accessing memory using this null pointer
void delete_pos(struct node **p, int pos)
{
struct node *q;
q=*p;
int i=1;
while((q->next!=NULL)&&(i<pos))
^^^^^^^^
//...
You need to check whether the list is empty in the very beginning of the function before the while loop.
After the while loop
while((q->next!=NULL)&&(i<pos))
{
q=q->next;
i++;
}
if the list initially is not empty when the pointer q can not be equal to NULL because the next value is assigned to the pointer only when the condition q->next!=NULL is true. That means that the user can specify a non-existent position (for example a too big position) but the last node will be in any case deleted. Or the user of the function can pass a negative number as a value of the position and in this case the first node will be deleted.
The parameter that specifies the position should be declared as having an unsigned integer type for example size_t and positions should start from zero.
The function should report whether a node in a given position was successfully deleted. That is the function should be declared like
int delete_pos( struct node **p, size_t pos );
In case when the deleted node is not the head or tail node then you forgot to update the data member prev of the next node after the deleted node.
else if(q->next!=NULL) //intermediate position
{
printf("Yes\n"); //debugging check to make sure that it's going to the right condition
q->prev->next=q->next;
}
You updated only the data member next of the node before the deleted node.
The function can look the following way
int delete_pos( struct node **head, size_t pos )
{
while ( *head && pos-- )
{
head = &( *head )->next;
}
int success = *head != NULL;
if ( success )
{
struct node *current = *head;
if ( ( *head )->next != NULL )
{
( *head )->next->prev = ( *head )->prev;
}
*head = ( *head )->next;
free( current );
}
return success;
}
Here is a demonstrative program that shows the function in action.
#include <stdio.h>
#include <stdlib.h>
struct node
{
int data;
struct node *prev;
struct node *next;
};
size_t create( struct node **head, const int a[], size_t n )
{
while ( *head )
{
struct node *current = *head;
*head = ( *head )->next;
free( current );
}
size_t i = 0;
for ( struct node *prev = NULL;
i != n && ( *head = malloc( sizeof( **head ) ) ) != NULL;
i++ )
{
( *head )->data = a[i];
( *head )->prev = prev;
( *head )->next = NULL;
prev = *head;
head = &( *head )->next;
}
return i;
}
void display( const struct node *head )
{
const struct node *tail = NULL;
for ( ; head != NULL; head = head->next )
{
printf( "%d -> ", head->data );
tail = head;
}
puts( "null" );
for ( ; tail != NULL; tail = tail->prev )
{
printf( "%d -> ", tail->data );
}
puts( "null" );
}
int delete_pos( struct node **head, size_t pos )
{
while ( *head && pos-- )
{
head = &( *head )->next;
}
int success = *head != NULL;
if ( success )
{
struct node *current = *head;
if ( ( *head )->next != NULL )
{
( *head )->next->prev = ( *head )->prev;
}
*head = ( *head )->next;
free( current );
}
return success;
}
int main(void)
{
struct node *head = NULL;
int a[] = { 1, 2, 3, 4, 5 };
create( &head, a, sizeof( a ) / sizeof( *a ) );
display( head );
putchar( '\n' );
delete_pos( &head, 0 );
display( head );
putchar( '\n' );
delete_pos( &head, 3 );
display( head );
putchar( '\n' );
delete_pos( &head, 1 );
display( head );
putchar( '\n' );
delete_pos( &head, 1 );
display( head );
putchar( '\n' );
delete_pos( &head, 0 );
display( head );
putchar( '\n' );
return 0;
}
The program output is
1 -> 2 -> 3 -> 4 -> 5 -> null
5 -> 4 -> 3 -> 2 -> 1 -> null
2 -> 3 -> 4 -> 5 -> null
5 -> 4 -> 3 -> 2 -> null
2 -> 3 -> 4 -> null
4 -> 3 -> 2 -> null
2 -> 4 -> null
4 -> 2 -> null
2 -> null
2 -> null
null
null

Realloc in a 2d array of pointers

I am trying to create a dynamic array of simple linked lists. The MAX_SIZE is 5. I would like to insert
a new node, for example the character 'c', at (link[5]). For every new character, i would like to allocate a new entry at the bottom of the array.
When i execute my code, I get this output above. I think there is a problem with realloc
[0] a b NULL
[1] c NULL
[2] f e d NULL
[3] NULL
[4] NULL
but the expected output is below:
[0] a b NULL
[1] c NULL
[2] f e d NULL
[3] NULL
[4] NULL
[5] c
My code is here
#include <stdio.h>
#include <stdlib.h>
typedef struct Node
{
char data;
struct Node *next;
} Node;
int push_front( Node **head, char data )
{
Node *new_node = malloc( sizeof( Node ) );
int success = new_node != NULL;
if ( success )
{
new_node->data = data;
new_node->next = *head;
*head = new_node;
}
return success;
}
void output( Node **head )
{
for( Node *current =*head; current != NULL && current->data != -1 ; current = current->next )
{
printf( "%c ", current->data );
}
printf( "%s", "NULL" );
}
void display( Node **set, int n )
{
for (int i = 0; i < n; i++){
printf("[%d] ", i);
output(&(set[i]));
printf("\n");
}
}
#define N 5
int main(void)
{
//Node * link[N] = { 0 };
Node **link;
link = calloc(N , sizeof (Node*));
for (int i = 0; i < N; i++){
link[i] = calloc(N , sizeof (Node));
link[i]->data = -1;
}
push_front( &link[0], 'b' );
push_front( &link[0], 'a' );
push_front( &link[1], 'c' );
push_front( &link[2], 'd' );
push_front( &link[2], 'e' );
push_front( &link[2], 'f' );
link = (Node **) realloc(link, (N + 1) * sizeof(Node*));
for(int i = 0; i < N + 1; i++){
link[i] = (Node *)realloc(link[i],(N + 1) * sizeof(Node));
}
push_front( &link[5], 'c' );
display(link, N);
return 0;
}
OP is doing some serious mistake while allocating memory to list and the accepted answer is not pointing out those mistakes. Hence, I am posting this answer.
I am trying to create a dynamic array of simple linked lists....
The way you are allocating memory, initially, you are creating array of array's of Node type objects.
From calloc(): [emphasis added]
void* calloc( size_t num, size_t size );
Allocates memory for an array of num objects of size and initializes all bytes in the allocated storage to zero.
So, this loop:
for (int i = 0; i < N; i++){
link[i] = calloc(N , sizeof (Node));
link[i]->data = -1;
}
will end up allocating memory for an array of N objects of size of Node type to link[i] pointer, where value of i lies in [0, 5).
The in-memory view would be something like this:
(read `n` as `next` pointer
and `:` is separator between `data` and `next` member of a node)
---- ----------------------
link[0] | | -> |-1:n|0:n|0:n|0:n|0:n| <---- array of Node type objects
| | ----------------------
| |
---- ----------------------
link[1] | | -> |-1:n|0:n|0:n|0:n|0:n| <---- array of Node type objects
| | ----------------------
| |
---- ----------------------
link[2] | | -> |-1:n|0:n|0:n|0:n|0:n| <---- array of Node type objects
| | ----------------------
| |
---- ----------------------
link[3] | | -> |-1:n|0:n|0:n|0:n|0:n| <---- array of Node type objects
| | ----------------------
| |
---- ----------------------
link[4] | | -> |-1:n|0:n|0:n|0:n|0:n| <---- array of Node type objects
| | ----------------------
| |
----
After calling push_front() on link array elements, the in-memory view would be something like this:
(read `n` as `next` pointer
and `:` is separator between `data` and `next` member of a node)
---- ----- ----- ----------------------
link[0] | | -> |a:n| -> |b:n| -> |-1:n|0:n|0:n|0:n|0:n|
| | ----- ----- ----------------------
| |
---- ----- ----------------------
link[1] | | -> |c:n| -> |-1:n|0:n|0:n|0:n|0:n|
| | ----- ----------------------
| |
---- ----- ----- ----- ----------------------
link[2] | | -> |f:n| -> |e:n| -> |d:n| -> |-1:n|0:n|0:n|0:n|0:n|
| | ----- ----- ----- ----------------------
| |
---- ----------------------
link[3] | | -> |-1:n|0:n|0:n|0:n|0:n|
| | ----------------------
| |
---- ----------------------
link[4] | | -> |-1:n|0:n|0:n|0:n|0:n|
| | ----------------------
| |
----
Here:
link = (Node **) realloc(link, (N + 1) * sizeof(Node*));
the link pointer will be reallocated the memory of new size N + 1 (assuming realloc() was success).
and, this loop:
for(int i = 0; i < N + 1; i++){
link[i] = (Node *)realloc(link[i],(N + 1) * sizeof(Node));
}
will end up reallocating link[i] pointer to memory of size N + 1 objects of Node type. You don't need to realloc() the link[i] pointers. The link[i] pointers are head of their respective linked list.
Now, you must be thinking - why printing of list giving expected output?
Because the reallocation is done by either:
a) expanding or contracting the existing area pointed to by ptr, if possible. The contents of the area remain unchanged up to the lesser of the new and old sizes. If the area is expanded, the contents of the new part of the array are undefined.
b) allocating a new memory block of size new_size bytes, copying memory area with size equal the lesser of the new and the old sizes, and freeing the old block.
So, link[i] pointers were pointing to a memory of a Node type object and realloc() will maintain the content of this memory after reallocation.
After reallocation, the in-memory view would be something like this:
(read `n` as `next` pointer, x as some garbage value
and `:` is separator between `data` and `next` member of a node)
---- ------------------------- ----- ----------------------
link[0] | | -> |a:n|x:n|x:n|x:n|x:n|x:n| |-> |b:n| -> |-1:n|0:n|0:n|0:n|0:n|
| | ---|--------------------- | ----- ----------------------
| | \----------------------/
---- ------------------------- ----------------------
link[1] | | -> |c:n|x:n|x:n|x:n|x:n|x:n| |-> |-1:n|0:n|0:n|0:n|0:n|
| | ---|--------------------- | ----------------------
| | \----------------------/
---- ------------------------- ----- ----- ----------------------
link[2] | | -> |f:n|x:n|x:n|x:n|x:n|x:n| |-> |e:n| -> |d:n| -> |-1:n|0:n|0:n|0:n|0:n|
| | ---|--------------------- | ----- ----- ----------------------
| | \----------------------/
---- --------------------------
link[3] | | -> |-1:n|0:n|0:n|0:n|0:n|x:n|
| | --------------------------
| |
---- --------------------------
link[4] | | -> |-1:n|0:n|0:n|0:n|0:n|x:n|
| | --------------------------
| |
---- -------------------------
link[5] | | -> |x:n|x:n|x:n|x:n|x:n|x:n|
| | -------------------------
| |
----
You can see the next pointers are preserved after reallocation and that's why when you are printing the list you are getting output as expected. I hope this giving you clear picture of mistakes you are making while allocating memory.
Now, after this statement
push_front( &link[5], 'c' );
the in-memory view of link[5] would be something like this:
---- ----- -------------------------
link[5] | | -> |c|n| -> |x:n|x:n|x:n|x:n|x:n|x:n|
| | ----- -------------------------
| |
----
Note that your output() may not work as expected while printing the link[5] list because the output() function for loop works on condition current != NULL && current->data != -1 and neither realloc() initialise memory to 0 (as the calloc() does) and nor you are assigning -1 to list[5]->data.
You don't need to allocate memory to link[i] pointers. The push_front() function is allocating the memory and creating a new node and adding that node in the array member list whose address passed as argument to push_front() function. Also, you should take care of freeing the list once done with them i.e. free the dynamically allocated memory before exiting.
Putting these altogether:
#include <stdio.h>
#include <stdlib.h>
#define N 5
typedef struct Node {
char data;
struct Node *next;
} Node;
int push_front (Node **head, char data) {
Node *new_node = malloc (sizeof (Node));
int success = new_node != NULL;
if (success) {
new_node->data = data;
new_node->next = *head;
*head = new_node;
}
return success;
}
void output (Node **head) {
for (Node *current = *head; current != NULL; current = current->next) {
printf ("%c ", current->data);
}
printf ("%s", "NULL");
}
void display (Node **set, int n) {
for (int i = 0; i < n; i++) {
printf ("[%d] ", i);
output (&(set[i]));
printf ("\n");
}
}
Node ** reallocate_list (Node **list, size_t old_size, size_t new_size) {
if (old_size == new_size) {
return list;
}
Node **temp = realloc (list, new_size * sizeof(Node*));
if (temp == NULL) {
printf ("Failed to allocate memory.\n");
/* do cleanup stuff and handle the allocation failure
* the way you want. I am simply exiting on allocation failure.
*/
exit (EXIT_FAILURE);
}
if (old_size < new_size) {
/* Initialise the newly added elements in the array
*/
for(size_t i = old_size; i < new_size; i++){
list[i] = NULL;
}
}
return temp;
}
void free_list (Node **head) {
Node *current = *head;
while (current) {
Node *x = current->next;
free (current);
current = x;
}
*head = NULL;
}
void free_all_list (Node **list, size_t num_of_lists) {
for (size_t i = 0; i < num_of_lists; i++) {
free_list (&list[i]);
}
}
int main (void) {
Node **link;
link = calloc(N , sizeof (Node*));
if (link == NULL) {
printf ("Failed to allocate memory.\n");
exit (EXIT_FAILURE);
}
printf ("link array size : %d\n", N);
/* handle the push_front() return value
*/
push_front (&link[0], 'b');
push_front (&link[0], 'a');
push_front (&link[1], 'c');
push_front (&link[2], 'd');
push_front (&link[2], 'e');
push_front (&link[2], 'f');
display (link, N);
printf ("Reallocating link array, new size : %d\n", N + 1);
link = reallocate_list (link, N, N + 1);
if (link == NULL) {
printf ("Failed to allocate memory.\n");
exit (EXIT_FAILURE);
}
push_front (&link[5], 'c');
display (link, N + 1);
free_all_list (link, N + 1);
free (link);
return 0;
}
Output:
# ./a.out
link array size : 5
[0] a b NULL
[1] c NULL
[2] f e d NULL
[3] NULL
[4] NULL
Reallocating link array, new size : 6
[0] a b NULL
[1] c NULL
[2] f e d NULL
[3] NULL
[4] NULL
[5] c NULL

Using fgets to read lines from a file into Binary Search Tree

Currently i'm trying to store each separate line in my file into a string, and then store it in a binary search tree, but a problem occurs. For some reason when I print my BST only the last line is outputted and not the first 3. Below is my code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct node
{
int count;
char* key;
struct node* left;
struct node* right;
};
struct node *newNode(char* item)
{
struct node* temp = (struct node*)malloc(sizeof(struct node));
temp->key = item;
temp->left = NULL;
temp->right = NULL;
temp->count = 1;
return temp;
};
void printInorder(struct node* root)
{
if(root != NULL)
{
printInorder(root->left);
printf("%s \n", root->key);
printInorder(root->right);
}
}
struct node* insert(struct node* node, char* key)
{
if(node == NULL)//When tree is empty
return newNode(key);
if(strcmp(key, node->key) < 0)
node->left = insert(node->left, key);
if(strcmp(key, node->key) > 0)
node->right = insert(node->right, key);
return node;
};
int main()
{
struct node *root = NULL;
int i = 0;
char str[100];
FILE* fp;
fp = fopen("textFile.txt", "r");
if ((fp = fopen("textFile.txt","r")) == NULL)
{
printf("Could not open textFile.txt\n");
exit(1);
}
while(fgets(str, 100, fp) != NULL)
{
++i;
root = insert(root, str);
printf("%3d: %s", i, str);
}
printf("bst printed\n");
printInorder(root);
return 0;
}
textFile.txt contains
bob is working.
david is a new hire.
alice is bob's boss.
charles doesn't like bob.
And when the bst is printed the only line that is outputted is the last one
Charles doesn't like bob.
Any help would really be appreciated.
Notice that when you create a node with newNode, you store a copy of the pointer passed into it, rather than a copy of the string being pointed at. This means that every time you insert a value into the tree, it stores a pointer to the str buffer in main. In other words, after you do your first insertion, things look like this:
+------------+
| BST Node | str
+------------+ +---+---+---+---+---+...+---+
| key | ---------> | b | o | b | | i | | 0 |
+------------+ +---+---+---+---+---+...+---+
When you then read the next line of the file, you're overwriting str with the contents of that line, so the picture looks like this:
+------------+
| BST Node | str
+------------+ +---+---+---+---+---+...+---+
| key | ---------> | d | a | v | i | d | | 0 |
+------------+ +---+---+---+---+---+...+---+
Notice that your BST now acts as though it contains "david is a new hire" even though you never inserted that value. As a result, when you try inserting "david is a new hire" into the BST, nothing happens.
The same thing happens for the next few reads, until eventually you read the final line of the file, when things look like this:
+------------+
| BST Node | str
+------------+ +---+---+---+---+---+...+---+
| key | ---------> | c | h | a | r | l | | 0 |
+------------+ +---+---+---+---+---+...+---+
This is why you're only seeing the line about Charlie at the end - the BST is directing you to the single shared copy of the buffer.
To fix this, make your BST store copies of the strings passed into it, or copy the strings before storing them in the tree. For example, you might have the newNode function call strdup to get its own copy of the string to store:
struct node *newNode(char* item)
{
struct node* temp = (struct node*)malloc(sizeof(struct node));
temp->key = strdup(item); // <--- here!
/* TODO: Error-handling! */
temp->left = NULL;
temp->right = NULL;
temp->count = 1;
return temp;
};
That should fix your issue. Just make sure to deallocate everything when you're done!

Reverse direction of pointers in doubly linked list

I have a circular doubly linked list and I want to change the direction of all the next and prev pointers. I can't really figure out what the source of the error is. When I print the reversed list, it gets the first two numbers correct but past that point the links list stops printing.
struct Link
{
TYPE value;
struct Link * next;
struct Link * prev;
};
struct CircularList
{
int size;
struct Link* sentinel;
};
static void init(struct CircularList* list)
{
list->sentinel = (struct Link *) malloc(sizeof(struct Link));
list->sentinel->next = list->sentinel;
list->sentinel->prev = list->sentinel;
list->size = 0;
}
struct CircularList* circularListCreate()
{
struct CircularList* list = malloc(sizeof(struct CircularList));
init(list);
return list;
}
void circularListAddFront(struct CircularList* list, TYPE value)
{
struct Link *newLink = (struct Link *) malloc(sizeof(struct Link));
newLink->value = value;
newLink->next = list->sentinel->next;
newLink->prev = list->sentinel;
list->sentinel->next = newLink;
if(circularListIsEmpty(list)) {
list->sentinel->prev = newLink;
}
list->size++;
}
void circularListRemoveFront(struct CircularList* list)
{
struct Link *temp = list->sentinel->next;
temp->next->prev = list->sentinel;
list->sentinel->next = temp->next;
free(temp);
list->size--;
}
void circularListRemoveBack(struct CircularList* list)
{
struct Link *temp = list->sentinel->prev;
temp->prev->next = list->sentinel;
list->sentinel->prev = temp->prev;
free(temp);
list->size--;
}
void circularListReverse(struct CircularList* list)
{
struct Link *link = list->sentinel->next;
while(link != list->sentinel) {
struct Link *nextTemp = link->next;
struct Link *prevTemp = link->prev;
link->prev = link->next;
link->next = prevTemp;
link = nextTemp;
}
struct Link *temp = list->sentinel->next;
list->sentinel->next = list->sentinel->prev;
list->sentinel->prev = temp;
}
If I run the following to test this I get the output 5 4 1 2 2 1 and then terminates with no errors.
struct CircularList *deque = circularListCreate();
circularListAddBack(deque, 1);
circularListAddBack(deque, 2);
circularListAddBack(deque, 3);
circularListAddFront(deque, 4);
circularListAddFront(deque, 5);
circularListAddFront(deque, 6);
circularListRemoveFront(deque);
circularListRemoveBack(deque);
circularListPrint(deque);
circularListReverse(deque);
circularListPrint(deque);
There's a bug in your circularListAddFront function, and (though not shown) probably also in circularListAddBack:
Assume this state of the list:
p+SENTINEL+n
| A |
-----|------
Now, let's say you add 42 to the front. You first allocate a new node and set its value and pointers. Also you set the sentinel next pointer:
struct Link *newLink = (struct Link *) malloc(sizeof(struct Link));
newLink->value = value;
newLink->next = list->sentinel->next;
newLink->prev = list->sentinel;
list->sentinel->next = newLink;
This leads to the following state:
p+SENTINEL+n
| A |
-----| |
| |
------ |
| | |
p+42+n |
A |
| |
---------
Which is not fine, because the prev pointer of the sentinel still points to itself. You fix that directly after that:
if(circularListIsEmpty(list)) {
list->sentinel->prev = newLink;
}
list->size++;
This gives the desired result:
p+SENTINEL+n
--| A |
| | |
| ------ |
| | | |
| p+42+n |
| A |
| | |
--------------
This is fine. Now let's add a 21 to the glorious list:
----------
| |
| V
| p+SENTINEL+n
| --| A |
| | | |
| | ------ |
| | | | |
| | p+42+n |
| | A |
| | | |
| -----| |
| | |
| p+21+n |
--| A |
| |
-----------
That's the state right before that if, and it has the same issue as before: There's one prev pointer wrong, this time it's not sentinels but the one of node 42: It should point to its previous node, which is now 21, and not to the sentinel.
Since the if is not taken, the sate remains. You don't notice it until reversing the list because you don't use the prev pointers until then.
To fix that, get rid of the if and correct the logic unconditionally:
When you insert a new node to the front ("after the sentinel"), then you need to change the prev pointer of the node that was at the front before and point it to the new node:
newLink->next = list->sentinel->next;
newLink->prev = list->sentinel;
list->sentinel->next->prev = newLink; // ADDITION
list->sentinel->next = newLink;
The code for reversing the list seems fine, though. And for now, I'm done with "line art ASCII graphics" :D

Remove nodes of a linked list on condition (C)

I am doing an exercise with a linked list, in which I need to remove some nodes according to a condition. The condition is ''remove the nodes that have a data stored that is less or equal to 'avg''.
I should consider three cases:
- removing the head node
- removing a node in the middle
- removing last node
I used three pointers, but this doesn't seem to work. What am I doing wrong? Am I accidentally missing some pointers? Thank you in advance!
void checkAvg(int avg, struct list* head_node){
struct list* prev;
struct list* curr;
struct list* next;
prev = head;
curr = prev->link;
next = curr->link;
while (curr!=NULL) {
if (prev->data <= avg) {
head = curr;
}
if (curr->data <= avg) {
prev->link = curr->link;
}
if (next->data <= avg) {
curr->link = next->link;
}
prev = prev->link;
curr = curr->link;
next = next->link;
}
}
EDIT:
I am sorry, as you told me, I wasn't specific. The program has to do this:
1) Read a linked list
2) Generate an output average of the values I read (sum of values / # of values)
3) remove from the list the nodes that have a data that is <= to the average
Free the memory of those nodes.
About the free() part, I have some difficulties with that, so I thought I first had to learn how to properly use pointers. As you may have noticed, I am not really good at that, I am starter.
Thank you for everyone who has replied, I am checking the replies right now
If your linked list does not have some sentinel head-node (and I strongly advise that it does not, as NULL is a damn fine value to indicate "I'm empty"), removal traversal is not overtly complicated. The places in your code where you seem to go astray are:
Passing the head pointer by-address and declaring the parameter as a pointer-to-pointer OR returning the new head pointer if the old one was disposed.
Maintaining consistency in your local pointer variables. You have to know for sure what everything points to at all times.
You can use pointer values, or you can use the actual pointers themselves (by address). I prefer the latter. In either case, the head node pointer must be passed by-address to allow potential modification of the caller's variable (like everything else in C) or the function can return the potentially new head node address. I prefer the former of those methods, as it leaves you the option of using the function return value for communicating error states to your caller. You're not doing that right now, but you should (hint).
void checkAvg(int avg, struct list** pp)
{
while (*pp)
{
if ((*pp)->data <= avg)
{
struct list *victim = *pp;
*pp = victim->link;
free(victim);
}
else
{ // advance to address of next "link" pointer
pp = &(*pp)->link;
}
}
}
It is worth noting this can be significantly simpler if the list is maintained as sorted in ascending order. If that is the case, the entire checkAvg function becomes substantially simpler. You can exit the loop as soon as you detect a value that no longer fits your condition:
void checkAvg(int avg, struct list** pp)
{
while (*pp && (*pp)->data <= avg)
{
struct list *victim = *pp;
*pp = victim->link;
free(victim)
}
}
In either case, the function is invoked by passing the head pointer on the caller-side by-address:
struct list *head = NULL;
//... populate list....
checkAvg(value, &head);
How it Works
A linked list is just something that looks like this:
-------- -------- --------
head ---> | link | ---> | link | ---> | link | ---> NULL
| val1 | | val2 | | val3 |
-------- -------- --------
Using the methods posted, traversing the list uses a pointer-to-pointer, which does something like this:
pp --:
: -------- -------- --------
head ---> | link | ---> | link | ---> | link | ---> NULL
| val1 | | val2 | | val3 |
-------- -------- --------
pp points to a pointer; not a node. Initially pp holds the address of the head pointer (was passed in by-address as a parameter).
So what happens if the first node matches your criteria? Ultimately, this is the result
pp --:
: -------- --------
head ---> | link | ---> | link | ---> NULL
| val2 | | val3 |
-------- --------
--------
victim ---> | link |
| val1 |
--------
and the victim node is summarily discarded (the link of victim actually still references the second node, but is meaningless in this context after the detachment happens.
So, what about if the second node was the one that needed removal (i.e. we skipped the first node). That gets a little more complicated:
pp -----:
:
---:---- --------
head ---> | link | ---> | link | ---> NULL
| val1 | | val3 |
-------- --------
--------
victim ---> | link |
| val2 |
--------
What this is trying to show (admittedly poorly) is that the pp pointer-to-pointer always holds the address of the pointer that we may be modifying. If we don't need to modify that pointer, we change the address held in pp to point to the next link pointer in the list (obtained through pp = &(*pp)->link
The latter case doesn't happen when the list is already sorted , thus the reason its iterative loop is simpler. We just enumerate the list, throwing out nodes until we find one that no longer meets our condition.
But no matter what, pp always holds the address of the pointer that points to the node we're working with.. Initially, that address is the address of the caller's head pointer.
Ok. I can only hope that makes it clearer. Some debugging with printf("pp=%p, *pp=%p\n", pp, *pp) in the loop makes what is actually happening most-educational. Walking through the algorithms by-hand in a debugger would be highly informative.
It's a lot easier than you think.
You're walking and modifying a linked list, so set up a current and previous.
void checkAvg(int avg, struct list** head_node){ //when calling, use checkAvg(avg, &head_node);
struct list* prev = NULL;
struct list* curr = *head_node;
Starting at the head...
while(curr != NULL){
if(curr->data <= avg){
if(prev == NULL){
*head_node = curr->next; //updates the head node
} else {
prev->next = curr->next; //removes the unwanted node
}
}
curr = curr->next;
}
You really don't need a special end case because the while loop terminates when curr is NULL; for the last item in the list, curr->next is NULL so when it get set it will end the loop. You also don't need to check if the list is empty, because the loop will end if it curr == NULL and you assign the head to curr at first.
I want some recursive magic tonight
Node* deleteBad(Node *head, int(*pred)(int)) {
if (head == NULL) {
return NULL;
}
if (pred(head->value)) {
head->next = deleteBad(head->next, pred);
return head;
}
else {
Node *next = head->next;
free(head);
return deleteBad(next, pred);
}
}
And accumulators
void deleteBad2(Node *head, int(*pred)(int), Node *out) {
if (head) {
if (pred(head->value)) {
out->next = head;
deleteBad2(head->next, pred, out->next);
} else {
out->next = head->next;
deleteBad2(head->next, pred, out);
}
}
}
And for those coxcombs who dislike recursion optimized version
void deleteBad3(Node *head, int(*pred)(int), Node *out) {
begin:
if (head) {
if (pred(head->value)) {
out->next = head;
out = out->next;
head = head->next;
goto begin;
}
else {
out->next = head->next;
head = head->next;
goto begin;
}
}
}
and if someone dislike gotos
void deleteBad3nogoto(Node *head, int(*pred)(int), Node *out) {
while (1) {
if (head) {
if (pred(head->value)) {
out->next = head;
out = out->next;
head = head->next;
}
else {
out->next = head->next;
head = head->next;
}
}
else {
break;
}
}
}
You're checking each node three times in each iteration, which leads to some weird behavior. Check the head before entering the loop, and only compare the current node each time:
void checkAvg(int avg, struct list* head_node){
struct list* prev;
struct list* curr;
while (head!=NULL) {
if (head->data <= avg) {
head = head->link;
} else {
break;
}
}
prev = head;
curr = prev->link;
while (curr!=NULL) {
if (curr->data <= avg) {
prev->link = curr->link;
}
prev = prev->link;
curr = prev->link;
}
}
Rather than checking the data of prev, curr and next, just check for one single data in the loop.
You also need to return the new head in case it changes.
I'd suggest something like that (tested):
#include <stdio.h>
#include <malloc.h>
typedef struct list_ {
struct list_ *link;
int data;
} list;
list* RemoveElementsBelow(int avg, list* head_node) {
while (head_node != NULL && head_node->data <= avg) {
// Remove the first.
list* new_head = head_node->link;
free(head_node);
head_node = new_head;
}
if (head_node == NULL) {
return NULL;
}
list* prev;
list* curr;
prev = head_node;
curr = prev->link;
while (curr != NULL) {
if (curr->data <= avg) {
prev->link = curr->link; // Will be NULL at the end of the list.
free(curr);
curr = prev->link;
} else {
prev = curr;
curr = curr->link;
}
}
return head_node;
}
list* AddToHead(int value, list* head_node) {
list* new_node = malloc(sizeof(list));
new_node->link = head_node;
new_node->data = value;
return new_node;
}
list* PrintAndDeleteList(list* head_node) {
while (head_node != NULL) {
list* new_head = head_node->link;
printf("%d ", head_node->data);
free(head_node);
head_node = new_head;
}
printf("\n");
}
int main(int argc, char **argv) {
list* my_list = NULL;
int i;
int sum = 0;
for (i = 1; i < argc; ++i) {
int value = atoi(argv[i]);
sum += value;
my_list = AddToHead(value, my_list);
}
if (argc == 1) {
return 1;
}
int avg = sum / (argc - 1);
printf("Average value: %d\n", avg);
my_list = RemoveElementsBelow(avg, my_list);
PrintAndDeleteList(my_list);
return 0;
}
Compiled:
gcc -o test test.c
Tested:
./test 10 20 30 40 50
Average value: 30
50 40
./test 50 40 30 20 10
Average value: 30
40 50

Resources