2

I'm new to programming in C and taking a course. I'm having trouble with one of the tasks I'm practicing. I'm supposed to Write a program that creates a linked list of 10 characters, then creates a copy of the list in reverse order. I have written (mostly copied) a code, but it only reverses the contents of my linked list, doesn't copy them to a new linked list in reverse order. It's also not working with letters even though I'm using char data type. works fine with numbers.

Here's my code:

#include <stdio.h>
#include <malloc.h>

struct Node
{
    char data;
    struct Node *next;
};

static void reverse(struct Node **head_ref)
{
    struct Node *previous = NULL;
    struct Node *current = *head_ref;
    struct Node *next;
    while (current != NULL)
    {
        next = current->next;
        current->next = previous;
        previous = current;
        current = next;
    }
    *head_ref = previous;
}

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)
{
    struct Node *temp = head;
    while (temp != NULL)
    {
        printf("%d ", temp->data);
        temp = temp->next;
    }
}

int main()
{
    struct Node *head = NULL;
    char element = NULL;
    printf("Enter 10 characters:\n");
    for (int i = 0; i <= 9; i++)
    {
        scanf_s("%d", &element);
        push(&head, element);
    }
 
    printf("Given linked list\n");
    printList(head);
    reverse(&head);
    printf("\nReversed Linked list \n");
    printList(head);
    getchar();
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Ausat Ali
  • 21
  • 2
  • 3
    If you need to create a reverse copy, then treat it as a *stack*. That will make the reversing rather trivial. – Some programmer dude Jan 28 '22 at 19:56
  • 3
    The reason why it is "not working with characters" is that you are reading the elements as integers in `scanf_s("%d", &element);`. – nielsen Jan 28 '22 at 20:07
  • 1
    Talking about stacks, you already work with the original list as a stack, even having a `push` function. Why not use that existing function to add elements to the reversed copy? – Some programmer dude Jan 28 '22 at 20:12

3 Answers3

3

This for loop

for (int i = 0; i <= 9; i++)
{
    scanf_s("%d", &element);
    push(&head, element);
}

invokes undefined behavior because there is used an incorrect conversion specifier %d with an object of the type char,

You need to write

for (int i = 0; i <= 9; i++)
{
    scanf_s( " %c", &element, 1 );
    push(&head, element);
}

Pay attention to the blank before the conversion specifier %c in the format string. This allows to skip white space characters in the input stream.

As for the function then it can be declared and defined the following simple way using the function push that you already defined

struct Node * reverse_copy( const struct Node *head )
{
    struct Node *new_head = NULL;

    for ( ; head != NULL; head = head->next )
    {
        push( &new_head, head->data );
    }

    return new_head;
}

And in main you can write something like

struct Node *second_head = reverse_copy( head );

Take into account that the function push would be more safer if it would process the situation when memory allocation for a node failed.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
2

To create a copy in reverse order, create a new list with the same values as the original list but prepend the new nodes using the push function.

Here is a modified version:

#include <stdio.h>
#include <stdlib.h>

struct Node {
    char data;
    struct Node *next;
};

void prepend(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 append(struct Node **head_ref, char new_data) {
    struct Node *new_node = (struct Node *)malloc(sizeof(struct Node));
    struct Node *node = *head_ref;
    new_node->data = new_data;
    new_node->next = NULL;
    if (!node) {
        *head_ref = new_node;
    } else {
        while (node->next)
            node = node->next;
        node->next = new_node;
    }
}

void printList(const struct Node *head) {
    const struct Node *temp = head;
    while (temp != NULL) {
        printf("%c ", temp->data);
        temp = temp->next;
    }
    printf("\n");
}

struct Node *copy_reverse(struct Node *list) {
    struct Node *new_list = NULL;
    while (list) {
        prepend(&new_list, list->data);
        list = list->next;
    }
    return new_list;
}

void freeList(struct Node *list) {
    while (list) {
        struct Node *node = list;
        list = list->next;
        free(node);
    }
}

int main() {
    struct Node *head = NULL;
    char element;
    printf("Enter 10 characters:\n");
    for (int i = 0; i < 10; i++) {
        scanf_s("%c", &element);
        push(&head, element);
    }
 
    printf("Given linked list\n");
    printList(head);

    struct Node *copy = copy_reverse(head);
    printf("\nReversed Linked list \n");
    printList(copy);
    freeList(head);
    freeList(copy);
    getchar();
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
1

You're almost there. All it needs is one tweak. In reverse, you need to create a new copy of the current node and use that instead. Also, since you'll be ending up with a second list and not altering the original, you should return the new list from reverse.

static struct Node* reverse(const struct Node* head_ref)
{
    struct Node* previous = NULL;
    const struct Node* current = head_ref;
    struct Node* copy;

    while (current != NULL) {
        copy = malloc(sizeof(*copy));
        if (copy == NULL) {
            // handle error
        }
        copy->data = current->data;
        copy->next = previous;
        previous = copy;
        current = current->next;
    }

    return previous;
}

You can also make the loop prettier by converting it to a for loop.

for (current = head_ref; current != NULL; current = current->next) {

Finally, when you print out the list, you're using %d in the printf format string. %d will print the char as an integer. To print out the actual character, use %c instead.

Daniel Walker
  • 6,380
  • 5
  • 22
  • 45