Author: Sim Mautner 2023
Adapted from slides by: Hayden Smith 2021
https://slides.com/haydensmith/comp2521-21t2-2-2
Why?
What?
Can you think of some examples?
What is a data type?
ADT is a description of a data type that focuses on it's high level behaviour, without regard for how it is implemented underneath. This means:
List data type: ordered collection of integer values.
What will we figure out first?
Let's brainstorm!
Now we start to write this as C code!
Notice that we aren't implementing anything yet?
List ListCreate() // create a new list
void ListAddStart(List, int) // add number to the start
void ListAddEnd(List, int) // add number to the end
void ListDeleteStart(List) // remove number from start
void ListDeleteEnd(List) // remove number from end
int ListMember(List, int) // membership test
int ListSize(List) // size of list
void ListDestroy(List) // destroy a created list#ifndef LIST_H
#define LIST_H
#include <stdio.h>
#define TRUE 1
#define FALSE 0
typedef struct ListRep *List;
// ADT functions go here
#endifThree key principles of ADTs in C:
Notice how we haven't defined "struct List"? That's not our job.
List.h
// List.h ... interface to List ADT
#ifndef LIST_H
#define LIST_H
#include <stdio.h>
#define TRUE 1
#define FALSE 0
typedef struct ListRep *List;
List ListCreate(); // create a new list
void ListAddStart(List, int); // add number to the start
void ListAddEnd(List, int); // add number to the end
void ListDeleteStart(List); // remove number from start
void ListDeleteEnd(List); // remove number from end
int ListMember(List, int); // membership test
int ListSize(List); // size of list
void ListDestroy(List); // destroy a created list
List ListCopy(List); // make a copy of a set
void ListShow(List); // display set on stdout
#endifCompleted List.h
But what's missing?
Programming by contract
// create new empty list
// pre:
// post: Valid list returned, list is empty
List ListCreate();
// add value to the start of the list
// pre: Valid list provided
// post: New element "newval" is now at the start of list l,
// the rest of list l remains unchanged
void ListAddStart(List l, int newval);
// list size
// pre: Valid list provided for l
// post: Response is the number of elements in the list
int ListSize(List l);my_list.c
List.c
my_list.o
List.o
./my_list
List.h
#include "List.h"
#include <stdio.h>
int main() {
List l = ListCreate();
// Could use Scanf instaed
for (int i = 1; i < 26; i += 2) {
ListAddEnd(l,i);
}
ListShow(l);
printf("\n");
}testList1.c
It's time to implement the list! The "user" of our list doesn't need to worry about this.
We will implement 3 different types of list:
#include "List.h"
#define MAX_ELEMS 10000
// concrete data structure
struct ListRep {
int elems[MAX_ELEMS];
int nelems;
};
List ListCreate() {...}
void ListAddStart(List, int) {...}
void ListAddEnd(List, int) {...}
void ListDeleteStart(List) {...}
void ListDeleteEnd(List) {...}
int ListMember(List, int) {...}
int ListSize(List) {...}
void ListDestroy(List) {...}
List ListCopy(List) {...}
void ShowList(List) {...}List-array.c
We can represent this set using an array. This means we do have to do upper and lower bounds checks because there will be a theoretical limit on the size of the set.
A sample of the implemented list
// create new empty list
List ListCreate()
{
List l = malloc(sizeof(struct ListRep));
if (l == NULL) {
fprintf(stderr, "Insufficient memory\n");
exit(1);
}
l->nelems = 0;
// assert(isValid(s));
return l;
}
// list membership test
int ListMember(List l, int n)
{
// assert(isValid(s));
int i;
for (i = 0; i < l->nelems; i++) {
if (l->elems[i] == n) {
return TRUE;
}
}
return FALSE;
}| Data Structure | addStart/ deleteStart (time) |
insertEnd/ deleteEnd (time) |
member (time) |
storage (space) |
|---|---|---|---|---|
| array | O(n) | O(1) | O(n) | O(E) |
Let's look at the time and space complexities:
| Data Structure | addStart/ deleteStart (time) |
insertEnd/ deleteEnd (time) |
member (time) |
storage (space) |
|---|---|---|---|---|
| array | O(n) | O(1) | O(n) | O(E) |
| simple linked list | O(1) | O(n) | O(n) | O(n) |
// concrete data structure
typedef struct Node {
int value;
struct Node *next;
struct Node *prev;
} Node;
struct ListRep {
Node * head; // pointer to the first node
Node * tail; // pointer to the last node
int nelems; // number of elements
};list-doubly-linked.c
// list membership test
int ListMember(List l, int n)
{
// assert(isValid(s));
Node * curr = l->head;
while (curr != NULL) {
if (curr->value == n) {
return TRUE;
}
curr = curr->next;
}
return FALSE;
}// create new empty list
List ListCreate()
{
List l = malloc(sizeof(struct ListRep));
if (l == NULL) {
fprintf(stderr, "Insufficient memory\n");
exit(EXIT_FAILURE);
}
l->head = NULL;
l->tail = NULL;
l->nelems = 0;
// assert(isValid(l));
return l;
}
| Data Structure | addStart/ deleteStart (time) |
insertEnd/ deleteEnd (time) |
member (time) |
storage (space) |
|---|---|---|---|---|
| array | O(n) | O(1) | O(n) | O(E) |
| simple linked list | O(1) | O(n) | O(n) | O(n) |
| doubly linked list | O(1) | O(1) | O(n) | O(n) |
What happens if we try to access elements of the implementation directly?
We might receive a "dereferencing pointer to incomplete type" error
... and often importantly...
Example 1:
Example 2:
What other examples can you think of?
Stack:
Stack Examples:
Queue:
Queue Examples: