Search code examples
clinked-liststack

Linked List - Stack Sorting in C


My code is following:

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

struct stack {
    int data;
    struct stack* next;
};
struct Student
{
    unsigned long int rollnumber;
    char name[100];
    struct Student *next;
    
}* head;
// Utility function to initialize stack
void initStack(struct stack** s) { *s = NULL; }
 
// Utility function to check if stack is empty
int isEmpty(struct stack* s)
{
    if (s == NULL)
        return 1;
    return 0;
}
 
// Utility function to push an item to stack
void push(struct stack** s, int x, unsigned long int rollnumber, char *name)
{
    struct stack* p = (struct stack*)malloc(sizeof(*p));
 
    if (p == NULL) {
        fprintf(stderr, "Memory allocation failed.\n");
        return;
    }
 
    p->data = x;
    p->next = *s;
    *s = p;
}
 
// Utility function to remove an item from stack
int pop(struct stack** s)
{
    int x;
    struct stack* temp;
 
    x = (*s)->data;
    temp = *s;
    (*s) = (*s)->next;
    free(temp);
 
    return x;
}

// Function to find top item
int top(struct stack* s) { return (s->data); }

// Recursive function to insert an item x in sorted way
void sortedInsert(struct stack** s, int x, unsigned long int rollnumber, char *name)
{
    // Base case: Either stack is empty or newly inserted
    // item is less than top (more than all existing)
    if (isEmpty(*s) || x < top(*s)) {
        push(s, x, rollnumber, name);
        return;
    }
    else{
    // If top is less, remove the top item and recur
    int temp = pop(s);
    sortedInsert(s, x, rollnumber, name);
 
    // Put back the top item removed earlier
    push(s, temp, rollnumber, name);}
}
 
// Function to sort stack
void sortStack(struct stack** s)
{
    // If stack is not empty
    char *name;
    unsigned long int rollnumber;
    if (!isEmpty(*s)) {
        // Remove the top item
        int x = pop(s);
 
        // Sort remaining stack
        sortStack(s);
 
        // Push the top item back in sorted stack
        sortedInsert(s, x, rollnumber, name);
    }
}
// Utility function to print contents of stack
void printStack(struct stack* s, unsigned long int rollnumber, char* name)
{
    struct Student* student = (struct Student *) malloc(sizeof(struct Student));
    struct Student* temp = head;
    while (s && temp!=NULL) {
        printf("%lu %s\n", temp->rollnumber, temp->name);
        s = s->next;
        temp = temp->next;
    }

    printf("\n");
}

void insert(unsigned long int rollnumber, char* name)
{
    
    struct Student * student = (struct Student *) malloc(sizeof(struct Student));
    student->rollnumber = rollnumber;
    strcpy(student->name, name);
    student->next = NULL;
    
    if(head==NULL){
        // if head is NULL
        // set student as the new head
        head = student;
    }
    else{
        // if list is not empty
        // insert student in beginning of head
        student->next = head;
        head = student;
    }
    
}

void Delete(unsigned long int rollnumber)
{
    struct Student * temp1 = head;
    struct Student * temp2 = head; 
    while(temp1!=NULL){
        
        if(temp1->rollnumber==rollnumber){
            
            printf("Record with roll number %lu Found\n", rollnumber);
            
            if(temp1==temp2){
                // this condition will run if
                // the record that we need to delete is the first node
                // of the linked list
                head = head->next;
                free(temp1);
            }
            else{
                // temp1 is the node we need to delete
                // temp2 is the node previous to temp1
                temp2->next = temp1->next;
                free(temp1); 
            }
            
            printf("Record successfully deleted\n");
            return;
            
        }
        temp2 = temp1;
        temp1 = temp1->next;
        
    }
printf("Student with roll number %lu is not found\n", rollnumber);
    
}
void display()
{
    struct Student * temp = head;
    while(temp!=NULL){
        
        printf("Roll Number: %lu\n", temp->rollnumber);
        printf("Name: %s\n", temp->name);
        temp = temp->next;
        
    }
}

int main(void)
{
    head = NULL;
    int choice;
    int fc_,id_,year_, fc_year;
    char name[100];
    struct student* s;
    unsigned long int rollnumber;
    struct stack* faculty;
    struct stack* year;
    struct stack* id;
    initStack(&faculty);
    initStack(&year);
    initStack(&id);
    printf("1 - Enter school number\n2 - Display school numbers by ID\n3 - Display school numbers sorted by year\n4 - Display school numbers sorted by the faculty codes\n5 - Delete a record by school number\n6 - Exit");
    do
    {
        printf("\nEnter Choice: ");
        scanf("%d", &choice);
        switch (choice)
        {
            case 1:
                printf("Enter roll number: ");
                scanf("%lu", &rollnumber);
                fc_ = rollnumber/pow(10, 6);
                id_ = rollnumber%(10*10*10*10);
                fc_year = rollnumber/pow(10,4);
                year_ = fc_year%(100);
                printf("Enter name: ");
                scanf("%s", name);
                insert(rollnumber, name);
                push(&faculty, fc_, rollnumber, name);
                push(&year, year_, rollnumber, name);
                push(&id, id_, rollnumber, name);
                break;
            case 2:
                printf("Sorted by ID: \n");
                sortStack(&id);
                printStack(id, rollnumber, name);
                break;
            case 3:
                printf("Sorted by year: \n");
                sortStack(&year);
                printStack(year, rollnumber, name);
                break;
            case 4:
                printf("Sorted by faculty code: \n");
                sortStack(&faculty);
                printStack(faculty, rollnumber, name);
                break;
            case 5:
                printf("Enter roll number to delete: ");
                scanf("%lu", &rollnumber);
                Delete(rollnumber);
                break;
            case 6:
                break;
        }
        
    } while (choice != 6);
}

