Week 12 Laboratory Sample Solutions

Objectives

  • More complex linked lists
  • Practice working with lists of lists

Activities To Be Completed

The following is a list of all the activities available to complete this week...

Worth 0.7 mark(s) in total:

  • list_get_middle
  • list_create

Worth 0.7 mark(s) in total:

  • list_delete_contains_string
  • list_delete_highest

Worth 0.4 mark(s) in total:

  • musical_chairs

Problem sets are capped at 15 marks (there are 4 possible bonus marks from the three-dot exercises that can bring you up to a total of 15 if you missed out on any other marks in the one- or two-dot exercises).

Completing just the one and two-dot exercises every week can give you the full 15 marks needed in this component.

For more details, see the course outline.

Exercise
(●◌◌)
:

Get the middle element from a Linked List

Download list_get_middle.c here, or copy it to your CSE account using the following command:

cp -n /import/reed/A/dp1091/public_html/24T3/activities/list_get_middle/list_get_middle.c .

Your task is to add code to this function in list_get_middle.c:

// Return middle element of a linked list
// if list contains [6,7,8,9,10]  8 is returned
// if a list has even number of elements, first of middle two elements returned
// if list contains [1,2,3,4] 2 is returned
// list can not be empty
int get_middle(struct node *head) {

    // PUT YOUR CODE HERE (change the next line!)
    return 42;

}

get_middle is given one argument, head which is the pointer to the first node in a linked list.

Add code to get_middle so that its returns the middle value of the list. If the list an even number of elements the first of the 2 elements in the middle of the list should be returned.

For example if the linked list contains these 8 elements:

1, 7, 8, 9, 13, 19, 21, 42

get_middle should return 9 because 9 and 13 are the middle two elements

And, for example if the linked list contains these 5 elements:

1, 2, 8, 1, 42

get_middle should return 8 because it is the middle element.

get_middle can assume the list is not empty.

Testing

list_get_middle.c also contains a main function which allows you to test your get_middle function.

This main function:

  1. converts the inputs to a linked list,
  2. assigns a pointer to the first node in the linked list to head,
  3. calls list_get_middle(head) and
  4. prints the result.

Do not change this main function. If you want to change it, you have misread the question.

Your list_get_middle function will be called directly in marking. The main function is only to let you test your list_get_middle function

Examples

dcc list_get_middle.c -o list_get_middle
./list_get_middle
How many numbers in initial list?: 9
1 2 4 8 16 32 64 128 256
16
./list_get_middle
How many numbers in initial list?: 6
2 4 6 5 8 9
6
./list_get_middle
How many numbers in initial list?: 5
13 15 17 19 18
17
./list_get_middle
How many numbers in initial list?: 2
42 4
42
./list_get_middle
How many numbers in initial list?: 1
42
42

Assumptions/Restrictions/Clarifications

  • get_middle should return a single integer
  • get_middle can assume the list has at least one element
  • get_middle should not change the linked list it is given Your function should not change the next or data fields of list nodes
  • get_middle should not use arrays
  • get_middle should not call malloc
  • get_middle should not call scanf (or getchar or fgets)
  • get_middle should not print anything. It should not call printf
  • Do not change the supplied main function. It will not be tested or marked
New! You can run an automated code style checker using the following command:
1091 style list_get_middle.c
    

When you think your program is working, you can use autotest to run some simple automated tests:

1091 autotest list_get_middle

When you are finished working on this exercise, you must submit your work by running give:

give dp1091 lab12_list_get_middle list_get_middle.c
    

You must run give before Monday 18 November 09:00 to obtain the marks for this lab exercise. Note that this is an individual exercise, the work you submit with give must be entirely your own.

Sample solution for list_get_middle.c
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

struct node {
    struct node *next;
    int          data;
};

int get_middle(struct node *head);
int length(struct node *head);
struct node *array_to_list(int len, int array[]);

// DO NOT CHANGE THIS MAIN FUNCTION
#define MAX_INIT_LIST_LEN 100
int main(void) {
    // Need to read in a number of ints into an array
    printf("How many numbers in initial list?: ");
    int list_size = 0;
    scanf("%d", &list_size);
    int initial_elems[MAX_INIT_LIST_LEN] = {0};
    int n_read = 0;
    while (n_read < list_size && scanf("%d", &initial_elems[n_read])) {
        n_read++;
    }

    // create linked list from first set of inputs
    struct node *head = NULL;
    if (n_read > 0) {
        // list has elements
        head = array_to_list(n_read, initial_elems);
    }

    int result = get_middle(head);
    printf("%d\n", result);

    return 0;
}


