Home > Software engineering >  Linked List - Stack Sorting in C
Linked List - Stack Sorting in C

Time:04-16

I am trying to make a student recording system. Rollnumber is a 9-digit number. The first 3 digits are faculty code, the other 2 digits are the year, and the last 4 digits are ID. My task is to add every record into a linked list and into a stack in order to sort it in ascending order. 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 sorted by ID (last 4 digits) 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
Sorted by ID: 
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?

CodePudding user response:

The primary problem is that there is no connection between your stack(s) and your student list, but the code assumes that there is. You can reorder the stacks however you like, but that does not reorder the list. When you print, the students simply come out in the reverse of their insertion order (because you insert at the front of the list, and then traverse the list from front to back).

You have two main alternatives:

  1. combine the list and all the stacks into one linked list. You can use this both list-style and stack-style if you like. OR

  2. Give the stack nodes pointers to the corresponding list elements, and traverse those when you print a stack to get the students in the order implied by the stack.

CodePudding user response:

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[100])
{
    struct student *st = malloc(sizeof(*st));
    if (!st) return NULL;
    
    st->rollnumber = rollnumber;
    strncpy(st->name, name, sizeof(name));
    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)
{
    unsigned long rollnumber1 = *(unsigned long*)s1;
    unsigned long rollnumber2 = *(unsigned long*)s2;
    
    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 strudent *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.
  • Related