When, for example, the choice is 2, I want to display the student name and whole rollnumber in ascending order. But when I run it that is what I get (C, B, A are the names given by me):

Enter Choice: 2
705102020 C
705102010 B
705102005 A

I am sure that I sorted it correctly, but probably my printStack function is not working properly. How can I reverse this?


Solution

  • It would be better to define a stack struct independantly of the rest of your code:

    struct stack {
        void *data;
        struct stack *next;
    };
    
    void stack_init(struct stack **ss)
    {
        *ss = NULL;
    }
    
    bool stack_push(struct stack **ss, void *data)
    {
        struct stack *node = malloc(sizeof *node);
        if (!node) return false;
        
        node->data = data; // Copy address, not data
        node->next = *ss;
        *ss = node;
        
        return true;
    }
    
    bool stack_pop(struct stack **ss, void **data)
    {
        if (!*ss) return false;
        
        struct stack *rm = *ss;
        *ss = (*ss)->next;
        if (data) *data = rm->data; // If nothing is provided (i.e. NULL), data will leak if it was allocated dynamically.
        free(rm);
        
        return true;
    }
    
    bool stack_top(const struct stack *ss, void **data)
    {
        if (!ss) return false;
        
        *data = ss->data;
        return true;
    }
    
    void stack_print(const struct stack *ss, void print(const struct stack*))
    {
        for (const struct stack *sp = ss; sp; sp = sp->next)
            print(sp);
    }
    

    You can implement your stack sorting function like that:

    struct stack *stack_find_min(struct stack *ss, int (*cmp)(const void*, const void*))
    {
        struct stack *min = ss;
        for (struct stack *sp = ss; sp; sp = sp->next)
            if (cmp(sp->data, min->data) < 0)
                min = sp;
        
        return min;
    }
    
    void stack_sort(struct stack *ss, int (*cmp)(const void*, const void*))
    {
        if (!ss) return;
        
        for (struct stack *sp = ss; sp; sp = sp->next) {
            struct stack *min = stack_find_min(sp, cmp);
            swap(&sp->data, &min->data);
        }
    }
    

    swap() swaps two pointers:

    void swap(void **p1, void **p2)
    {
        void *tmp = *p1;
        *p1 = *p2;
        *p2 = tmp;
    }
    

    After that, define your student data structure:

    struct student {
        unsigned long rollnumber;
        char name[100];
        struct student *next;
    };
    
    struct student *student_insert(struct student **head, unsigned long rollnumber, const char *name)
    {
        struct student *st = malloc(sizeof(*st));
        if (!st) return NULL;
        
        st->rollnumber = rollnumber;
        strncpy(st->name, name, 99);
        st->next = *head;
        *head = st;
        
        return st;
    }
    
    void student_print(const struct stack *ss)
    {
        struct student *st = ss->data;
        printf("%ld\t%s\n", st->rollnumber, st->name);
    }
    
    int student_compare_id(const void *s1, const void *s2)
    {
        // MUST cast void* into (struct student*) first.
        const struct student *student1 = s1;
        const struct student *student2 = s2;
        
        unsigned long rollnumber1 = student1->rollnumber;
        unsigned long rollnumber2 = student2->rollnumber;
        
        unsigned long id1 = rollnumber1 % 10000;
        unsigned long id2 = rollnumber2 % 10000;
        
        return id1 == id2 ? 0 : (id1 < id2 ? -1 : 1);
    }
    

    If you want to test the above:

    int main(void)
    {
        struct stack *ss;
        stack_init(&ss);
    
        struct student *st = NULL;
        student_insert(&st, 100181234, "James");
        student_insert(&st, 200195678, "John");
        student_insert(&st, 300200324, "Goku");
        student_insert(&st, 400214321, "Taylor");
        
        for (struct student *p = st; p; p = p->next)
            stack_push(&ss, p);
    
        puts("Unsorted stack:");    
        stack_print(ss, student_print);
        
        stack_sort(ss, student_compare_id);
        
        puts("\nSorted by ID:");
        stack_print(ss, student_print);
    
        // Don't forget to free the memory allocated for students
        // and pop all nodes of the stack (ommited here).
    }
    

    Output:

    Unsorted stack:
    100181234   James
    200195678   John
    300200324   Goku
    400214321   Taylor
    
    Sorted by ID:
    300200324   Goku
    100181234   James
    400214321   Taylor
    200195678   John
    

    Few more things:

    • Don't use scanf() to read user input. fgets() is safer.
    • You can replace pow(10, 4) directly by 10000. Why wasting CPU cycles to calculate constants?
    • In your insert() function, there is no need to test for *head == NULL. The code in the else statement handles this case.

    EDIT: In student_compare_id(), the const void* pointers MUST BE CASTED to const struct student* first before dereferencing. The previous version seemed to work because the rollnumber happened to be the first field in the struct. That was an undefined behavior in all its glory. Fixed that now.

    Here is the previous version that NO ONE SHOULD USE:

    int student_compare_id(const void *s1, const void *s2)
    {
        unsigned long rollnumber1 = *(unsigned long*)s1; // WRONG!
        unsigned long rollnumber2 = *(unsigned long*)s2; // WRONG!
        
        unsigned long id1 = rollnumber1 % 10000;
        unsigned long id2 = rollnumber2 % 10000;
        
        return id1 == id2 ? 0 : (id1 < id2 ? -1 : 1);
    }