// Return middle element of a linked list
// if list contains [6,7,8,9,10]  8 is returned
// if a list has even number of elements, first of middle two elements returned
// if list contains [1,2,3,4] 2 is returned
// list can not be empty
int get_middle(struct node *head) {
    assert(head != NULL);
    int len = length(head);
    int middle = (len - 1)/2;

    int i = 0;
    struct node *n = head;
    while (i < middle) {
        i = i + 1;
        n = n->next;
    }
    return n->data;
}

// Return length of a linked list.
int length(struct node *head) {
    int len = 0;
    struct node *n = head;
    while (n != NULL) {
        len = len + 1;
        n = n->next;
    }
    return len;
}


// DO NOT CHANGE THIS FUNCTION
// create linked list from array of strings
struct node *array_to_list(int len, int array[]) {
    struct node *head = NULL;
    int i = len - 1;
    while (i >= 0) {
        struct node *n = malloc(sizeof (struct node));
        assert(n != NULL);
        n->next = head;
        n->data = array[i];
        head = n;
        i -= 1;
    }   
    return head;
}

Exercise
(●◌◌)
:

Create a Linked List from Command Line Arguments, and then Free it

Download list_create.c here, or copy it to your CSE account using the following command:

cp -n /import/reed/A/dp1091/public_html/24T3/activities/list_create/list_create.c .

Complete the program list_create.c. The program converts command line arguments into a linked list, prints out the linked list, and then frees the linked list. It is your job to complete the function that creates the linked list (arguments_to_list) and the function that frees the linked list (free_list).

Examples

dcc list_create.c -o list_create
./list_create 5 3 2 9 4 1
5 -> 3 -> 2 -> 9 -> 4 -> 1 -> X
./list_create I love this COMP1511 exercise!!
I -> love -> this -> COMP1511 -> exercise!! -> X
./list_create 8915 10563 10821 9979 14640 8433 6894 125
8915 -> 10563 -> 10821 -> 9979 -> 14640 -> 8433 -> 6894 -> 125 -> X
./list_create
X

Assumptions/Restrictions/Clarifications

  • Any length of command line arguments can be given
  • Do not change the supplied main function. Only your arguments_to_list and free_list function will be tested
New! You can run an automated code style checker using the following command:
1091 style list_create.c
    

When you think your program is working, you can use autotest to run some simple automated tests:

1091 autotest list_create

When you are finished working on this exercise, you must submit your work by running give:

give dp1091 lab12_list_create list_create.c
    

You must run give before Monday 18 November 09:00 to obtain the marks for this lab exercise. Note that this is an individual exercise, the work you submit with give must be entirely your own.

Sample solution for list_create.c
// Create a list from Command Line Arguments
// list_create.c
//
// This program was written by Morgan Swaak (z5476300)
// on 5/04/24
//
// A program which creates and prints a linked list 
// from command line arguments!

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

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

struct node *arguments_to_list(int argc, char *argv[]);
void free_list(struct node *head);
void print_list(struct node *head);

int main(int argc, char **argv) {
    struct node *head = arguments_to_list(argc, argv);
    print_list(head);
    free_list(head);

    return 0;
}

// Create linked list from argument values
struct node *arguments_to_list(int argc, char *argv[]) {
    if (argc == 1) {
        return NULL;
    }
    struct node *head = malloc(sizeof(struct node));

    struct node *current = head;
    current->data = argv[1];
    current->next = NULL;
    int i = 2;
    while (i < argc) {
        struct node *new = malloc(sizeof(struct node));
        current->next = new;
        new->data = argv[i];
        new->next = NULL;
        current = new;
        i++;
    }
    return head;
}

// Free the linked list from memory
void free_list(struct node *head) {
    struct node *current = head;
    while (current != NULL) {
        struct node *delete = current;
        current = current->next;
        free(delete); 
    }
}

// Print the values of the linked list
void print_list(struct node *head) {
    struct node *current = head;
    while (current != NULL) {
        printf("%s -> ", current->data);
        current = current->next;
    }
    printf("X\n");
}

