LEVEL 1: FORGING YOUR CHARACTER (Structs & Enums)
Before we can venture into the labyrinth, we need to define our character's attributes. In C, we use a struct to group different data types into a single "record". For this quest, our character is a studentRecord.
The `studentRecord` Blueprint
Our struct holds all the essential stats for one student. Notice how it contains character arrays for strings, an integer for the ID, and a special enum type for the school year.
struct studentRecord {
char studentName[MAX_NAME_LENGTH];
char major[MAX_NAME_LENGTH];
schoolYearType schoolYear; // This is our special Enum type!
unsigned int IDNumber;
struct studentRecord* next; // The magic pointer that links to the next character!
};
The `enum` Scroll
An enum (enumeration) is a way to create a set of named integer constants. It makes our code more readable than using magic numbers like 0, 1, 2, 3. Here, `freshman` is secretly `0`, `sophomore` is `1`, and so on.
typedef enum { freshman = 0, sophomore, junior, senior } schoolYearType;
LEVEL 2: MASTERING THE POINTER WAND (Memory Management)
Pointers are the magic wands of C. They don't hold data themselves; they hold the memory address *where* data lives. To create new student records on the fly, we must master memory allocation.
Casting `malloc()`
The malloc() spell (memory allocation) reserves a chunk of memory from the "heap". We tell it how much space we need (the size of one `studentRecord`), and it gives us back a generic pointer to that space. We then "cast" it to the correct pointer type.
// Reserve memory for one new student and get a pointer to it.
struct studentRecord* newNode = (struct studentRecord*)malloc(sizeof(struct studentRecord));
The `free()` Spell of Banishment
Memory you allocate with `malloc()` is not automatically cleaned up! If you lose the pointer to it, that memory becomes a "memory leak," draining your system's power. The free() spell is essential. You must cast it on every piece of memory you're done with to return it to the system.
// When we delete a node, we must free its memory to prevent leaks.
free(nodeToDelete);
LEVEL 3: NAVIGATING THE LABYRINTH (Linked Lists)
A linked list is a chain of `structs` (we call them "nodes"). Each node holds its own data and, crucially, a pointer to the `next` node in the chain. The last node's `next` pointer is `NULL`, signaling the end of the path.
Traversal: The Basic Move
To walk through the list, you create a temporary pointer (let's call it `current`) and start it at the head of the list. Then, you loop, moving the pointer forward with `current = current->next;` until you hit `NULL`.
struct studentRecord* current = list; // Start at the beginning.
while (current != NULL) {
// Do something with the 'current' node...
current = current->next; // Move to the next node.
}
BOSS BATTLES: IMPLEMENTING THE CORE FUNCTIONS
These are the key functions from your homework. Master them, and you've conquered the level.
Mini-Boss: `countNodes()`
A simple traversal to count every node in the list. This is your warm-up.
int countNodes()
{
int count = 0; // Start counter at zero.
struct studentRecord* current = list; // Temporary pointer to walk the list.
while (current != NULL) {
count++; // Add one for the current student.
current = current->next; // Move to the next student.
}
return count; // Return the total.
}
Mini-Boss: `displayList()`
Traverse the list and print each student's stats. The key challenge is converting the `schoolYear` enum back to a human-readable string.
void displayList()
{
struct studentRecord* current = list; // Temp pointer to walk the list.
while (current != NULL) {
printf("\nStudent name: %s", current->studentName);
printf("\nStudent major: %s", current->major);
printf("\nID number: %d", current->IDNumber);
// Use a switch to translate the enum number to a string.
switch (current->schoolYear) {
case freshman: printf("\nSchoolYear: freshman\n"); break;
case sophomore: printf("\nSchoolYear: sophomore\n"); break;
case junior: printf("\nSchoolYear: junior\n"); break;
case senior: printf("\nSchoolYear: senior\n"); break;
}
current = current->next; // Move to the next student.
}
}
Helper Mob: `swapNodes()`
A crucial helper function for sorting. It swaps the *data* inside two nodes but leaves their `next` pointers untouched. This is much more efficient than re-wiring the list structure.
void swapNodes(struct studentRecord* node1, struct studentRecord* node2)
{
// We need temporary variables to hold the data from node1 during the swap.
char tempName[MAX_NAME_LENGTH];
char tempMajor[MAX_NAME_LENGTH];
schoolYearType tempSchoolYear;
unsigned int tempID;
// Use strcpy for strings and direct assignment for numbers/enums.
strcpy(tempName, node1->studentName);
strcpy(tempMajor, node1->major);
tempSchoolYear = node1->schoolYear;
tempID = node1->IDNumber;
strcpy(node1->studentName, node2->studentName);
strcpy(node1->major, node2->major);
node1->schoolYear = node2->schoolYear;
node1->IDNumber = node2->IDNumber;
strcpy(node2->studentName, tempName);
strcpy(node2->major, tempMajor);
node2->schoolYear = tempSchoolYear;
node2->IDNumber = tempID;
}
Final Boss: `deleteNode()`
This is where your pointer skills are truly tested. You must find the node, carefully re-wire the `next` pointers to bypass it, and then `free()` its memory. Handling the head of the list is a critical special case!
int deleteNode(char* studentName_input)
{
struct studentRecord* current = list; // To find the node.
struct studentRecord* previous = NULL; // To keep track of the node before.
// Case 1: The list is empty.
if (list == NULL) {
return -1; // Nothing to delete.
}
// Case 2: The head node is the one to delete.
if (strcmp(list->studentName, studentName_input) == 0) {
current = list; // Mark the head for deletion.
list = list->next; // The second node is now the new head.
free(current); // Banish the old head!
return 1; // Success!
}
// Case 3: Search the rest of the list.
while (current != NULL && strcmp(current->studentName, studentName_input) != 0) {
previous = current; // Move 'previous' to where 'current' is.
current = current->next; // Move 'current' one step forward.
}
// If we finished the loop and 'current' is NULL, the student wasn't found.
if (current == NULL) {
return 0; // Not found.
}
else { // We found the node at 'current'.
// Stitch the list: make the previous node's 'next' skip over 'current'.
previous->next = current->next;
free(current); // Banish the found node.
return 1; // Success!
}
}
TREASURE ROOM: THE COMPLETE `addSort()` FUNCTION
This is the final challenge, combining all your skills: duplicate checking, memory allocation, appending to the list, and finally, sorting the list using a Bubble Sort and your `swapNodes` helper. This is the complete solution to Q1.
int addSort(char* studentName_input, char* major_input, char* schoolYear_input, unsigned int IDNumber_input)
{
struct studentRecord* current = list;
// 1. Check for Duplicates
while(current != NULL){
if(strcmp(current->studentName, studentName_input) == 0){
return 0; // Found a duplicate, fail.
}
current = current->next;
}
// 2. Create New Node
struct studentRecord* newNode = (struct studentRecord*)malloc(sizeof(struct studentRecord));
strcpy(newNode->studentName, studentName_input);
strcpy(newNode->major, major_input);
newNode->IDNumber = IDNumber_input;
newNode->next = NULL;
if (strcmp(schoolYear_input, "freshman") == 0) newNode->schoolYear = freshman;
else if (strcmp(schoolYear_input, "sophomore") == 0) newNode->schoolYear = sophomore;
else if (strcmp(schoolYear_input, "junior") == 0) newNode->schoolYear = junior;
else if (strcmp(schoolYear_input, "senior") == 0) newNode->schoolYear = senior;
// 3. Append Node to List
if (list == NULL) {
list = newNode;
} else {
current = list;
while (current->next != NULL) {
current = current->next;
}
current->next = newNode;
}
// 4. Sort The List (Bubble Sort)
int swapped;
struct studentRecord* ptr1;
if (list == NULL || list->next == NULL) {
return 1; // Already sorted.
}
do {
swapped = 0;
ptr1 = list;
while (ptr1->next != NULL) {
if (strcmp(ptr1->studentName, ptr1->next->studentName) > 0) {
swapNodes(ptr1, ptr1->next);
swapped = 1;
}
ptr1 = ptr1->next;
}
} while (swapped);
return 1; // Success!
}