Author: Hayden Smith 2021
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:
Set data type: collection of unique 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?
Set SetCreate() // create a new set
void SetInsert(Set, int) // add number into set
void SetDelete(Set, int) // remove number from set
int SetMember(Set, int) // set membership test
int SetCard(Set) // size of set
Set SetUnion(Set, Set) // union
Set SetIntersect(Set, Set) // intersection
void SetDestroy(Set) // destroy a created set#ifndef SET_H
#define SET_H
#include <stdio.h>
#include <stdbool.h>
typedef struct SetRep *Set;
// ADT functions go here
#endifThree key principles of ADTs in C:
Notice how we haven't defined "struct SetRep"? That's not our job.
Set.h
// Set.h ... interface to Set ADT
#ifndef SET_H
#define SET_H
#include <stdio.h>
#include <stdbool.h>
typedef struct SetRep *Set;
Set SetCreate(); // create new empty set
void SetDestroy(Set); // free memory used by set
void SetInsert(Set,int); // add value into set
void SetDelete(Set,int); // remove value from set
bool SetMember(Set,int); // set membership
Set SetUnion(Set,Set); // union
Set SetIntersect(Set,Set); // intersection
int SetCard(Set); // cardinality
// others
Set SetCopy(Set); // make a copy of a set
void ShowSet(Set); // display set on stdout
#endifCompleted Set.h
But what's missing?
Programming by contract
// create new empty set
// pre: Integer provided is positive
// post: Valid set returned, set is empty
Set SetCreate();
// add value into set
// pre: Valid set provided
// post: New element "n" is now in set s
void SetInsert(Set s, int newval);
// pre: Valid set provided for s1 and s2
// post: ∀ n ∈ res, n ∈ s1 or n ∈ s2
Set SetUnion(Set s1, Set s2) { ... }
// cardinality
// pre: Valid set provided for s
// post: Response is the number of elements in the set
int SetCard(Set s);my_set.c
Set.c
my_set.o
Set.o
./my_set
Set.h
#include "Set.h"
#include <stdio.h>
int main() {
Set s = SetCreate();
// Could use Scanf instaed
for (int i = 1; i < 26; i += 2) {
SetInsert(s,i);
}
SetShow(s);
printf("\n");
}testSet1.c
It's time to implement the set! The "user" of our set doesn't need to worry about this.
We will implement 3 different types of sets:
#define MAX_ELEMS 10000
// concrete data structure
struct SetRep {
int elems[MAX_ELEMS];
int nelems;
};
Set SetCreate(int) { ... }
void SetInsert(Set, int) { ... }
void SetDelete(Set, int) { ... }
int SetMember(Set, int) { ... }
int SetCard(Set) { ... }
Set SetUnion(Set, Set) { ... }
Set SetIntersect(Set, Set) { ... }
void SetDestroy(Set) { ... }Set-array.c
We can represent this set using an array (unsorted). 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 set
// create new empty set
Set SetCreate()
{
Set s = malloc(sizeof(struct SetRep));
if (s == NULL) {
fprintf(stderr, "Insufficient memory\n");
exit(1);
}
s->nelems = 0;
// assert(isValid(s));
return s;
}
// set membership test
int SetMember(Set s, int n)
{
// assert(isValid(s));
int i;
for (i = 0; i < s->nelems; i++) {
if (s->elems[i] == n) {
return TRUE;
}
}
return FALSE;
}| Data Structure | insert (time) |
delete (time) |
member (time) |
union or intersection (time) |
storage (space) |
|---|---|---|---|---|---|
| unsorted array | O(n) | O(n) | O(n) | O(n * m) | O(E) |
Let's look at the time and space complexities:
Same data structure as for unsorted array. Differences:
| Data Structure | insert (time) |
delete (time) |
member (time) |
union or intersection (time) |
storage (space) |
|---|---|---|---|---|---|
| unsorted array | O(n) | O(n) | O(n) | O(n * m) | O(E) |
| sorted array | O(n) | O(n) | O(logn) | O(n * m) | O(E) |
typedef struct Node {
int value;
struct Node *next;
} Node;
struct SetRep {
Node *elems; // pointer to first node
int nelems; // number of nodes
};
Set SetCreate() { ... }
void SetInsert(Set, int) { ... }
void SetDelete(Set, int) { ... }
int SetMember(Set, int) { ... }
int SetCard(Set) { ... }
Set SetUnion(Set, Set) { ... }
Set SetIntersect(Set, Set) { ... }
void SetDestroy(Set) { ... }Set-list.c
// create new empty set
Set newSet()
{
Set s = malloc(sizeof(struct SetRep));
if (s == NULL) {...}
s->nelems = 0;
s->elems = s->last = NULL;
return s;
}
// set membership test
int SetMember(Set s, int n)
{
// assert(isValid(s));
Node *cur = s->elems;
while (cur != NULL) {
if (cur->value == n) return true;
cur = cur->next;
}
return false;
}
| Data Structure | insert (time) |
delete (time) |
member (time) |
union or intersection (time) |
storage (space) |
|---|---|---|---|---|---|
| unsorted array | O(n) | O(n) | O(n) | O(n * m) | O(E) |
| sorted array | O(n) | O(n) | O(logn) | O(n * m) | O(E) |
| unsorted linked list | O(n) | O(n) | O(n) | O(n * m) | O(n) |
What happens if we try to access elements of the implementation directly?
We might receive a "dereferencing pointer to incomplete type" error