Exercise
(●●◌)
:

Delete first element containing a specific string from a linked list

Download list_delete_contains_string.c here, or copy it to your CSE account using the following command:

cp -n /import/reed/A/dp1091/public_html/24T3/activities/list_delete_contains_string/list_delete_contains_string.c .

Your task is to add code to this function in list_delete_contains_string.c:

// Delete the first node in the list containing the specific string
// The deleted node is freed.
// If no node contains the specified string, the list is not changed
// The head of the list is returned.
struct node *delete_contains(char string[MAX_SIZE], struct node *head) {
    return NULL;
}

Note list_delete_contains_string.c uses the following familiar data type:

struct node {
    struct node *next;
    char        data[MAX_SIZE];
};

delete_contains is given two argument, value and head.

  • data is a string with max size of 100 characters
  • head is the pointer to the first node in a linked list

The function is designed to delete the first node in the linked list that contains a specific string.

Add code to delete_contains so that it deletes the first node in the linked list that whose data field equals a specified string and returns a pointer to the new list.

  • If data does not occur in the linked list, the list should not be changed.

  • If data occurs more than once in the linked list, only the first occurrence should be deleted. Note, if the list is now empty delete_contains should return NULL.

delete_contains should call free to free the memory of the node it deletes.

Testing

list_delete_contains.c also contains a main function which allows you to test your delete_contains function.

This main function:

  1. takes in command line arguments
  2. converts the command line inputs to a linked list,
  3. assigns a pointer to the first node in the linked list to head,
  4. reads a sstring from standard input and assigns it to data,
  5. calls delete_contains(data, head) and
  6. prints the result.

Do not change this main function. If you want to change it, you have misread the question.

Your delete_contains function will be called directly in marking. The main function is only to let you test your delete_contains function

dcc list_delete_contains_string.c -o list_delete_contains_string
./list_delete_contains_string cats sleep 16 to 18 hours per day
Enter a string to delete: cats
[sleep, 16, to, 18, hours, per, day]
./list_delete_contains_string the bumblebee bat is the worlds smallest mammal
Enter a string to delete: bumblebee
[the, bat, is, the, worlds, smallest, mammal]
./list_delete_contains_string
Enter a string to delete: hello
[]
./list_delete_contains_string there are parts of Africa in all four hemispheres
Enter a string to delete: four
[there, are, parts, of, Africa, in, all, hemispheres]
./list_delete_contains_string the most money ever paid for a cow in an auction was 1.3 million dollars
Enter a string to delete: dollars
[the, most, money, ever, paid, for, a, cow, in, an, auction, was, 1.3, million]

Assumptions/Restrictions/Clarifications

  • delete_contains should call free to free the memory for the node it deletes
  • delete_contains should not change the data fields of list nodes.
  • delete_contains should not use arrays.
  • delete_contains should not call malloc.
  • delete_contains should not call scanf (or getchar or fgets).
  • delete_contains should not print anything. It should not call printf.
  • Do not change the supplied main function. It will not be tested or marked.
New! You can run an automated code style checker using the following command:
1091 style list_delete_contains_string.c
    

When you think your program is working, you can use autotest to run some simple automated tests:

1091 autotest list_delete_contains_string

When you are finished working on this exercise, you must submit your work by running give:

give dp1091 lab12_list_delete_contains_string list_delete_contains_string.c
    

You must run give before Monday 18 November 09:00 to obtain the marks for this lab exercise. Note that this is an individual exercise, the work you submit with give must be entirely your own.

Sample solution for list_delete_contains_string.c
// list_delete_contains_string.c
// This program removes the node in a linked list matching a given string
// Written by Sofia De Bellis (z5418801), on July 2023

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

#define MAX_SIZE 100

struct node {
    struct node *next;
    char        data[MAX_SIZE];
};


struct node *delete_contains(char string[MAX_SIZE], struct node *head);
struct node *array_to_list(int len, char *array[]);
void print_list(struct node *head);

// DO NOT CHANGE THIS MAIN FUNCTION
int main(int argc, char *argv[]) {
    // create linked list from command line arguments excluding the program name
    struct node *head = NULL;
    if (argc > 0) {
        // list has elements
        head = array_to_list(argc - 1, &argv[1]);
    }

    printf("Enter a string to delete: ");
    char string[MAX_SIZE];
    fgets(string, MAX_SIZE, stdin);
    string[strcspn(string, "\n")] = 0;
    struct node *new_head = delete_contains(string, head);
    print_list(new_head);

    return 0;
}


