Reputation: 21
Now I have created a loop to make 25 nodes for the doubly linked list. By Initializing head pointer as NULL in the main function, now the forward_traversing
and show_first
functions do work as intended. But the tail value cannot by initialized NULL and I don't know what to assign it or to change in this code to allow for the backward_traversing
and show_last
function to work.
During Debugging it throws Exception of "Segmentation Fault" in the functions:- backward_traversal
and show_last.
#include <stdio.h>
#include <stdlib.h>
typedef struct Node
{
int value;
struct Node *next_link;
struct Node *prev_link;
} Node;
Node *create_node(int value)
{
Node *new_node = malloc(sizeof(Node));
new_node->value = value;
new_node->next_link = NULL;
new_node->prev_link = NULL;
return new_node;
}
void forward_traversing(Node *head)
{
Node *temp = head;
while (temp != NULL)
{
printf("%d ", temp -> value);
temp = temp->next_link;
}
printf("\n");
}
void backward_traversing(Node *tail)
{
Node *temp = tail;
while (temp != NULL)
{
printf("%d ", temp->value);
temp = temp->prev_link;
}
printf("\n");
}
void show_first(Node *head)
{
printf("%d is the first value\n", head->value);
}
void show_last(Node *tail)
{
printf("%d is the last value\n", tail->value);
}
int main()
{
Node *head = NULL , *temp = NULL , *tail = NULL;
for (int i = 25; i >= 0; i--){
temp = create_node(i);
temp -> next_link = head;
temp -> prev_link = tail;
head = temp;
}
forward_traversing(head);
backward_traversing(tail);
show_first(head);
show_last(tail);
free(head);
free(tail);
free(temp);
return 0;
}
I am currently trying to make several functions to insert at the start, end and in reference to another node and maybe somehow make that automate my making node process.
For OS I have Windows 11 and Compiler is GNU default compiler in Vs Code
Upvotes: 1
Views: 72
Reputation: 351039
There are a few issues in your code:
As you already found out, you never update tail
to anything else than NULL
. This means that:
NULL
as value for their prev_link
member.NULL
to it. Even if you would pass it the pointer to the first created node, it would only print that node because of the first point.free(tail)
will failYour list creation loop body only sets two pointers to add a node to a list, but it doesn't take care of setting any pointers towards that new node. If you add a node to the end of a list (either end), you need to have code to update the currently last (or first) node so it gets a pointer to the new node you want to add.
I would suggest to extend the create_node
function with an extra parameter that represents the node after which the new node should be inserted (if it is not NULL
). That way you take that logic out of your main function.
free(head)
and free(tail)
are not a good idea. These calls will free exactly 2 nodes, but this is problematic:
head
and tail
would be equal, and so you would call free
twice on the same pointer: that is an error.head
and tail
equal to NULL
) you would also make invalid calls to free
.The way to go is to write a function that iterates the list and frees every node in it.
Here is how the above remarks would affect the following functions in your code:
Node *create_node(int value, Node *prev) // Add parameter
{
Node *new_node = malloc(sizeof(Node));
new_node->value = value;
// Use extra argument to insert node after given node:
Node *next = prev ? prev->next_link : NULL;
new_node->prev_link = prev;
new_node->next_link = next;
// Adjust links TOWARDS the new node:
if (prev) prev->next_link = new_node;
if (next) next->prev_link = new_node;
return new_node;
}
// Function to free a complete list
void free_list(Node *head) {
while (head) {
Node *temp = head;
head = head->next_link;
free(temp);
}
}
int main()
{
Node *head, *tail;
// Treat the first node outside of the loop, so
// you can update head just this one time:
head = tail = create_node(0, NULL);
for (int i = 1; i < 25; i++) {
// Appending a node becomes easy when logic is
// completely in create_node function:
tail = create_node(i, tail);
}
printf("Forward: ");
forward_traversing(head);
printf("Backward: ");
backward_traversing(tail);
show_first(head);
show_last(tail);
// Free *any* node in the list
free_list(head);
return 0;
}
Once you start working with more than one list, it becomes impractical to manage head
and tail
for each of those. Then it becomes handy to also define a struct
for lists, which would have those two members (head
and tail
). Once you have that you would use that as argument type for many of the functions you have.
Another point to make concerns separation of concerns. Printing is something you want to keep out of most functions. In particular, a traversal over a list could be useful for other purposes also, like for calculating the sum of the entries. It would be wasteful if you had to create a separate traversal function just to get a sum, and another one to get a count, ...etc. Better would be to make the traversal generic, so that you can decide with a function argument what should happen with each visited value.
Here is an illustration of what you could do:
#include <stdio.h>
#include <stdlib.h>
typedef struct Node
{
int value;
struct Node *next_link;
struct Node *prev_link;
} Node;
typedef struct List {
Node *head;
Node *tail;
} List;
Node *create_node(int value, Node *prev) {
Node *new_node = malloc(sizeof(Node));
new_node->value = value;
Node *next = prev ? prev->next_link : NULL;
new_node->prev_link = prev;
new_node->next_link = next;
if (prev) prev->next_link = new_node;
if (next) next->prev_link = new_node;
return new_node;
}
int is_empty(List *list) {
return !list->head;
}
void append(List *list, int value) {
list->tail = create_node(value, list->tail);
if (!list->head) list->head = list->tail;
}
int pop(List *list) {
if (is_empty(list)) return -1;
Node *temp = list->tail;
list->tail = list->tail->prev_link;
if (!list->tail) list->head = NULL;
int value = temp->value;
free(temp);
return value;
}
void clear_list(List *list) {
while (!is_empty(list)) pop(list);
}
void forward_traversing(List *list, void (*f)(int)) {
Node *temp = list->head;
while (temp) {
f(temp->value);
temp = temp->next_link;
}
}
void backward_traversing(List *list, void (*f)(int)) {
Node *temp = list->tail;
while (temp) {
f(temp->value);
temp = temp->prev_link;
}
}
void print_int(int n) {
printf("%d ", n);
}
int peek_first(List *list) {
return is_empty(list) ? -1 : list->head->value;
}
int peek_last(List *list) {
return is_empty(list) ? -1 : list->tail->value;
}
int main() {
List list = {NULL, NULL};
for (int i = 0; i < 25; i++) {
append(&list, i);
}
// All printing is taken out of the list-specific functions
printf("Forward: ");
backward_traversing(&list, print_int);
printf("\n");
printf("Backward: ");
backward_traversing(&list, print_int);
printf("\n");
printf("%d is the first value\n", peek_first(&list));
printf("%d is the last value\n", peek_last(&list));
clear_list(&list);
return 0;
}
Upvotes: 0
Reputation: 44339
As your code adds new elements to the front of the list, you should only set tail
when inserting first element. So:
Instead of:
temp -> prev_link = tail;
Do:
temp -> prev_link = NULL;
if (tail == NULL) tail = temp; // First element so update tail
When head
isn't NULL, you also need to update head->prev_link
if (head != NULL) head->prev_link = temp;
Further, you write: "Now I have created a loop to make 25 nodes"
Well, no you don't. This code:
for (int i = 25; i >= 0; i--){
will run 26 times so you create 26 nodes.
It's more common to make a loop starting from 0
so I would suggest this instead:
#define NODES 25
for (int i = 0; i < NODES; ++i){
temp = create_node(NODES - 1 - i);
temp -> next_link = head;
temp -> prev_link = NULL;
if (tail == NULL) tail = temp; // First element so update tail
if (head != NULL) head->prev_link = temp;
head = temp;
}
or perhaps a bit more clear:
for (int i = 0; i < NODES; ++i){
temp = create_node(NODES - 1 - i);
if (head == NULL) {
head = temp;
tail = temp;
} else {
temp -> next_link = head;
head->prev_link = temp;
head = temp;
}
}
Upvotes: 0