// Delete the first node in the list containing the specific string
// The deleted node is freed.
// If no node contains the specified string, the list is not changed
// The head of the list is returned.

struct node *delete_contains(char string[MAX_SIZE], struct node *head) {
    if (head == NULL) {
        // list is empty no node to delete
        return NULL;
    } else if (strcmp(head->data, string) == 0) {
        // deleting the first node
        struct node *new_head = head->next;
        free(head);
        return new_head;
    } else if (head->next == NULL) {
        // first node is the only node
        // first node is definitely not value
        return head;
    }

    struct node *n = head;
    // find node before first node containing i
    while (n->next->next != NULL && strcmp(n->next->data, string) != 0) {
        n = n->next;
    }

    if (strcmp(n->next->data, string) == 0) {
        struct node *new_next = n->next->next;
        free(n->next);
        n->next = new_next;
    }
    return head;
}

// DO NOT CHANGE THIS FUNCTION
// create linked list from array of strings
struct node *array_to_list(int len, char *array[]) {
    struct node *head = NULL;
    int i = len - 1;
    while (i >= 0) {
        struct node *n = malloc(sizeof (struct node));
        assert(n != NULL);
        n->next = head;
        strcpy(n->data, array[i]);
        head = n;
        i -= 1;
    }   
    return head;
}

// DO NOT CHANGE THIS FUNCTION
// print linked list
void print_list(struct node *head) {
    printf("[");    
    struct node *n = head;
    while (n != NULL) {
        // If you're getting an error here,
        // you have returned an invalid list
        printf("%s", n->data);
        if (n->next != NULL) {
            printf(", ");
        }
        n = n->next;
    }
    printf("]\n");
}

Exercise
(●●◌)
:

Remove the Highest Elements

Download list_delete_highest.c here, or copy it to your CSE account using the following command:

cp -n /import/reed/A/dp1091/public_html/24T3/activities/list_delete_highest/list_delete_highest.c .

Your task is to add code to this function in list_delete_highest.c:

//
// Delete the node(s) in the list that contain the highest value
// The deleted node(s) are freed.
// The head of the list is returned.
//
struct node *delete_highest(struct node *head) {

    // PUT YOUR CODE HERE (change the next line!)
    return NULL;

}

Note list_delete_highest.c uses the following familiar data type:

struct node {
    struct node *next;
    int          data;
};

delete_highest is given one argument, head

  • head is the pointer to the first node in a linked list

Add code to delete_highest so that it deletes the all nodes in the linked list whose data field are equal to the highest data value in the list

delete_highest should return a pointer to the new list

If the list is now empty delete_highest should return NULL

delete_highest should call free to free the memory of any node it deletes

For example if the linked list contains these 8 elements:

16, 7, 8, 19, 13, 19, 2, 12

delete_highest should return a pointer to a list with these elements:

16, 7, 8, 13, 2, 12

Testing

list_delete_highest.c also contains a main function which allows you to test your delete_highest function

This main function:

  1. converts the inputs to a linked list,
  2. assigns a pointer to the first node in the linked list to head,
  3. calls delete_highest(head) and
  4. prints the result.

Do not change this main function. If you want to change it, you have misread the question.

Your delete_highest function will be called directly in marking. The main function is only to let you test your delete_highest function

Examples

dcc list_delete_highest.c -o list_delete_highest
./list_delete_highest
Total numbers in list: 8
16 7 8 19 13 19 2 12
[16, 7, 8, 13, 2, 12]
./list_delete_highest
Total numbers in list: 5
200 150 27 200 200
[150, 27]
./list_delete_highest
Total numbers in list: 5
4 6 2 4 6
[4, 2, 4]
./list_delete_highest
Total numbers in list: 1
42
[]
./list_delete_highest
Total numbers in list: 0
[]

Assumptions/Restrictions/Clarifications

  • delete_highest should call free to free the memory for any nodes it deletes
  • delete_highest should not change the data fields of list nodes
  • delete_highest should not use arrays
  • delete_highest should not call malloc
  • delete_highest should not call scanf (or getchar or fgets)
  • delete_highest should not print anything. It should not call printf

Do not change the supplied main function. It will not be tested or marked.

New! You can run an automated code style checker using the following command:
1091 style list_delete_highest.c
    

When you think your program is working, you can use autotest to run some simple automated tests:

1091 autotest list_delete_highest

When you are finished working on this exercise, you must submit your work by running give:

give dp1091 lab12_list_delete_highest list_delete_highest.c
    

You must run give before Monday 18 November 09:00 to obtain the marks for this lab exercise. Note that this is an individual exercise, the work you submit with give must be entirely your own.

Sample solution for list_delete_highest.c
// List Delete Highest
// Will find the highest value stored in a linked list
// then will delete all occurences of that value
// in the list.

// Marc Chee (cs1511@cse.unsw.edu.au), April 2020

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

#define MAX_LIST_LEN 100
#define MIN_VALUE -1

struct node {
    struct node *next;
    int          data;
};

struct node *delete_highest(struct node *head);
int return_highest(struct node *head);
struct node *delete_contains(int value, struct node *head, int *num_removed);
struct node *array_to_list(int len, int array[]);
void print_list(struct node *head);

int main(void) {
    // scan in quantity of numbers
    printf("Total numbers in list: ");
    int list_size;
    scanf(" %d", &list_size);

    // scan numbers into array
    int n_read = 0;
    int numbers_list[MAX_LIST_LEN] = {0};
    while (n_read < list_size && scanf(" %d", &numbers_list[n_read])) {
        n_read++;
    }

    // create linked list from inputs
    struct node *head = array_to_list(n_read, numbers_list);

    struct node *new_head = delete_highest(head);
    print_list(new_head);

    return 0;
}

// deletes all occurences of the highest value
// nodes in a list
// returns the head of the list
struct node *delete_highest(struct node *head) {
    if (head == NULL) {
        return head;
    }
    int highest = return_highest(head);
    
    // keep removing elements until there are none
    // of the highest left
    int num_removed = 1;
    while (num_removed) {
        head = delete_contains(highest, head, &num_removed);
    }
    return head;
}

// returns the highest value in the list
// this function assumes that the list has
// at least one element!
int return_highest(struct node *head) {
    int highest = head->data;
    struct node *current = head->next;
    while (current != NULL) {
        if (current->data > highest) {
            highest = current->data;
        }
        current = current->next;
    }
    return highest;
}

// Delete the first node in the list containing i
// The deleted node is freed.
// If no node contains i, the list is not changed
// num_removed will contain how many elements were removed
// The head of the list is returned.

struct node *delete_contains(int value, struct node *head, int *num_removed) {
    *num_removed = 0;
    if (head == NULL) {
        // list is empty no node to delete
        return NULL;
    } else if (head->data == value) {
        // deleting the first node
        struct node *new_head = head->next;
        free(head);
        *num_removed = 1;
        return new_head;
    } else if (head->next == NULL) {
        // first node is the only node
        // first node is definitely not value
        return head;
    }

    struct node *n = head;
    // find node before first node containing i
    while (n->next->next != NULL && n->next->data != value) {
        n = n->next;
    }

    if (n->next->data == value) {
        struct node *new_next = n->next->next;
        free(n->next);
        n->next = new_next;
        *num_removed = 1;
    }
    return head;
}

// create linked list from array of ints
struct node *array_to_list(int len, int array[]) {
    struct node *head = NULL;
    int i = len - 1;
    while (i >= 0) {
        struct node *n = malloc(sizeof (struct node));
        assert(n != NULL);
        n->next = head;
        n->data = array[i];
        head = n;
        i -= 1;
    }   
    return head;
}

// print linked list
void print_list(struct node *head) {
    printf("[");    
    struct node *n = head;
    while (n != NULL) {
        // If you're getting an error here,
        // you have returned an invalid list
        printf("%d", n->data);
        if (n->next != NULL) {
            printf(", ");
        }
        n = n->next;
    }
    printf("]\n");
}

Exercise
(●●●)
:

Play the Game of Chairs. Win or die.

Download musical_chairs.c here, or copy it to your CSE account using the following command:

cp -n /import/reed/A/dp1091/public_html/24T3/activities/musical_chairs/musical_chairs.c .

Your task is to add code to this function in musical_chairs.c:

// Make music for a certain number of turns.
// Each turn of music makes the players move
// one chair along the list.
// After they've moved that many times, the
// first chair in the list is removed, along
// with the person sitting in it.
struct chair *make_music(int turns, struct chair *chairs) {
    // IMPLEMENT THIS FUNCTION
    return chairs;
}

Welcome to the Game of Chairs, where you either win or have your memory freed.

musical_chairs is written using the following structs that cannot be changed:

// player in the game of chairs
struct player {
    char name[MAX_NAME_LENGTH];
};

// A node in a linked list of chairs
struct chair {
    struct player *sitting;
    struct chair *next;
};

The chair struct is a linked list node.

The player struct represents a player that can sit on a chair (represented by the chair's pointer aiming at the player).

make_music is given a pointer to a chair, which is the first element in a list of chairs. It is also given an int turns which represents how many turns of movement there will be before the music stops.

Like the game of Musical Chairs, this program will have players move along the linked list, changing which chair they're sitting in.

In make_music, every player moves turns spaces along the linked list. Anyone who moves off the end of the linked list, should then move to the head of the list, so the players will end up rotating through the list as if it's a loop. This would be similar to if the next of the last chair was aimed at the first chair.

Once all the players have finished moving, the head of the list of chairs is removed. This means both that chair and the player sitting in it are removed from the game. make_music should then print out the name of the player that was removed.

Be careful to make sure you free all memory used in this game!

For example if a list of chairs called thrones looks like this:

throne points at the player named "Spoiler Alert"
throne points at the player named "Eddard Stark"
throne points at the player named "Joffrey Baratheon"
throne points at the player named "Cersei Lannister"
throne points at the player named "Robert Baratheon"

Then the following function is called:

make_music(3, thrones);

The output would be:

Joffrey Baratheon

and the resulting linked list would look like this:

(throne pointed at "Joffrey Baratheon" but was removed)
throne points at the player named "Cersei Lannister"
throne points at the player named "Robert Baratheon"
throne points at the player named "Spoiler Alert"
throne points at the player named "Eddard Stark"

In this list, all the players have moved down 3 chairs and are now sitting in different chairs. Anyone that moved past the end of the chairs was moved back to the top of the list of chairs.

Assumptions/Restrictions/Clarifications

  • You can assume the list provided to make_music will not be empty.
  • You can assume the number of turns will not be negative.
  • struct player and struct chair cannot be edited. They must be used as they are.
  • The be_seated function will help you create chairs. It cannot be edited and must be used as it is. You may not use arrays in this solution. Arrays are not necessary to complete this task.
  • You must free all memory used in your program. Use dcc --leak-check if you need to check for memory leaks. Autotest will also check your code for leaks
  • Your submitted file may contain a main function. It will not be tested or marked.
New! You can run an automated code style checker using the following command:
1091 style musical_chairs.c
    

When you think your program is working, you can use autotest to run some simple automated tests:

1091 autotest musical_chairs

When you are finished working on this exercise, you must submit your work by running give:

give dp1091 lab12_musical_chairs musical_chairs.c
    

You must run give before Monday 18 November 09:00 to obtain the marks for this lab exercise. Note that this is an individual exercise, the work you submit with give must be entirely your own.

Sample solution for musical_chairs.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_NAME_LENGTH 100

// Do not edit these structs. You may use them exactly as
// they are but you cannot make changes to them

// player in the game of chairs
struct player {
    char name[MAX_NAME_LENGTH];
};

// A node in a linked list of chairs
struct chair {
    struct player *sitting;
    struct chair *next;
};

// Move the players along to the next chair
// The chairs are acting as a loop, so if 
// a player moves off the end of the list,
// they'll end up back at the start
void move_players(struct chair *chairs) {
    struct chair *c = chairs;
    struct player *unseated = NULL;
    while (c != NULL) {
        struct player *moving = c->sitting;
        c->sitting = unseated;
        unseated = moving;
        c = c->next;
    }
    // the last unseated then goes back to the
    // first chair
    if (chairs != NULL) {
        chairs->sitting = unseated;
    }
}

// Make music for a certain number of turns
// Each turn of music makes the players move
// one chair along the list.
// After they've moved that many times, the
// first chair in the list is removed, along
// with the person sitting in it
struct chair *make_music(int turns, struct chair *chairs) {
    int i = 0;
    while (i < turns) {
        move_players(chairs);
        i++;
    }
    // remove the head!
    struct chair *beheaded = chairs;
    printf("%s\n", beheaded->sitting->name);
    chairs = chairs->next;
    free(beheaded->sitting);
    free(beheaded);
    
    return chairs;
}

// This helper function is only for this main, but
// it may help you to both understand and test this exercise.
// You may use this function for testing, but
// YOU CANNOT CHANGE THIS FUNCTION
struct chair *be_seated(char *name, struct chair *heir) {
    struct chair *c = malloc(sizeof(struct chair));
    c->sitting = malloc(sizeof(struct player));
    strcpy(c->sitting->name, name);
    c->next = heir;
    return c;
}

// This is a main function which could be used
// to test your make_music function.
// It will not be marked.
// Only your make_music function will be marked.
int main(void) {
    struct chair *thrones = be_seated("Robert Baratheon", NULL);
    thrones = be_seated("Cersei Lannister", thrones);
    thrones = be_seated("Joffrey Baratheon", thrones);
    thrones = be_seated("Eddard Stark", thrones);
    thrones = be_seated("Spoiler Alert", thrones);
        
    thrones = make_music(1, thrones);
    thrones = make_music(3, thrones);
    thrones = make_music(0, thrones);
    thrones = make_music(2, thrones);

    free(thrones->sitting);
    free(thrones);
        
    return 0;
}

Exercise — individual:
(Not For Marks) Debugging - List remove second last

Copy the program debug_remove_second_last.c from the course account to your directory by typing (make sure you type the dot at the end):

cp ~dp1091/public_html/24T3/activities/debug_remove_second_last/debug_remove_second_last.c .

Note that this exercise is not marked or worth marks!

Debugging Tips!

Some debugging tips for you:

  • dcc output - as you run into issues, dcc will point you to where the errors are. Remember that dcc gives you the line number the issue is on, and will give some sort of explanation. Make sure you read everything dcc gives you. Sometimes we get “errors carried forward”, so find your first error, fix that, then recompile.
  • print statements - sometimes it can be handy to see if the flow of your code puts you in the spot you expect it to be (ie. inside the right if statement, or going through a loop the correct amount of times). A quick way you can check this is by putting print statements in your code for testing purposes, like "the value of x is %d and y is %d". This lets you check that you got against what you expected.
  • DPST1091 debugging guide - https://cgi.cse.unsw.edu.au/~dp1091/24T3/resources/debugging_guide.html

The Task

This task takes in a linked list via command line arguments. Then, it attempt to remove the second last element of the list previously created via the delete_second_last.

For example if the existing list is 1 -> 2 -> 3 -> 4-> X, after calling the delete_second_last function on the list the list is as follows: 1 -> 2 -> 4 -> X. Note, the node containing the value 3 is removed from the list since it is the second last element in the list.

Currently it has some issues it is your job to figure them out and fix the code. Note, you should only need to modify the delete_second_last function to fix the program.

Examples

dcc debug_remove_second_last.c -o debug_remove_second_last
./debug_remove_second_last 1 2 3 4 5 6
Original list: [1, 2, 3, 4, 5, 6]
Modified list: [1, 2, 3, 4, 6]
./debug_remove_second_last 1 2 3
Original list: [1, 2, 3]
Modified list: [1, 3]
./debug_remove_second_last 1 2
Original list: [1, 2]
Modified list: [2]
./debug_remove_second_last 1
Original list: [1]
Modified list: []
./debug_remove_second_last 
Original list: []
Modified list: []

Assumptions/Restrictions/Clarifications

  • You may assume all command line arguments will be integers
New! You can run an automated code style checker using the following command:
1091 style debug_remove_second_last.c
    

When you think your program is working, you can use autotest to run some simple automated tests:

1091 autotest debug_remove_second_last
Sample solution for debug_remove_second_last.c
// debug_remove_second_last.c
// This program removes the second last node of a linked list
// Written by Sofia De Bellis (z5418801), on July 2023

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

struct node {
    struct node *next;
    int         data;
};


struct node *delete_second_last(struct node *head);
struct node *array_to_list(int len, char *array[]);
void print_list(struct node *head);
int get_list_length(struct node *head);

// DO NOT CHANGE THIS MAIN FUNCTION
int main(int argc, char *argv[]) {
    // create linked list from command line arguments excluding the program name
    struct node *head = NULL;
    if (argc > 0) {
        // list has elements
        head = array_to_list(argc - 1, &argv[1]);
    }
    printf("Original list: ");
    print_list(head);
    head = delete_second_last(head);
    printf("Modified list: ");
    print_list(head);

    return 0;
}


// Deletes the second last node in a linked list
// The deleted node is freed.
// The head of the list is returned.
struct node *delete_second_last(struct node *head) {
    if (head == NULL) {
        return NULL;
    }

    int list_length = get_list_length(head);

    // new node is head of list
    if (head == NULL || list_length == 1) {
        free(head);
        return NULL;
    } else if (list_length == 2) {
        struct node *temp = head;
        head = head->next;
        free(temp);
        return head;
    }

    struct node *current = head;
    while (current->next->next->next != NULL) {
        current = current->next;
    }

    struct node *temp = current->next;
    current->next = current->next->next;
    free(temp);

    return head;
}

// DO NOT CHANGE THIS FUNCTION
// create linked list from array of strings
struct node *array_to_list(int len, char *array[]) {
    struct node *head = NULL;
    int i = len - 1;
    while (i >= 0) {
        struct node *n = malloc(sizeof (struct node));
        assert(n != NULL);
        n->next = head;
        n->data = atoi(array[i]);
        head = n;
        i -= 1;
    }   
    return head;
}

// DO NOT CHANGE THIS FUNCTION
// print linked list
void print_list(struct node *head) {
    printf("[");    
    struct node *n = head;
    while (n != NULL) {
        // If you're getting an error here,
        // you have returned an invalid list
        printf("%d", n->data);
        if (n->next != NULL) {
            printf(", ");
        }
        n = n->next;
    }
    printf("]\n");
}

// DO NOT EDIT
// Gets the length of a linked lists
int get_list_length(struct node *head) {
    int length = 0;
    struct node *current = head;
    while (current != NULL) {
        length++;
        current = current->next;
    }
    return length;
}

Submission

When you are finished each exercises make sure you submit your work by running give.

You only need to do this if the exercise specifies a give command, otherwise - the exercise is not worth marks.

You can run give multiple times. Only your last submission will be marked.

Don't submit any exercises you haven't attempted.

If you are working at home, you may find it more convenient to upload your work via give's web interface.

Remember you have until Week 13 Monday 9:00am to submit your work.

You cannot obtain marks by e-mailing your code to tutors or lecturers.

You check the files you have submitted here.

Automarking will be run by the lecturer several days after the submission deadline, using test cases different to those autotest runs for you. (Hint: do your own testing as well as running autotest.)

After automarking is run by the lecturer you can view your results here. The resulting mark will also be available via give's web interface.

Lab Marks

When all components of a lab are automarked you should be able to view the the marks via give's web interface or by running this command on a CSE machine:

1091 classrun -sturec

Generative AI Permission Level

In completing this assessment, you are permitted to use standard editing and referencing functions in the software you use to complete your assessment. These functions are described below. You must not use any functions that generate or paraphrase passages of text or other media, whether based on your own work or not.

If your Convenor has concerns that your submission contains passages of AI-generated text or media, you may be asked to account for your work. If you are unable to satisfactorily demonstrate your understanding of your submission, you may be referred to UNSW Conduct & Integrity Office for investigation for academic misconduct and possible penalties.

DPST1091/CPTG1391 Specific Information

You are permitted to use the tools dcc-help to help you understand the error messages you may get when compiling the code you have written.

You are permitted to use autotest-help to help you understand why your code may not be passing the automated tests.

You are not permitted to submit code generated by automatic AI tools such as Github Copilot, ChatGPT, Google Bard in DPST1091/CPTG1391/COMP1511 for assignments. Submitting code generated by Github Copilot, ChatGPT, Google Bard and similar tools will be treated as plagiarism.

Our reasoning behind our decisions:

Systems such as Github Copilot and ChatGPT based on large language models or other generative artificial intelligence techniques, look likely to become heavily used by programmers. However, you need a good understanding of the language you are coding in and the systems involved before you can effectively use these tools. Using these tools to generate code for DPST1091/CPTG1391/COMP1511 instead of writing the code yourself will hinder your learning.