[AED] TP topics

- tema08
- tema09
- tema10
- tema11

Signed-off-by: TiagoRG <tiago.rgarcia@ua.pt>
This commit is contained in:
Tiago Garcia 2023-12-17 14:17:41 +00:00
parent 7423b92a6e
commit 14683d6f87
Signed by: TiagoRG
GPG Key ID: DFCD48E3F420DB42
91 changed files with 7102 additions and 0 deletions

Binary file not shown.

View File

@ -14,6 +14,9 @@
| [06](https://github.com/TiagoRG/uaveiro-leci/tree/master/2ano/1semestre/aed/teoricas/tema06) | Tipos Abstratos | | [06](https://github.com/TiagoRG/uaveiro-leci/tree/master/2ano/1semestre/aed/teoricas/tema06) | Tipos Abstratos |
| [07](https://github.com/TiagoRG/uaveiro-leci/tree/master/2ano/1semestre/aed/teoricas/tema07) | Listas Ligadas | | [07](https://github.com/TiagoRG/uaveiro-leci/tree/master/2ano/1semestre/aed/teoricas/tema07) | Listas Ligadas |
| [08](https://github.com/TiagoRG/uaveiro-leci/tree/master/2ano/1semestre/aed/teoricas/tema08) | Árvores Binárias | | [08](https://github.com/TiagoRG/uaveiro-leci/tree/master/2ano/1semestre/aed/teoricas/tema08) | Árvores Binárias |
| [09](https://github.com/TiagoRG/uaveiro-leci/tree/master/2ano/1semestre/aed/teoricas/tema09) | Dicionários |
| [10](https://github.com/TiagoRG/uaveiro-leci/tree/master/2ano/1semestre/aed/teoricas/tema10) | Grafos |
| [11](https://github.com/TiagoRG/uaveiro-leci/tree/master/2ano/1semestre/aed/teoricas/tema11) | C++ |
--- ---
*Pode conter erros, caso encontre algum, crie um* [*ticket*](https://github.com/TiagoRG/uaveiro-leci/issues/new) *Pode conter erros, caso encontre algum, crie um* [*ticket*](https://github.com/TiagoRG/uaveiro-leci/issues/new)

View File

@ -0,0 +1,184 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira / Joao Manuel Rodrigues
//
// Binary Max Heap storing pointers to generic items.
//
#include "MaxHeap.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
//
// A binary or 2-way heap is a complete tree stored in an array.
//
// +-------------------------------------------------------------------------------+
// | 0 |
// +---------------------------------------+---------------------------------------+
// | 1 | 2 |
// +-------------------+-------------------+-------------------+-------------------+
// | 3 | 4 | 5 | 6 |
// +---------+---------+---------+---------+---------+---------+---------+---------+
// | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14
// |
// +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
// | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
// 30 |
// +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
//
// The children of node k, if any, are 2*k+1 and 2*k+2.
//
// The value of each node cannot be SMALLER that those of its children.
//
// The heap data structure
struct _Heap {
void** array;
int capacity;
int size;
compFunc compare;
printFunc print;
};
MaxHeap* MaxHeapCreate(int capacity, compFunc compF, printFunc printF) {
MaxHeap* h = (MaxHeap*)malloc(sizeof(MaxHeap)); // alloc heap header
if (h == NULL) abort();
h->array = (void**)malloc(capacity * sizeof(void*)); // alloc array
if (h->array == NULL) {
free(h);
abort();
}
h->capacity = capacity;
h->size = 0;
h->compare = compF;
h->print = printF;
return h;
}
void MaxHeapDestroy(MaxHeap** pph) {
MaxHeap* ph = *pph;
if (ph == NULL) return;
free(ph->array);
free(ph);
*pph = NULL;
}
int MaxHeapCapacity(const MaxHeap* ph) { return ph->capacity; }
int MaxHeapSize(const MaxHeap* ph) { return ph->size; }
int MaxHeapIsEmpty(const MaxHeap* ph) { return ph->size == 0; }
int MaxHeapIsFull(const MaxHeap* ph) { return ph->size == ph->capacity; }
void* MaxHeapGetMax(const MaxHeap* ph) {
assert(!MaxHeapIsEmpty(ph));
return ph->array[0];
}
// Internal functions
// n is the index of a node (n in [0, size[).
// _child(n, 1) is the index of the first child of node n, if < size.
// _child(n, 2) is the index of the second child of node n, if < size.
static inline int _child(int n, int c) { return 2 * n + c; }
// _parent(n) is the index of the parent node of node n, if n>0.
static inline int _parent(int n) {
assert(n > 0);
return (n - 1) / 2;
}
// Insert the item into the heap
void MaxHeapInsert(MaxHeap* ph, void* item) {
assert(!MaxHeapIsFull(ph));
// start at the first vacant spot (just after the last occupied node)
int n = ph->size;
while (n > 0) {
int p = _parent(n);
// if item not larger than _parent, then we've found the right spot!
if (ph->compare(item, ph->array[p]) <= 0) break;
// otherwise, move down the item at node p to open up space for new item
ph->array[n] = ph->array[p];
// update
n = p; // p is the new vacant spot
}
ph->array[n] = item; // store item at node n
ph->size++;
}
// Remove the Max item
void MaxHeapRemoveMax(MaxHeap* ph) {
assert(!MaxHeapIsEmpty(ph));
ph->size--; // NOTE: we're decreasing the size first!
int n = 0; // the just emptied spot... must fill it with largest child
while (1) {
// index of first child
int max = _child(n, 1); // first child (might not exist)
if (!(max < ph->size)) break; // if no second child, stop looking
// if second child is larger, choose it
if (ph->compare(ph->array[max + 1], ph->array[max]) > 0) {
max = max + 1;
}
// if largest child is not larger than last, stop looking
if (!(ph->compare(ph->array[max], ph->array[ph->size]) > 0)) break;
// move largest child to fill empty _parent spot
ph->array[n] = ph->array[max];
n = max; // now, the largest child spot was just emptied!
}
// move last element to emptied spot
ph->array[n] = ph->array[ph->size];
// mark last element as vacant
ph->array[ph->size] = NULL;
}
// Check the (max-)heap property (the heap invariant):
// Each node must be >= than each of its children.
// Equivalently (but easier):
// Each node must be <= its parent.
int MaxHeapCheck(const MaxHeap* ph) {
// For each node other than root: compare with its parent
for (int n = 1; n < ph->size; n++) {
int p = _parent(n);
if (ph->compare(ph->array[n], ph->array[p]) > 0) return 0;
}
return 1;
}
// Visualize the heap items as a tree
static void _HeapView(const MaxHeap* ph, int level, const char* edge,
int root) {
if (root < ph->size) {
_HeapView(ph, level + 1, "/", _child(root, 1));
printf("%*s", 4 * level, edge);
ph->print(ph->array[root]);
printf("\n");
_HeapView(ph, level + 1, "\\", _child(root, 2));
}
}
// Visualize the heap both as a tree and as an array.
void MaxHeapView(const MaxHeap* ph) {
printf("tree:\n");
_HeapView(ph, 0, ":", 0); // : marks the root
printf("array:");
for (int i = 0; i < ph->size; i++) {
printf(" ");
ph->print(ph->array[i]);
}
printf("\nsize: %d\n", ph->size);
}

View File

@ -0,0 +1,51 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira / Joao Manuel Rodrigues
//
// Binary Max Heap storing pointers to generic items.
//
#ifndef _MAX_HEAP_
#define _MAX_HEAP_
// The type for MaxHeap instances
typedef struct _Heap MaxHeap;
// The type for item comparator functions
typedef int (*compFunc)(const void* p1, const void* p2);
// The type for item printer functions
typedef void (*printFunc)(void* p);
// CREATE/DESTROY
MaxHeap* MaxHeapCreate(int capacity, compFunc compF, printFunc printF);
void MaxHeapDestroy(MaxHeap** pph);
// GETTERS
int MaxHeapCapacity(const MaxHeap* ph);
int MaxHeapSize(const MaxHeap* ph);
int MaxHeapIsEmpty(const MaxHeap* ph);
int MaxHeapIsFull(const MaxHeap* ph);
void* MaxHeapGetMax(const MaxHeap* ph);
// MODIFY
void MaxHeapInsert(MaxHeap* ph, void* item);
void MaxHeapRemoveMax(MaxHeap* ph);
// CHECK/VIEW
int MaxHeapCheck(const MaxHeap* ph);
void MaxHeapView(const MaxHeap* ph);
#endif

View File

@ -0,0 +1,91 @@
//
// João Manuel Rodrigues, AlgC, May 2020
// Joaquim Madeira, AlgC, May 2020
//
// TESTING the TAD MaxHeap implementation
//
// This program accepts multiple arguments.
// If the argument is:
// a number : it is inserted into a max-heap.
// - : the first (maximum) item is removed from the head
// ? : the heap content is shown, both in tree form and array form.
// Try the arguments below.
// ARGS 1 2 3 4 5 6 ?
// ARGS 6 5 4 3 2 1 ?
// ARGS 3 5 2 1 6 4 ?
// ARGS 5 6 3 1 4 2 ?
// ARGS 5 6 3 ? 1 ? 4 ? 2 ?
// ARGS 8 4 9 4 3 1 10 2 5 7 11 6 ?
// ARGS 5 6 3 ? - ? 1 ? 4 ? 2 ? - ? - ? - ? - ?
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "MaxHeap.h"
// Storing pointers to integers
// The comparator for integer items
int comparator(const void* p1, const void* p2) {
int d = *(int*)p1 - *(int*)p2;
return (d > 0) - (d < 0);
}
// The printer for integer items
void printer(void* p) { printf("%d ", *(int*)p); }
int main(int argc, char* argv[]) {
printf("CREATE AN EMPTY HEAP\n");
// with capacity for at most argc items
MaxHeap* h1 = MaxHeapCreate(argc, comparator, printer);
printf("Capacity = %d\n", MaxHeapCapacity(h1));
printf("Size = %d\n", MaxHeapSize(h1));
printf("\nPROCESS ARGS\n");
for (int i = 1; i < argc; i++) {
int* aux;
char* arg = argv[i];
printf("ARG %s: ", arg);
switch (arg[0]) {
case '?': // View and Check
printf("View\n");
MaxHeapView(h1); // for debugging
printf("Check: %s\n", MaxHeapCheck(h1) ? "OK" : "ERROR");
break;
case '-':
aux = (int*)MaxHeapGetMax(h1);
printf("Removing %d\n", *aux);
MaxHeapRemoveMax(h1);
free(aux);
break;
default: // assume it's an item to insert
aux = (int*)malloc(sizeof(*aux));
*aux = atoi(arg);
printf("Inserting %d\n", *aux);
MaxHeapInsert(h1, aux);
}
}
printf("\nFINISHED ARGS\n");
printf("Size = %d\n", MaxHeapSize(h1));
printf("\nREMOVING REMAINING ITEMS\n");
while (!MaxHeapIsEmpty(h1)) {
int* aux = (int*)MaxHeapGetMax(h1);
printf("Removing %d\n", *aux);
MaxHeapRemoveMax(h1);
free(aux);
}
printf("\nFINISHED REMOVING\n");
MaxHeapView(h1);
MaxHeapDestroy(&h1);
return 0;
}

View File

@ -0,0 +1,176 @@
// JMR, 2021
// Complete the functions (marked by ...)
// so that it passes all tests in DateTest.
#include "Date.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
const Date DateMIN = {0, 1, 1};
const Date DateMAX = {9999, 12, 31};
// Check if a yy,mm,dd tuple forms a valid date.
// (This would be a public static method in Java.)
int DateIsValid(int yy, int mm, int dd) {
return (DateMIN.year) <= yy && yy <= (DateMAX.year) && 1 <= mm && mm <= 12 &&
1 <= dd && dd <= DateDaysInMonth(yy, mm);
}
// Function to test desired internal invariant for valid Date values:
// the Date should contain valid year,month,day fields.
static int invariant(Date* d) { return DateIsValid(d->year, d->month, d->day); }
// Alocate and store a date given by yy, mm, dd integers.
// (yy, mm, dd) are required to form a valid date.
// Returns the pointer to the new date structure,
// or NULL if allocation fails.
Date* DateCreate(int yy, int mm, int dd) {
assert(DateIsValid(yy, mm, dd));
// JMADEIRA
Date* d = malloc(sizeof(*d));
if (d == NULL) {
return NULL;
}
d->year = (uint16_t)yy;
d->month = (uint8_t)mm;
d->day = (uint8_t)dd;
assert(invariant(d)); // check invariant
return d;
}
// Free the memory pointed to by *pd and invalidate *pd contents.
// Precondition: *pd must not be NULL.
// Postcondition: *pd is set to NULL.
void DateDestroy(Date** pd) {
assert(*pd != NULL);
// JMADEIRA
free(*pd);
*pd = NULL;
}
// table of month lengths in common and leap years
static const uint8_t monthLength[][12] = {
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
int DateDaysInMonth(int yy, int mm) {
int isLeap = DateIsLeapYear(yy);
return monthLength[isLeap][mm - 1];
}
int DateIsLeapYear(int yy) {
return (yy % 4 == 0 && yy % 100 != 0) || yy % 400 == 0;
}
static char* fmts[] = {
(char*)"%04d-%02d-%02d", // YMD
(char*)"%3$02d/%2$02d/%1$04d", // DMY
(char*)"%2$02d/%3$02d/%1$04d", // MDY
};
static char strBuffer[64];
// Return a formatted string representation of date d.
// Careful: the string buffer will be overwritten by the next call.
// You should strcpy or strdup the result if you need persistence!
char* DateFormat(const Date* d, int FMT) {
if (d == NULL)
snprintf(strBuffer, sizeof(strBuffer), "NULL");
else
snprintf(strBuffer, sizeof(strBuffer), fmts[FMT], d->year, d->month,
d->day);
return strBuffer;
}
// Parse str according to format and return NEW Date,
// or NULL if invalid or memory error.
Date* DateParse1(const char* str, int FMT) {
int yy, mm, dd;
int n = sscanf(str, fmts[FMT], &yy, &mm, &dd);
Date* d = NULL;
if (n == 3 && DateIsValid(yy, mm, dd)) {
d = DateCreate(yy, mm, dd);
}
return d;
}
// Parse str according to format and fill in *d.
// Return 1 and update *d if str contains a correctly formatted and valid date.
// Return 0 and leave *d untouched, otherwise.
int DateParse(Date* d, const char* str, int FMT) {
int yy, mm, dd;
int n = sscanf(str, fmts[FMT], &yy, &mm, &dd);
if (!(n == 3 && DateIsValid(yy, mm, dd))) return 0;
d->year = yy;
d->month = mm;
d->day = dd;
return 1;
}
// Compare dates a and b.
// Return an integer >0 if a>b, 0 if a==b and <0 if a<b.
int DateCompare(const Date* a, const Date* b) {
// JMADEIRA
int r = (int)a->year - (int)b->year;
if (r != 0) {
return r;
}
r = (int)a->month - (int)b->month;
if (r != 0) {
return r;
}
r = (int)a->day - (int)b->day;
return r;
}
// Increment date.
// Precondition: d must precede DateMAX.
void DateIncr(Date* d) {
assert(DateCompare(d, &DateMAX) < 0);
// JMADEIRA
if ((int)d->day < DateDaysInMonth((int)(d->year), (int)(d->month))) {
d->day++;
} else {
d->day = 1;
if ((int)d->month < 12) {
d->month++;
} else {
d->month = (uint8_t)1;
d->year++;
}
}
assert(invariant(d)); // check invariant
}
// Decrement date.
// Precondition: d must succeed DateMIN.
void DateDecr(Date* d) {
assert(DateCompare(d, &DateMIN) > 0);
// JMADEIRA
if ((int)d->day > 1) {
d->day--;
} else {
// decrMonth
if ((int)d->month > 1) {
d->month--;
} else {
d->year--;
d->month = (uint8_t)12;
}
d->day = DateDaysInMonth((int)(d->year), (int)(d->month));
}
assert(invariant(d)); // check invariant
}

View File

@ -0,0 +1,49 @@
// JMR, 2021
#ifndef _DATE_
#define _DATE_
#include <inttypes.h>
struct _date {
uint16_t year;
uint8_t month;
uint8_t day;
};
// The Date type. (To be used in clients.)
typedef struct _date Date;
// Constants
extern const Date DateMIN;
extern const Date DateMAX;
// Macros to select date format
#define YMD 0
#define DMY 1
#define MDY 2
Date* DateCreate(int yy, int mm, int dd);
void DateDestroy(Date** pd);
int DateIsValid(int yy, int mm, int dd);
int DateDaysInMonth(int yy, int mm);
int DateIsLeapYear(int yy);
char* DateFormat(const Date* d, int FMT);
Date* DateParse1(const char* str, int FMT);
int DateParse(Date* d, const char* str, int FMT);
int DateCompare(const Date* a, const Date* b);
void DateIncr(Date* d);
void DateDecr(Date* d);
#endif //_DATE_

View File

@ -0,0 +1,184 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira / Joao Manuel Rodrigues
//
// Binary Max Heap storing pointers to generic items.
//
#include "MaxHeap.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
//
// A binary or 2-way heap is a complete tree stored in an array.
//
// +-------------------------------------------------------------------------------+
// | 0 |
// +---------------------------------------+---------------------------------------+
// | 1 | 2 |
// +-------------------+-------------------+-------------------+-------------------+
// | 3 | 4 | 5 | 6 |
// +---------+---------+---------+---------+---------+---------+---------+---------+
// | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14
// |
// +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
// | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
// 30 |
// +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
//
// The children of node k, if any, are 2*k+1 and 2*k+2.
//
// The value of each node cannot be SMALLER that those of its children.
//
// The heap data structure
struct _Heap {
void** array;
int capacity;
int size;
compFunc compare;
printFunc print;
};
MaxHeap* MaxHeapCreate(int capacity, compFunc compF, printFunc printF) {
MaxHeap* h = (MaxHeap*)malloc(sizeof(MaxHeap)); // alloc heap header
if (h == NULL) abort();
h->array = (void**)malloc(capacity * sizeof(void*)); // alloc array
if (h->array == NULL) {
free(h);
abort();
}
h->capacity = capacity;
h->size = 0;
h->compare = compF;
h->print = printF;
return h;
}
void MaxHeapDestroy(MaxHeap** pph) {
MaxHeap* ph = *pph;
if (ph == NULL) return;
free(ph->array);
free(ph);
*pph = NULL;
}
int MaxHeapCapacity(const MaxHeap* ph) { return ph->capacity; }
int MaxHeapSize(const MaxHeap* ph) { return ph->size; }
int MaxHeapIsEmpty(const MaxHeap* ph) { return ph->size == 0; }
int MaxHeapIsFull(const MaxHeap* ph) { return ph->size == ph->capacity; }
void* MaxHeapGetMax(const MaxHeap* ph) {
assert(!MaxHeapIsEmpty(ph));
return ph->array[0];
}
// Internal functions
// n is the index of a node (n in [0, size[).
// _child(n, 1) is the index of the first child of node n, if < size.
// _child(n, 2) is the index of the second child of node n, if < size.
static inline int _child(int n, int c) { return 2 * n + c; }
// _parent(n) is the index of the parent node of node n, if n>0.
static inline int _parent(int n) {
assert(n > 0);
return (n - 1) / 2;
}
// Insert the item into the heap
void MaxHeapInsert(MaxHeap* ph, void* item) {
assert(!MaxHeapIsFull(ph));
// start at the first vacant spot (just after the last occupied node)
int n = ph->size;
while (n > 0) {
int p = _parent(n);
// if item not larger than _parent, then we've found the right spot!
if (ph->compare(item, ph->array[p]) <= 0) break;
// otherwise, move down the item at node p to open up space for new item
ph->array[n] = ph->array[p];
// update
n = p; // p is the new vacant spot
}
ph->array[n] = item; // store item at node n
ph->size++;
}
// Remove the Max item
void MaxHeapRemoveMax(MaxHeap* ph) {
assert(!MaxHeapIsEmpty(ph));
ph->size--; // NOTE: we're decreasing the size first!
int n = 0; // the just emptied spot... must fill it with largest child
while (1) {
// index of first child
int max = _child(n, 1); // first child (might not exist)
if (!(max < ph->size)) break; // if no second child, stop looking
// if second child is larger, choose it
if (ph->compare(ph->array[max + 1], ph->array[max]) > 0) {
max = max + 1;
}
// if largest child is not larger than last, stop looking
if (!(ph->compare(ph->array[max], ph->array[ph->size]) > 0)) break;
// move largest child to fill empty _parent spot
ph->array[n] = ph->array[max];
n = max; // now, the largest child spot was just emptied!
}
// move last element to emptied spot
ph->array[n] = ph->array[ph->size];
// mark last element as vacant
ph->array[ph->size] = NULL;
}
// Check the (max-)heap property (the heap invariant):
// Each node must be >= than each of its children.
// Equivalently (but easier):
// Each node must be <= its parent.
int MaxHeapCheck(const MaxHeap* ph) {
// For each node other than root: compare with its parent
for (int n = 1; n < ph->size; n++) {
int p = _parent(n);
if (ph->compare(ph->array[n], ph->array[p]) > 0) return 0;
}
return 1;
}
// Visualize the heap items as a tree
static void _HeapView(const MaxHeap* ph, int level, const char* edge,
int root) {
if (root < ph->size) {
_HeapView(ph, level + 1, "/", _child(root, 1));
printf("%*s", 4 * level, edge);
ph->print(ph->array[root]);
printf("\n");
_HeapView(ph, level + 1, "\\", _child(root, 2));
}
}
// Visualize the heap both as a tree and as an array.
void MaxHeapView(const MaxHeap* ph) {
printf("tree:\n");
_HeapView(ph, 0, ":", 0); // : marks the root
printf("array:");
for (int i = 0; i < ph->size; i++) {
printf(" ");
ph->print(ph->array[i]);
}
printf("\nsize: %d\n", ph->size);
}

View File

@ -0,0 +1,51 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira / Joao Manuel Rodrigues
//
// Binary Max Heap storing pointers to generic items.
//
#ifndef _MAX_HEAP_
#define _MAX_HEAP_
// The type for MaxHeap instances
typedef struct _Heap MaxHeap;
// The type for item comparator functions
typedef int (*compFunc)(const void* p1, const void* p2);
// The type for item printer functions
typedef void (*printFunc)(void* p);
// CREATE/DESTROY
MaxHeap* MaxHeapCreate(int capacity, compFunc compF, printFunc printF);
void MaxHeapDestroy(MaxHeap** pph);
// GETTERS
int MaxHeapCapacity(const MaxHeap* ph);
int MaxHeapSize(const MaxHeap* ph);
int MaxHeapIsEmpty(const MaxHeap* ph);
int MaxHeapIsFull(const MaxHeap* ph);
void* MaxHeapGetMax(const MaxHeap* ph);
// MODIFY
void MaxHeapInsert(MaxHeap* ph, void* item);
void MaxHeapRemoveMax(MaxHeap* ph);
// CHECK/VIEW
int MaxHeapCheck(const MaxHeap* ph);
void MaxHeapView(const MaxHeap* ph);
#endif

View File

@ -0,0 +1,105 @@
// JMR, 2021
// Complete the functions (marked by ...)
// so that it passes all tests in PersonTest.
#include "Person.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// This variable stores the last ID that was assigned to a Person.
// It should be used to assign serial, unique IDs automatically upon creation.
// The first person will have id=1, the second id=2, etc.
static int lastID = 0;
// Alocate and store a Person.
// Returns the pointer to the new structure,
// or NULL if allocation fails.
// The names are copied to internally allocated memory.
Person *PersonCreate(const char *fname, const char *lname, int yy, int mm,
int dd) {
// JMADEIRA
Person *p = malloc(sizeof(*p));
if (p == NULL) {
return p;
}
// Allocating memory for the name strings
size_t flen = strnlen(fname, 100);
size_t llen = strnlen(lname, 100);
char *s1 = malloc((flen + 1) * sizeof(char));
if (s1 == NULL) {
free(p);
return NULL;
}
char *s2 = malloc((llen + 1) * sizeof(char));
if (s2 == NULL) {
free(s1);
free(p);
return NULL;
}
p->id = ++lastID;
// DIRECT ACCESS
p->birthDate.year = (uint16_t)yy;
p->birthDate.month = (uint8_t)mm;
p->birthDate.day = (uint8_t)dd;
p->firstName = s1;
p->lastName = s2;
strcpy(p->firstName, fname); // copy the names
strcpy(p->lastName, lname);
return p;
}
// Free the memory pointed to by *pp and by the names inside it,
// and invalidate *pp contents.
// Precondition: *pp must not be NULL.
// Postcondition: *pp is set to NULL.
void PersonDestroy(Person **pp) {
assert(*pp != NULL);
// JMADEIRA
free((*pp)->firstName); // free the names
free((*pp)->lastName);
free(*pp);
*pp = NULL;
}
// Prints a person formatted as "[id, lastname, firstname, birthdate]",
// followed by a suffix string.
void PersonPrintf(Person *p, const char *suffix) {
if (p == NULL)
printf("NULL%s", suffix);
else
printf("(%d, %s, %s, %s)%s", p->id, p->lastName, p->firstName,
DateFormat(&(p->birthDate), YMD), suffix);
}
// Compare birth dates of two persons.
// Return a negative/positive integer if p1 was born before/after p2.
// Return zero if p1 and p2 were born on the same date.
int PersonCompareByBirth(const Person *p1, const Person *p2) {
// JMADEIRA
return DateCompare(&(p1->birthDate), &(p2->birthDate));
}
// Compare two persons by last name, then first name (if last name is the same).
// Return a -/+/0 integer if p1 precedes/succeeds/is equal to p2.
int PersonCompareByLastFirstName(const Person *p1, const Person *p2) {
// JMADEIRA
int cmp = strcmp(p1->lastName, p2->lastName);
if (cmp != 0) {
return cmp;
}
cmp = strcmp(p1->firstName, p2->firstName);
return cmp;
}

View File

@ -0,0 +1,27 @@
// JMR, 2021
#ifndef _PERSON_
#define _PERSON_
#include "Date.h"
typedef struct {
int id;
Date birthDate; // this is the actual structure, not a pointer!
char *firstName;
char *lastName;
} Person;
Person *PersonCreate(const char *fname, const char *lname, int yy, int mm,
int dd);
void PersonDestroy(Person **pd);
void PersonPrintf(Person *p, const char *suffix);
int PersonCompareByBirth(const Person *p1, const Person *p2);
int PersonCompareByLastFirstName(const Person *p1, const Person *p2);
#endif // PERSON

View File

@ -0,0 +1,86 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira / Joao Manuel Rodrigues
//
// Creating Max Heaps of persons, with different comparator functions.
//
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "MaxHeap.h"
#include "Person.h"
// Comparators for Persons
int ComparatorByBirthday(const void* p1, const void* p2) {
return PersonCompareByBirth((Person*)p1, (Person*)p2);
}
int ComparatorByLastFirstName(const void* p1, const void* p2) {
return PersonCompareByLastFirstName((Person*)p1, (Person*)p2);
}
// The printer for Persons
void Printer(void* p) { PersonPrintf((Person*)p, " "); }
int main(void) {
// An array of persons
const int NP = 10; // number of persons
Person* person[NP];
person[0] = PersonCreate("Eva", "Maia", 1977, 10, 20);
person[1] = PersonCreate("Maria", "Silva", 2003, 12, 30);
person[2] = PersonCreate("Paulo", "Guedes", 2003, 12, 31);
person[3] = PersonCreate("Carlos", "Silva", 1999, 8, 31);
person[4] = PersonCreate("Filipe", "Matos", 2001, 1, 1);
person[5] = PersonCreate("Carla", "Oliveira", 1959, 1, 1);
person[6] = PersonCreate("Vitor", "Maia", 1969, 2, 28);
person[7] = PersonCreate("Olga", "Costa", 1967, 2, 29);
person[8] = PersonCreate("Tiago", "Santos", 1996, 6, 13);
person[9] = PersonCreate("Sara", "Santos", 2007, 3, 1);
printf("\n--- Create a Max Heap, ordered according to person bithday ---\n");
MaxHeap* heap_1 = MaxHeapCreate(NP, ComparatorByBirthday, Printer);
printf("\n--- Create a Max Heap, ordered according to person name ---\n");
MaxHeap* heap_2 = MaxHeapCreate(NP, ComparatorByLastFirstName, Printer);
printf("\n--- Adding persons to the heaps ---\n");
for (size_t n = 0; n < 6; n++) {
int i = rand() % NP;
MaxHeapInsert(heap_1, person[i]);
MaxHeapInsert(heap_2, person[i]);
}
printf("\n--- Printing the heaps ---\n");
MaxHeapView(heap_1);
MaxHeapView(heap_2);
printf("\n--- Listing heap contents in non-increasing birthday order ---\n");
while (!MaxHeapIsEmpty(heap_1)) {
Person* p = MaxHeapGetMax(heap_1);
PersonPrintf(p, "\n");
MaxHeapRemoveMax(heap_1);
}
printf("\n--- Listing heap contents in non-increasing name order ---\n");
while (!MaxHeapIsEmpty(heap_2)) {
Person* p = MaxHeapGetMax(heap_2);
PersonPrintf(p, "\n");
MaxHeapRemoveMax(heap_2);
}
// Free everything
// (If you comment out any of these, valgrind should detect it!)
for (int i = 0; i < NP; i++) {
PersonDestroy(&(person[i]));
}
MaxHeapDestroy(&heap_1);
MaxHeapDestroy(&heap_2);
return 0;
}

View File

@ -0,0 +1,150 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira, May 2020, November 2023
//
// Simple Hash Table using Open Addressing
// Key and Value are STRINGS - To simplify the example
//
#include <stdio.h>
#include <stdlib.h>
#include "HashTable.h"
int main(void) {
// FIRST HT - Use the first key character only - Linear Probing
printf(
"\nHT 1 --- Hashing with first key character only"
"\n --- Linear Probing\n");
HashTable* table01 = HashTableCreate(17, hash1, linearProbing, 0);
HashTablePut(table01, "January", "1st month of the year");
HashTablePut(table01, "February", "2nd month of the year");
HashTablePut(table01, "March", "3rd month");
HashTablePut(table01, "April", "4th month");
HashTablePut(table01, "May", "5th month");
HashTablePut(table01, "June", "6th month");
HashTablePut(table01, "July", "7th month");
HashTablePut(table01, "August", "8th month");
HashTablePut(table01, "September", "9th month");
HashTablePut(table01, "October", "10th month");
HashTablePut(table01, "November", "11th month");
HashTablePut(table01, "December", "12th month");
HashTableDisplay(table01);
printf("\nReplacing some table elements\n");
HashTableReplace(table01, "December", "The last month of the year");
HashTableReplace(table01, "February", "The second month of the year");
HashTableReplace(table01, "November", "Almost at the end of the year");
HashTableDisplay(table01);
char* string = HashTableGet(table01, "December");
printf("\n*** %s = %s ***\n\n", "December", string);
free(string);
// SECOND HT - Use the first key character only - Quadratic Probing
printf(
"\nHT 2 --- Hashing with first key character only"
"\n --- Quadratic Probing\n");
HashTable* table02 = HashTableCreate(17, hash1, quadraticProbing, 0);
HashTablePut(table02, "January", "1st month of the year");
HashTablePut(table02, "February", "2nd month of the year");
HashTablePut(table02, "March", "3rd month");
HashTablePut(table02, "April", "4th month");
HashTablePut(table02, "May", "5th month");
HashTablePut(table02, "June", "6th month");
HashTablePut(table02, "July", "7th month");
HashTablePut(table02, "August", "8th month");
HashTablePut(table02, "September", "9th month");
HashTablePut(table02, "October", "10th month");
HashTablePut(table02, "November", "11th month");
HashTablePut(table02, "December", "12th month");
HashTableDisplay(table02);
printf("\nReplacing some table elements\n");
HashTableReplace(table02, "December", "The last month of the year");
HashTableReplace(table02, "February", "The second month of the year");
HashTableReplace(table02, "November", "Almost at the end of the year");
HashTableDisplay(table02);
string = HashTableGet(table02, "December");
printf("\n*** %s = %s ***\n\n", "December", string);
free(string);
// THIRD HT - Use the first and second key characters - Linear Probing
printf(
"\nHT 3 --- Hashing with first and second key characters"
"\n --- Linear Probing"
"\n --- Resizing allowed\n");
HashTable* table03 = HashTableCreate(17, hash2, linearProbing, 1);
HashTablePut(table03, "January", "1st month of the year");
HashTablePut(table03, "February", "2nd month of the year");
HashTablePut(table03, "March", "3rd month");
HashTablePut(table03, "April", "4th month");
HashTablePut(table03, "May", "5th month");
HashTablePut(table03, "June", "6th month");
HashTablePut(table03, "July", "7th month");
HashTablePut(table03, "August", "8th month");
HashTablePut(table03, "September", "9th month");
HashTablePut(table03, "October", "10th month");
HashTablePut(table03, "November", "11th month");
HashTablePut(table03, "December", "12th month");
HashTableDisplay(table03);
HashTableRemove(table03, "January");
HashTableRemove(table03, "February");
HashTableRemove(table03, "March");
HashTableRemove(table03, "April");
HashTableDisplay(table03);
HashTableRemove(table03, "May");
HashTableRemove(table03, "June");
HashTableRemove(table03, "July");
HashTableRemove(table03, "August");
HashTableDisplay(table03);
HashTableRemove(table03, "September");
HashTableRemove(table03, "October");
HashTableRemove(table03, "November");
HashTableRemove(table03, "December");
HashTableDisplay(table03);
// 4TH HT - Use the first and second key characters - Quadratic Probing
printf(
"\nHT 4 --- Hashing with first and second key characters"
"\n --- Quadratic Probing"
"\n --- Resizing allowed\n");
HashTable* table04 = HashTableCreate(17, hash2, quadraticProbing, 1);
HashTablePut(table04, "January", "1st month of the year");
HashTablePut(table04, "February", "2nd month of the year");
HashTablePut(table04, "March", "3rd month");
HashTablePut(table04, "April", "4th month");
HashTablePut(table04, "May", "5th month");
HashTablePut(table04, "June", "6th month");
HashTablePut(table04, "July", "7th month");
HashTablePut(table04, "August", "8th month");
HashTablePut(table04, "September", "9th month");
HashTablePut(table04, "October", "10th month");
HashTablePut(table04, "November", "11th month");
HashTablePut(table04, "December", "12th month");
HashTableDisplay(table04);
// DESTROYING
HashTableDestroy(&table01);
HashTableDestroy(&table02);
HashTableDestroy(&table03);
HashTableDestroy(&table04);
return 0;
}

View File

@ -0,0 +1,342 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira, May 2020, November 2023
//
// Simple Hash Table using Open Addressing
// Key and Value are STRINGS - To simplify the example
//
#include "HashTable.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct _HashTableBin {
char* key;
char* value;
int isDeleted;
int isFree;
};
struct _HashTableHeader {
unsigned int size;
unsigned int numActive;
unsigned int numUsed;
hashFunction hashF;
probeFunction probeF;
int resizeIsEnabled;
struct _HashTableBin* table;
};
// Auxiliary functions for experimenting
unsigned int hash1(const char* key) {
assert(strlen(key) > 0);
return key[0];
}
unsigned int hash2(const char* key) {
assert(strlen(key) > 0);
if (strlen(key) == 1) return key[0];
return key[0] + key[1];
}
unsigned int linearProbing(unsigned int index, unsigned int i,
unsigned int size) {
return (index + i) % size;
}
//
// Simple quadratic probing --- improve !!
//
unsigned int quadraticProbing(unsigned int index, unsigned int i,
unsigned int size) {
return (index + i * i) % size;
}
// TAD Functions
HashTable* HashTableCreate(unsigned int size, hashFunction hashF,
probeFunction probeF, int resizeIsEnabled) {
assert(size > 0);
HashTable* hTable = (HashTable*)malloc(sizeof(struct _HashTableHeader));
assert(hTable != NULL);
hTable->table =
(struct _HashTableBin*)malloc(size * sizeof(struct _HashTableBin));
assert(hTable->table != NULL);
hTable->size = size;
hTable->numActive = 0;
hTable->numUsed = 0;
hTable->hashF = hashF;
hTable->probeF = probeF;
hTable->resizeIsEnabled = resizeIsEnabled;
for (unsigned int i = 0; i < size; i++) {
hTable->table[i].key = NULL;
hTable->table[i].value = NULL;
hTable->table[i].isFree = 1;
hTable->table[i].isDeleted = 0;
}
return hTable;
}
void HashTableDestroy(HashTable** p) {
assert(*p != NULL);
HashTable* t = *p;
for (unsigned int i = 0; i < t->size; i++) {
if (t->table[i].key) free(t->table[i].key);
if (t->table[i].value) free(t->table[i].value);
}
free(t->table);
free(t);
*p = NULL;
}
static void _resizeHashTable(HashTable* hashT, unsigned int newSize) {
if (newSize == 0) return;
// Do this is a better way: choose a near prime
if (newSize % 2 == 0) {
newSize++;
}
struct _HashTableBin* oldBinsArray = hashT->table;
unsigned int oldSize = hashT->size;
hashT->table =
(struct _HashTableBin*)malloc(newSize * sizeof(struct _HashTableBin));
assert(hashT->table != NULL);
for (unsigned int i = 0; i < newSize; i++) {
hashT->table[i].key = NULL;
hashT->table[i].value = NULL;
hashT->table[i].isFree = 1;
hashT->table[i].isDeleted = 0;
}
hashT->size = newSize;
hashT->numActive = 0;
hashT->numUsed = 0;
// Copy items to the new array of bins
// Disable resizing while copying
hashT->resizeIsEnabled = 0;
for (unsigned int i = 0; i < oldSize; i++) {
if (oldBinsArray[i].isFree || oldBinsArray[i].isDeleted) {
continue;
}
HashTablePut(hashT, oldBinsArray[i].key, oldBinsArray[i].value);
}
hashT->resizeIsEnabled = 1;
// Free the old array
for (unsigned int i = 0; i < oldSize; i++) {
if (oldBinsArray[i].key) free(oldBinsArray[i].key);
if (oldBinsArray[i].value) free(oldBinsArray[i].value);
}
free(oldBinsArray);
}
// HashTable properties
int HashTableIsEmpty(const HashTable* hashT) { return hashT->numActive == 0; }
int HashTableIsFull(const HashTable* hashT) {
return hashT->numUsed == hashT->size;
}
// Getters
int HashTableGetNumberOfItems(const HashTable* hashT) {
return hashT->numActive;
}
double HashTableGetLoadFactor(const HashTable* hashT) {
return (double)hashT->numActive / (double)hashT->size;
}
// Operations with items
//
// If the key belongs to the table, return the corresponding index
// If not, return one (next) available index or -1, if the table is full
//
static int _searchHashTable(const HashTable* hashT, const char* key) {
unsigned int hashKey = hashT->hashF(key);
unsigned int index;
struct _HashTableBin* bin;
for (unsigned int i = 0; i < hashT->size; i++) {
index = hashT->probeF(hashKey, i, hashT->size);
bin = &(hashT->table[index]);
if (bin->isFree) {
// Not in the table !
return index;
}
if ((bin->isDeleted == 0) && (strcmp(bin->key, key) == 0)) {
// Found it !
return index;
}
}
// SHOULD NEVER HAPPEN !!
return -1;
}
int HashTableContains(const HashTable* hashT, const char* key) {
int result = _searchHashTable(hashT, key);
if (result == -1 || hashT->table[result].isFree == 1) {
// NOT FOUND
return 0;
}
return 1;
}
char* HashTableGet(const HashTable* hashT, const char* key) {
int index = _searchHashTable(hashT, key);
if (index == -1 || hashT->table[index].isFree == 1) {
// NOT FOUND
return NULL;
}
struct _HashTableBin* bin = &(hashT->table[index]);
char* result = (char*)malloc(sizeof(char) * (1 + strlen(bin->value)));
strcpy(result, bin->value);
return result;
}
int HashTablePut(HashTable* hashT, const char* key, const char* value) {
int result = _searchHashTable(hashT, key);
if (result == -1) {
// NO PLACE AVAILABLE
return 0;
}
if (hashT->table[result].isFree == 0) {
// ALREADY IN THE TABLE
return 0;
}
// Does NOT BELONG to the table
// See if it can be stored earlier in the chain, by starting again
// Losing some efficiency here
unsigned int hashKey = hashT->hashF(key);
unsigned int index;
struct _HashTableBin* bin;
for (unsigned int i = 0; i < hashT->size; i++) {
index = hashT->probeF(hashKey, i, hashT->size);
bin = &(hashT->table[index]);
if (bin->isFree) {
bin->key = (char*)malloc(sizeof(char) * (1 + strlen(key)));
strcpy(bin->key, key);
bin->value = (char*)malloc(sizeof(char) * (1 + strlen(value)));
strcpy(bin->value, value);
bin->isFree = bin->isDeleted = 0;
hashT->numActive++;
hashT->numUsed++;
if (hashT->resizeIsEnabled && HashTableGetLoadFactor(hashT) > 0.5) {
_resizeHashTable(hashT, hashT->size * 2);
}
return 1;
}
if (bin->isDeleted) {
bin->key = (char*)malloc(sizeof(char) * (1 + strlen(key)));
strcpy(bin->key, key);
bin->value = (char*)malloc(sizeof(char) * (1 + strlen(value)));
strcpy(bin->value, value);
bin->isFree = bin->isDeleted = 0;
hashT->numActive++;
return 1;
}
}
return 0;
}
int HashTableReplace(const HashTable* hashT, const char* key,
const char* value) {
int index = _searchHashTable(hashT, key);
if (index == -1 || hashT->table[index].isFree == 1) {
// NOT FOUND
return 0;
}
struct _HashTableBin* bin = &(hashT->table[index]);
free(bin->value);
bin->value = (char*)malloc(sizeof(char) * (1 + strlen(value)));
strcpy(bin->value, value);
return 1;
}
int HashTableRemove(HashTable* hashT, const char* key) {
int index = _searchHashTable(hashT, key);
if (index == -1 || hashT->table[index].isFree == 1) {
// NOT FOUND
return 0;
}
// Mark as deleted to keep the chain
hashT->table[index].isDeleted = 1;
hashT->numActive--;
free(hashT->table[index].key);
free(hashT->table[index].value);
hashT->table[index].key = hashT->table[index].value = NULL;
if (hashT->resizeIsEnabled && HashTableGetLoadFactor(hashT) < 0.125) {
_resizeHashTable(hashT, hashT->size / 2);
}
return 1;
}
// DISPLAYING on the console
void HashTableDisplay(const HashTable* hashT) {
printf("---\n");
printf("size = %2d | Used = %2d | Active = %2d\n", hashT->size,
hashT->numUsed, hashT->numActive);
for (unsigned int i = 0; i < hashT->size; i++) {
printf("%3d - ", i);
printf("Free = %d - ", hashT->table[i].isFree);
printf("Deleted = %d - ", hashT->table[i].isDeleted);
if (hashT->table[i].key) {
unsigned int hashValue = hashT->hashF(hashT->table[i].key);
unsigned int firstIndex = hashValue % hashT->size;
printf("Hash = %4d, 1st index = %3d, (%s, %s)\n", hashValue, firstIndex,
hashT->table[i].key, hashT->table[i].value);
} else
printf("\n");
}
printf("---\n");
}

View File

@ -0,0 +1,66 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira, May 2020, November 2023
//
// Simple Hash Table using Open Addressing
// Key and Value are STRINGS - To simplify the example
//
#ifndef _HASH_TABLE_
#define _HASH_TABLE_
typedef struct _HashTableHeader HashTable;
typedef unsigned int (*hashFunction)(const char* key);
typedef unsigned int (*probeFunction)(unsigned int index, unsigned int i,
unsigned int size);
// Auxiliary functions for experimenting
unsigned int hash1(const char* key);
unsigned int hash2(const char* key);
unsigned int linearProbing(unsigned int index, unsigned int i,
unsigned int size);
unsigned int quadraticProbing(unsigned int index, unsigned int i,
unsigned int size);
// TAD Functions
HashTable* HashTableCreate(unsigned int capacity, hashFunction hashF,
probeFunction probeF, int resizeIsEnabled);
void HashTableDestroy(HashTable** p);
// HashTable properties
int HashTableIsEmpty(const HashTable* hashT);
int HashTableIsFull(const HashTable* hashT);
// Getters
int HashTableGetNumberOfItems(const HashTable* hashT);
double HashTableGetLoadFactor(const HashTable* hashT);
// Operations with items
int HashTableContains(const HashTable* hashT, const char* key);
char* HashTableGet(const HashTable* hashT, const char* key);
int HashTablePut(HashTable* hashT, const char* key, const char* value);
int HashTableReplace(const HashTable* hashT, const char* key,
const char* value);
int HashTableRemove(HashTable* hashT, const char* key);
// DISPLAYING on the console
void HashTableDisplay(const HashTable* hashT);
#endif // _HASH_TABLE_

View File

@ -0,0 +1,77 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira, May 2020, November 2023
//
// Simple exmaple for COUNTING WORDS using Open Addressing
// Keys are STRINGS - Values are INTEGERS
// Adapted from TOS - AED - 2015
//
#include <stdio.h>
#include <stdlib.h>
#include "HashTable.h"
void registerWords(HashTable* hashT, FILE* file) {
//
// scan the entire file
//
// token processing SHOULD BE improved !!!!!
//
char line[10000];
char word[64];
int i;
int j;
while (fgets(line, sizeof(line), file) != NULL) {
// replace non-digits and non-letters by a space
for (i = 0; line[i] != '\0'; i++)
if ((line[i] < '0' || line[i] > '9') &&
(line[i] < 'A' || line[i] > 'Z') &&
(line[i] < 'a' || line[i] > 'z') && line[i] != '\'')
line[i] = ' ';
// go over the line and extract each word
for (i = 0; line[i] != '\0'; i += j)
if (line[i] == ' ')
j = 1;
else {
for (j = 0; line[i + j] != '\0' && line[i + j] != ' '; j++)
if (j < 63) word[j] = line[i + j];
word[(j < 63) ? j : 63] = '\0';
HashTableIncrement(hashT, word);
}
}
}
int main(int argc, char** argv) {
if (argc == 1) {
printf("program_name file_name\n");
return 0;
}
FILE* file = fopen(argv[1], "r");
if (file == NULL) {
fprintf(stderr, "Unable to open file %s\n", argv[1]);
exit(1);
}
// HT - Use the first two key characters only - Linear Probing - Resizing
HashTable* countsTable = HashTableCreate(50021, hash2, linearProbing, 1);
registerWords(countsTable, file);
HashTableDisplayItems(countsTable);
// Closing the file
fclose(file);
// DESTROYING
HashTableDestroy(&countsTable);
return 0;
}

View File

@ -0,0 +1,363 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira, May 2020, November 2023
//
// Simple Hash Table for COUNTING WORDS using Open Addressing
// Keys are STRINGS - Values are INTEGERS
//
#include "HashTable.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct _HashTableBin {
char* key;
unsigned int value;
int isDeleted;
int isFree;
};
struct _HashTableHeader {
unsigned int size;
unsigned int numActive;
unsigned int numUsed;
hashFunction hashF;
probeFunction probeF;
int resizeIsEnabled;
struct _HashTableBin* table;
};
// Auxiliary functions for experimenting
unsigned int hash1(const char* key) {
assert(strlen(key) > 0);
return key[0];
}
unsigned int hash2(const char* key) {
assert(strlen(key) > 0);
if (strlen(key) == 1) return key[0];
return key[0] + key[1];
}
unsigned int linearProbing(unsigned int index, unsigned int i,
unsigned int size) {
return (index + i) % size;
}
//
// Simple quadratic probing --- improve !!
//
unsigned int quadraticProbing(unsigned int index, unsigned int i,
unsigned int size) {
return (index + i * i) % size;
}
// TAD Functions
HashTable* HashTableCreate(unsigned int size, hashFunction hashF,
probeFunction probeF, int resizeIsEnabled) {
assert(size > 0);
HashTable* hTable = (HashTable*)malloc(sizeof(struct _HashTableHeader));
assert(hTable != NULL);
hTable->table =
(struct _HashTableBin*)malloc(size * sizeof(struct _HashTableBin));
assert(hTable->table != NULL);
hTable->size = size;
hTable->numActive = 0;
hTable->numUsed = 0;
hTable->hashF = hashF;
hTable->probeF = probeF;
hTable->resizeIsEnabled = resizeIsEnabled;
for (unsigned int i = 0; i < size; i++) {
hTable->table[i].key = NULL;
hTable->table[i].value = 0;
hTable->table[i].isFree = 1;
hTable->table[i].isDeleted = 0;
}
return hTable;
}
void HashTableDestroy(HashTable** p) {
assert(*p != NULL);
HashTable* t = *p;
for (unsigned int i = 0; i < t->size; i++) {
if (t->table[i].key) free(t->table[i].key);
}
free(t->table);
free(t);
*p = NULL;
}
static void _resizeHashTable(HashTable* hashT, unsigned int newSize) {
if (newSize == 0) return;
// Do this is a better way: choose a near prime
if (newSize % 2 == 0) {
newSize++;
}
struct _HashTableBin* oldBinsArray = hashT->table;
unsigned int oldSize = hashT->size;
hashT->table =
(struct _HashTableBin*)malloc(newSize * sizeof(struct _HashTableBin));
assert(hashT->table != NULL);
for (unsigned int i = 0; i < newSize; i++) {
hashT->table[i].key = NULL;
hashT->table[i].value = 0;
hashT->table[i].isFree = 1;
hashT->table[i].isDeleted = 0;
}
hashT->size = newSize;
hashT->numActive = 0;
hashT->numUsed = 0;
// Copy items to the new array of bins
// Disable resizing while copying
hashT->resizeIsEnabled = 0;
for (unsigned int i = 0; i < oldSize; i++) {
if (oldBinsArray[i].isFree || oldBinsArray[i].isDeleted) {
continue;
}
HashTablePut(hashT, oldBinsArray[i].key, oldBinsArray[i].value);
}
hashT->resizeIsEnabled = 1;
// Free the old array
for (unsigned int i = 0; i < oldSize; i++) {
if (oldBinsArray[i].key) free(oldBinsArray[i].key);
}
free(oldBinsArray);
}
// HashTable properties
int HashTableIsEmpty(const HashTable* hashT) { return hashT->numActive == 0; }
int HashTableIsFull(const HashTable* hashT) {
return hashT->numUsed == hashT->size;
}
// Getters
int HashTableGetNumberOfItems(const HashTable* hashT) {
return hashT->numActive;
}
double HashTableGetLoadFactor(const HashTable* hashT) {
return (double)hashT->numActive / (double)hashT->size;
}
// Operations with items
//
// If the key belongs to the table, return the corresponding index
// If not, return one (next) available index or -1, if the table is full
//
static int _searchHashTable(const HashTable* hashT, const char* key) {
unsigned int hashKey = hashT->hashF(key);
unsigned int index;
struct _HashTableBin* bin;
for (unsigned int i = 0; i < hashT->size; i++) {
index = hashT->probeF(hashKey, i, hashT->size);
bin = &(hashT->table[index]);
if (bin->isFree) {
// Not in the table !
return index;
}
if ((bin->isDeleted == 0) && (strcmp(bin->key, key) == 0)) {
// Found it !
return index;
}
}
// SHOULD NEVER HAPPEN !!
return -1;
}
int HashTableContains(const HashTable* hashT, const char* key) {
int result = _searchHashTable(hashT, key);
if (result == -1 || hashT->table[result].isFree == 1) {
// NOT FOUND
return 0;
}
return 1;
}
unsigned int HashTableGet(const HashTable* hashT, const char* key) {
int index = _searchHashTable(hashT, key);
if (index == -1 || hashT->table[index].isFree == 1) {
// NOT FOUND
return 0;
}
return hashT->table[index].value;
}
int HashTablePut(HashTable* hashT, const char* key, unsigned int value) {
int result = _searchHashTable(hashT, key);
if (result == -1) {
// NO PLACE AVAILABLE
return 0;
}
if (hashT->table[result].isFree == 0) {
// ALREADY IN THE TABLE
return 0;
}
// Does NOT BELONG to the table
// See if it can be stored earlier in the chain, by starting again
// Losing some efficiency here
unsigned int hashKey = hashT->hashF(key);
unsigned int index;
struct _HashTableBin* bin;
for (unsigned int i = 0; i < hashT->size; i++) {
index = hashT->probeF(hashKey, i, hashT->size);
bin = &(hashT->table[index]);
if (bin->isFree) {
bin->key = (char*)malloc(sizeof(char) * (1 + strlen(key)));
strcpy(bin->key, key);
bin->value = value;
bin->isFree = bin->isDeleted = 0;
hashT->numActive++;
hashT->numUsed++;
if (hashT->resizeIsEnabled && HashTableGetLoadFactor(hashT) > 0.5) {
_resizeHashTable(hashT, hashT->size * 2);
}
return 1;
}
if (bin->isDeleted) {
bin->key = (char*)malloc(sizeof(char) * (1 + strlen(key)));
strcpy(bin->key, key);
bin->value = value;
bin->isFree = bin->isDeleted = 0;
hashT->numActive++;
return 1;
}
}
return 0;
}
//
// Increment the current value
// If key does not exist, insert it with a count of 1
//
int HashTableIncrement(HashTable* hashT, const char* key) {
int index = _searchHashTable(hashT, key);
if (index == -1) {
// NOT FOUND AND NO PLACE --- SHOULD NOT HAPPEN !!
return 0;
}
if (hashT->table[index].isFree == 0) {
// ALREADY IN THE TABLE - INCREMENT
hashT->table[index].value++;
return 1;
}
// NEW KEY - INSERT
// LOOSING SOME EFFICIENCY HERE
return HashTablePut(hashT, key, 1);
}
int HashTableReplace(const HashTable* hashT, const char* key,
unsigned int value) {
int index = _searchHashTable(hashT, key);
if (index == -1 || hashT->table[index].isFree == 1) {
// NOT FOUND
return 0;
}
hashT->table[index].value = value;
return 1;
}
int HashTableRemove(HashTable* hashT, const char* key) {
int index = _searchHashTable(hashT, key);
if (index == -1 || hashT->table[index].isFree == 1) {
// NOT FOUND
return 0;
}
// Mark as deleted to keep the chain
hashT->table[index].isDeleted = 1;
hashT->numActive--;
free(hashT->table[index].key);
hashT->table[index].key = NULL;
hashT->table[index].value = 0;
if (hashT->resizeIsEnabled && HashTableGetLoadFactor(hashT) < 0.125) {
_resizeHashTable(hashT, hashT->size / 2);
}
return 1;
}
// DISPLAYING on the console
void HashTableDisplay(const HashTable* hashT) {
printf("---\n");
printf("size = %2d | Used = %2d | Active = %2d\n", hashT->size,
hashT->numUsed, hashT->numActive);
for (unsigned int i = 0; i < hashT->size; i++) {
printf("%3d - ", i);
printf("Free = %d - ", hashT->table[i].isFree);
printf("Deleted = %d - ", hashT->table[i].isDeleted);
if (hashT->table[i].key) {
unsigned int hashValue = hashT->hashF(hashT->table[i].key);
unsigned int firstIndex = hashValue % hashT->size;
printf("Hash = %4d, 1st index = %3d, (%s, %d)\n", hashValue, firstIndex,
hashT->table[i].key, hashT->table[i].value);
} else
printf("\n");
}
printf("---\n");
}
void HashTableDisplayItems(const HashTable* hashT) {
printf("NUMBER OF ITEMS = %d\n\n", hashT->numActive);
for (unsigned int i = 0; i < hashT->size; i++) {
if (hashT->table[i].key) {
printf("%s %d\n", hashT->table[i].key, hashT->table[i].value);
}
}
}

View File

@ -0,0 +1,70 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira, May 2020, November 2023
//
// Simple Hash Table for COUNTING WORDS using Open Addressing
// Keys are STRINGS - Values are INTEGERS
//
#ifndef _HASH_TABLE_
#define _HASH_TABLE_
typedef struct _HashTableHeader HashTable;
typedef unsigned int (*hashFunction)(const char* key);
typedef unsigned int (*probeFunction)(unsigned int index, unsigned int i,
unsigned int size);
// Auxiliary functions for experimenting
unsigned int hash1(const char* key);
unsigned int hash2(const char* key);
unsigned int linearProbing(unsigned int index, unsigned int i,
unsigned int size);
unsigned int quadraticProbing(unsigned int index, unsigned int i,
unsigned int size);
// TAD Functions
HashTable* HashTableCreate(unsigned int capacity, hashFunction hashF,
probeFunction probeF, int resizeIsEnabled);
void HashTableDestroy(HashTable** p);
// HashTable properties
int HashTableIsEmpty(const HashTable* hashT);
int HashTableIsFull(const HashTable* hashT);
// Getters
int HashTableGetNumberOfItems(const HashTable* hashT);
double HashTableGetLoadFactor(const HashTable* hashT);
// Operations with items
int HashTableContains(const HashTable* hashT, const char* key);
unsigned int HashTableGet(const HashTable* hashT, const char* key);
int HashTablePut(HashTable* hashT, const char* key, unsigned int value);
int HashTableIncrement(HashTable* hashT, const char* key);
int HashTableReplace(const HashTable* hashT, const char* key,
unsigned int value);
int HashTableRemove(HashTable* hashT, const char* key);
// DISPLAYING on the console
void HashTableDisplay(const HashTable* hashT);
void HashTableDisplayItems(const HashTable* hashT);
#endif // _HASH_TABLE_

View File

@ -0,0 +1,10 @@
Hash Table using Open Addressing
Partially based on source code from
*The Joys of Hashing*
by Thomas Mailund
https://www.apress.com/9781484240656

View File

@ -0,0 +1,85 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira, May 2020, November 2023
//
// Simple Hash Table using Separate Chaining
// Key and Value are STRINGS - To simplify the example
//
#include <stdio.h>
#include <stdlib.h>
#include "HashTable.h"
int main(void) {
// FIRST HT - Use the first key character only
HashTable* table01 = HashTableCreate(17, hash1);
HashTablePut(table01, "January", "1st month of the year");
HashTablePut(table01, "February", "2nd month of the year");
HashTablePut(table01, "March", "3rd month");
HashTablePut(table01, "April", "4th month");
HashTablePut(table01, "May", "5th month");
HashTablePut(table01, "June", "6th month");
HashTablePut(table01, "July", "7th month");
HashTablePut(table01, "August", "8th month");
HashTablePut(table01, "September", "9th month");
HashTablePut(table01, "October", "10th month");
HashTablePut(table01, "November", "11th month");
HashTablePut(table01, "December", "12th month");
HashTableDisplay(table01);
HashTableReplace(table01, "December", "The last month of the year");
HashTableReplace(table01, "February", "The second month of the year");
HashTableReplace(table01, "November", "Almost at the end of the year");
HashTableDisplay(table01);
char* string = HashTableGet(table01, "December");
printf("\n*** %s = %s ***\n\n", "December", string);
free(string);
// SECOND HT - Use the first and second key characters
HashTable* table02 = HashTableCreate(17, hash2);
HashTablePut(table02, "January", "1st month of the year");
HashTablePut(table02, "February", "2nd month of the year");
HashTablePut(table02, "March", "3rd month");
HashTablePut(table02, "April", "4th month");
HashTablePut(table02, "May", "5th month");
HashTablePut(table02, "June", "6th month");
HashTablePut(table02, "July", "7th month");
HashTablePut(table02, "August", "8th month");
HashTablePut(table02, "September", "9th month");
HashTablePut(table02, "October", "10th month");
HashTablePut(table02, "November", "11th month");
HashTablePut(table02, "December", "12th month");
HashTableDisplay(table02);
HashTableRemove(table02, "January");
HashTableRemove(table02, "February");
HashTableRemove(table02, "March");
HashTableRemove(table02, "April");
HashTableDisplay(table02);
HashTableRemove(table02, "May");
HashTableRemove(table02, "June");
HashTableRemove(table02, "July");
HashTableRemove(table02, "August");
HashTableDisplay(table02);
HashTableRemove(table02, "September");
HashTableRemove(table02, "October");
HashTableRemove(table02, "November");
HashTableRemove(table02, "December");
HashTableDisplay(table02);
// DESTROYING
HashTableDestroy(&table01);
HashTableDestroy(&table02);
// return 0;
}

View File

@ -0,0 +1,238 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira, May 2020, November 2023
//
// Simple Hash Table using Separate Chaining
// Key and Value are STRINGS - To simplify the example
//
#include "HashTable.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "SortedList.h"
struct _HashTableBin {
char* key;
char* value;
};
struct _HashTableHeader {
unsigned int size;
unsigned int numBins;
hashFunction hashF;
List** table;
};
// The comparator: to compare a key (*p1) with the key on the list bin (*p2)
int comparator(const void* p1, const void* p2) {
return strcmp(((struct _HashTableBin*)p1)->key,
((struct _HashTableBin*)p2)->key);
}
// Auxiliary functions for experimenting
unsigned int hash1(const char* key) {
assert(strlen(key) > 0);
return key[0];
}
unsigned int hash2(const char* key) {
assert(strlen(key) > 0);
if (strlen(key) == 1) return key[0];
return key[0] + key[1];
}
// TAD Functions
HashTable* HashTableCreate(unsigned int size, hashFunction hashF) {
assert(size > 0);
HashTable* hTable = (HashTable*)malloc(sizeof(struct _HashTableHeader));
assert(hTable != NULL);
hTable->table = (List**)malloc(size * sizeof(List*));
assert(hTable->table != NULL);
hTable->size = size;
hTable->numBins = 0;
hTable->hashF = hashF;
for (unsigned int i = 0; i < size; i++) {
hTable->table[i] = ListCreate(comparator);
}
return hTable;
}
void HashTableDestroy(HashTable** p) {
assert(*p != NULL);
HashTable* t = *p;
// For each chain
for (unsigned int i = 0; i < t->size; i++) {
List* l = t->table[i];
// Free the HT bins of each list
while (ListIsEmpty(l) == 0) {
struct _HashTableBin* bin = ListRemoveHead(l);
free(bin->key);
free(bin->value);
free(bin);
}
// Destroy the list header
ListDestroy(&(t->table[i]));
}
free(t->table);
free(t);
*p = NULL;
}
// HashTable properties
int HashTableIsEmpty(const HashTable* hashT) { return hashT->numBins == 0; }
// Getters
unsigned int HashTableGetNumberOfItems(const HashTable* hashT) {
return hashT->numBins;
}
double HashTableGetLoadFactor(const HashTable* hashT) {
return (double)hashT->numBins / (double)hashT->size;
}
// Operations with items
//
// Search for the key
// If found, the list current node is updated
//
static int _searchKeyInList(List* l, char* key) {
if (ListIsEmpty(l)) {
return 0;
}
// Needed for the comparator
// Shallow copy of the key: just the pointer
struct _HashTableBin searched;
searched.key = key;
ListMoveToHead(l);
return ListSearch(l, &searched) != -1;
}
int HashTableContains(const HashTable* hashT, char* key) {
unsigned int index = hashT->hashF(key) % hashT->size;
List* l = hashT->table[index];
return _searchKeyInList(l, key);
}
char* HashTableGet(const HashTable* hashT, char* key) {
unsigned int index = hashT->hashF(key) % hashT->size;
List* l = hashT->table[index];
// Search and update current, if found
if (_searchKeyInList(l, key) == 0) {
return NULL;
}
struct _HashTableBin* bin = ListGetCurrentItem(l);
char* result = (char*)malloc(sizeof(char) * (1 + strlen(bin->value)));
strcpy(result, bin->value);
return result;
}
int HashTablePut(HashTable* hashT, char* key, char* value) {
unsigned int index = hashT->hashF(key) % hashT->size;
List* l = hashT->table[index];
if (_searchKeyInList(l, key) == 1) {
// FOUND, cannot be added to the table
return 0;
}
// Does NOT BELONG to the table
// Insert a new bin in the list
struct _HashTableBin* bin = (struct _HashTableBin*)malloc(sizeof(*bin));
bin->key = (char*)malloc(sizeof(char) * (1 + strlen(key)));
strcpy(bin->key, key);
bin->value = (char*)malloc(sizeof(char) * (1 + strlen(value)));
strcpy(bin->value, value);
ListInsert(l, bin);
hashT->numBins++;
return 1;
}
int HashTableReplace(const HashTable* hashT, char* key, char* value) {
unsigned int index = hashT->hashF(key) % hashT->size;
List* l = hashT->table[index];
// Search and update current, if found
if (_searchKeyInList(l, key) == 0) {
return 0;
}
struct _HashTableBin* bin = ListGetCurrentItem(l);
free(bin->value);
bin->value = (char*)malloc(sizeof(char) * (1 + strlen(value)));
strcpy(bin->value, value);
return 1;
}
int HashTableRemove(HashTable* hashT, char* key) {
unsigned int index = hashT->hashF(key) % hashT->size;
List* l = hashT->table[index];
// Search and update current, if found
if (_searchKeyInList(l, key) == 0) {
return 0;
}
// Get rid of the bin
struct _HashTableBin* bin = ListGetCurrentItem(l);
free(bin->key);
free(bin->value);
free(bin);
// Get rid of the list node
ListRemoveCurrent(l);
hashT->numBins--;
return 1;
}
// DISPLAYING on the console
void HashTableDisplay(const HashTable* hashT) {
printf("---\n");
printf("Size = %2d | Active = %2d\n", hashT->size, hashT->numBins);
for (unsigned int i = 0; i < hashT->size; i++) {
printf("%3d - ", i);
if (ListIsEmpty(hashT->table[i])) {
printf("\n");
} else {
printf("\n");
List* l = hashT->table[i];
ListMoveToHead(l);
for (int i = 0; i < ListGetSize(l); i++) {
struct _HashTableBin* bin = ListGetCurrentItem(l);
unsigned int hashValue = hashT->hashF(bin->key);
printf(" Hash = %4d, (%s, %s)\n", hashValue, bin->key, bin->value);
ListMoveToNext(l);
}
}
}
printf("---\n");
}

View File

@ -0,0 +1,54 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira, May 2020, November 2023
//
// Simple Hash Table using Separate Chaining
// Key and Value are STRINGS - To simplify the example
//
#ifndef _HASH_TABLE_
#define _HASH_TABLE_
typedef struct _HashTableHeader HashTable;
typedef unsigned int (*hashFunction)(const char* key);
// Auxiliary functions for experimenting
unsigned int hash1(const char* key);
unsigned int hash2(const char* key);
// TAD Functions
HashTable* HashTableCreate(unsigned int capacity, hashFunction hashF);
void HashTableDestroy(HashTable** p);
// HashTable properties
int HashTableIsEmpty(const HashTable* hashT);
// Getters
unsigned int HashTableGetNumberOfItems(const HashTable* hashT);
double HashTableGetLoadFactor(const HashTable* hashT);
// Operations with items
int HashTableContains(const HashTable* hashT, char* key);
char* HashTableGet(const HashTable* hashT, char* key);
int HashTablePut(HashTable* hashT, char* key, char* value);
int HashTableReplace(const HashTable* hashT, char* key, char* value);
int HashTableRemove(HashTable* hashT, char* key);
// DISPLAYING on the console
void HashTableDisplay(const HashTable* hashT);
#endif // _HASH_TABLE_

View File

@ -0,0 +1,340 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira, April 2020, November 2023
//
// Adapted from Tomás Oliveira e Silva, AED, September 2015
//
// SORTED LIST implementation based on an linked list
//
// ***************** COMPLETAR AS FUNCOES !!! *******************
#include "SortedList.h"
#include <assert.h>
#include <stdlib.h>
struct _ListNode {
void* item;
struct _ListNode* next;
};
struct _SortedList {
int size; // current List size
struct _ListNode* head; // the head of the List
struct _ListNode* tail; // the tail of the List
struct _ListNode* current; // the current node
int currentPos; // the current node index
compFunc compare;
};
List* ListCreate(compFunc compF) {
List* l = (List*)malloc(sizeof(List));
assert(l != NULL);
l->size = 0;
l->head = NULL;
l->tail = NULL;
l->current = NULL;
l->currentPos = -1; // Default: before the head of the list
l->compare = compF;
return l;
}
void ListDestroy(List** p) {
assert(*p != NULL);
List* l = *p;
ListClear(l);
free(l);
*p = NULL;
}
void ListClear(List* l) {
assert(l != NULL);
struct _ListNode* p = l->head;
struct _ListNode* aux;
while (p != NULL) {
aux = p;
p = aux->next;
free(aux);
}
l->size = 0;
l->head = NULL;
l->tail = NULL;
l->current = NULL;
l->currentPos = -1; // Default: before the head of the list
}
int ListGetSize(const List* l) {
assert(l != NULL);
return l->size;
}
int ListIsEmpty(const List* l) {
assert(l != NULL);
return (l->size == 0) ? 1 : 0;
}
// Current node functions
int ListGetCurrentIndex(const List* l) {
assert(l != NULL);
return l->currentPos;
}
void* ListGetCurrentItem(const List* l) {
assert(l != NULL && l->current != NULL);
return l->current->item;
}
void ListModifyCurrentValue(const List* l, void* p) {
assert(l != NULL && l->current != NULL);
l->current->item = p;
}
// The move and search functions return 0 on success and -1 on failure (on
// success the current node is changed, on failure it is not changed)
// Search
//
// starting at the current node, search for the first node with a value of *p
// on failure the current node is not changed
//
int ListSearch(List* l, const void* p) {
int i = (l->currentPos < 0) ? 0 : l->currentPos;
struct _ListNode* sn = (l->currentPos < 0) ? l->head : l->current;
while (i < l->size && l->compare(p, sn->item) > 0) {
i++;
sn = sn->next;
}
if (i == l->size) {
return -1;
} // failure
if (l->compare(p, sn->item) < 0) {
return -1;
} // failure
l->current = sn;
l->currentPos = i;
return 0; // success
}
// Move to functions
int ListMove(List* l, int newPos) {
if (newPos < -1 || newPos > l->size) {
return -1;
} // failure
if (newPos == -1 || newPos == l->size) {
l->current = NULL;
} else if (newPos == 0) {
l->current = l->head;
} else if (newPos == l->size - 1) {
l->current = l->tail;
} else {
if (l->currentPos == -1 || l->currentPos == l->size ||
newPos < l->currentPos) {
l->current = l->head;
l->currentPos = 0;
}
for (int i = l->currentPos; i < newPos; i++) {
l->current = l->current->next;
}
}
l->currentPos = newPos;
return 0; // success
}
int ListMoveToNext(List* l) { return ListMove(l, l->currentPos + 1); }
int ListMoveToPrevious(List* l) { return ListMove(l, l->currentPos - 1); }
int ListMoveToHead(List* l) { return ListMove(l, 0); }
int ListMoveToTail(List* l) { return ListMove(l, l->size - 1); }
// Insert function
//
// insert a node
// the current node is not changed
// return 0 on success
// return -1 on failure
//
int ListInsert(List* l, void* p) {
struct _ListNode* sn = (struct _ListNode*)malloc(sizeof(struct _ListNode));
assert(sn != NULL);
sn->item = p;
sn->next = NULL;
// Empty list
if (l->size == 0) {
l->head = l->tail = sn;
l->size = 1;
return 0;
}
// Search
int i = 0;
struct _ListNode* prev = NULL;
struct _ListNode* aux = l->head;
while (i < l->size && l->compare(p, aux->item) > 0) {
i++;
prev = aux;
aux = aux->next;
}
if (i == l->size) { // Append at the tail
l->tail->next = sn;
l->tail = sn;
l->size++;
return 0;
}
if (l->compare(p, aux->item) == 0) { // Already exists !!
free(sn);
return -1;
} // failure
if (i == 0) { // Append at the head
sn->next = l->head;
l->head = sn;
l->size++;
if (l->currentPos >= 0) {
l->currentPos++;
}
return 0;
}
sn->next = aux;
prev->next = sn;
l->size++;
if (l->currentPos >= i) {
l->currentPos++;
}
return 0;
}
// Remove functions
//
// remove the head of the list and make its next node the new head
// if the current node is the head, it is replaced by its next node
//
void* ListRemoveHead(List* l) {
assert(l->size > 0);
if (l->current == l->head) {
l->current = l->head->next;
l->currentPos++;
}
if (l->size == 1) {
void* p = l->head->item;
free(l->head);
l->head = NULL;
l->tail = NULL;
l->size = 0;
if (l->currentPos > 0) l->currentPos = 0;
return p;
} else {
struct _ListNode* sn = l->head->next;
void* p = l->head->item;
free(l->head);
l->head = sn;
if (l->currentPos > 0) l->currentPos--;
l->size--;
return p;
}
}
//
// remove the tail of the list and make its previous node the new tail
// if the current node is the tail, it is replaced by it by its next node
//
void* ListRemoveTail(List* l) {
assert(l->size > 0);
if (l->current == l->tail) {
l->current = NULL;
l->currentPos++;
}
if (l->size == 1) {
void* p = l->head->item;
free(l->head);
l->head = NULL;
l->tail = NULL;
l->current = NULL;
if (l->currentPos > 0) l->currentPos = 0;
l->size = 0;
return p;
} else {
struct _ListNode* sn = l->head;
while (sn->next != l->tail) sn = sn->next;
sn->next = NULL;
void* p = l->tail->item;
free(l->tail);
l->tail = sn;
if (l->currentPos == l->size) l->currentPos--;
l->size--;
return p;
}
}
//
// remove the current node and make its next node the current node
//
void* ListRemoveCurrent(List* l) {
assert(l->currentPos >= 0 && l->currentPos < l->size);
if (l->currentPos == 0)
return ListRemoveHead(l);
else if (l->currentPos == l->size - 1)
return ListRemoveTail(l);
else {
struct _ListNode* sn = l->head;
while (sn->next != l->current) sn = sn->next;
sn->next = l->current->next;
void* p = l->current->item;
free(l->current);
l->current = sn->next;
l->size--;
return p;
}
}
// Tests
void ListTestInvariants(const List* l) {
assert(l->size >= 0);
if (l->size == 0)
assert(l->head == NULL && l->tail == NULL);
else
assert(l->head != NULL && l->tail != NULL);
if (l->size == 1) assert(l->head == l->tail);
assert(-1 <= l->currentPos && l->currentPos <= l->size);
if (l->currentPos == -1 || l->currentPos == l->size)
assert(l->current == NULL);
struct _ListNode* sn = l->head;
for (int i = 0; i < l->size; i++) {
if (i == l->size - 1)
assert(sn == l->tail && sn->next == NULL);
else
assert(sn->next != NULL);
if (i == l->currentPos) assert(sn == l->current);
sn = sn->next;
}
}

View File

@ -0,0 +1,70 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira, April 2020, November 2023
//
// Adapted from Tomás Oliveira e Silva, AED, September 2015
//
// SORTED LIST implementation based on a linked list
//
#ifndef _SORTED_LIST_
#define _SORTED_LIST_
typedef struct _SortedList List;
typedef int (*compFunc)(const void* p1, const void* p2);
List* ListCreate(compFunc compF);
void ListDestroy(List** p);
void ListClear(List* l);
int ListGetSize(const List* l);
int ListIsEmpty(const List* l);
// Current node functions
int ListGetCurrentIndex(const List* l);
void* ListGetCurrentItem(const List* l);
void ListModifyCurrentItem(const List* l, void* p);
// The move and search functions return 0 on success and -1 on failure (on
// success the current node is changed, on failure it is not changed)
// Search
int ListSearch(List* l, const void* p);
// Move to
int ListMove(List* l, int newPos);
int ListMoveToNext(List* l);
int ListMoveToPrevious(List* l);
int ListMoveToHead(List* l);
int ListMoveToTail(List* l);
// Insert
int ListInsert(List* l, void* p);
// Remove
void* ListRemoveHead(List* l);
void* ListRemoveTail(List* l);
void* ListRemoveCurrent(List* l);
// Tests
void ListTestInvariants(const List* l);
#endif // _SORTED_LIST_

View File

@ -0,0 +1,6 @@
Implement RESIZING + REHASHING
When the Load Factor N/M reaches 8, double the table size.
When the Load Factor N/M reaches 2, halve the table siize.

View File

@ -0,0 +1 @@
GRAFOS_EM_FICHEIRO

View File

@ -0,0 +1,458 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira, Joao Manuel Rodrigues - June 2021, Nov 2023
//
// Graph - Using a list of adjacency lists representation
//
#include "Graph.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "SortedList.h"
struct _Vertex {
unsigned int id;
unsigned int inDegree;
unsigned int outDegree;
List* edgesList;
};
struct _Edge {
unsigned int adjVertex;
double weight;
};
struct _GraphHeader {
int isDigraph;
int isComplete;
int isWeighted;
unsigned int numVertices;
unsigned int numEdges;
List* verticesList;
};
// The comparator for the VERTICES LIST
int graphVerticesComparator(const void* p1, const void* p2) {
unsigned int v1 = ((struct _Vertex*)p1)->id;
unsigned int v2 = ((struct _Vertex*)p2)->id;
int d = v1 - v2;
return (d > 0) - (d < 0);
}
// The comparator for the EDGES LISTS
int graphEdgesComparator(const void* p1, const void* p2) {
unsigned int v1 = ((struct _Edge*)p1)->adjVertex;
unsigned int v2 = ((struct _Edge*)p2)->adjVertex;
int d = v1 - v2;
return (d > 0) - (d < 0);
}
Graph* GraphCreate(unsigned int numVertices, int isDigraph, int isWeighted) {
Graph* g = (Graph*)malloc(sizeof(struct _GraphHeader));
if (g == NULL) abort();
g->isDigraph = isDigraph;
g->isComplete = 0;
g->isWeighted = isWeighted;
g->numVertices = numVertices;
g->numEdges = 0;
g->verticesList = ListCreate(graphVerticesComparator);
for (unsigned int i = 0; i < numVertices; i++) {
struct _Vertex* v = (struct _Vertex*)malloc(sizeof(struct _Vertex));
if (v == NULL) abort();
v->id = i;
v->inDegree = 0;
v->outDegree = 0;
v->edgesList = ListCreate(graphEdgesComparator);
ListInsert(g->verticesList, v);
}
assert(g->numVertices == ListGetSize(g->verticesList));
return g;
}
Graph* GraphCreateComplete(unsigned int numVertices, int isDigraph) {
Graph* g = GraphCreate(numVertices, isDigraph, 0);
g->isComplete = 1;
List* vertices = g->verticesList;
ListMoveToHead(vertices);
unsigned int i = 0;
for (; i < g->numVertices; ListMoveToNext(vertices), i++) {
struct _Vertex* v = ListGetCurrentItem(vertices);
List* edges = v->edgesList;
for (unsigned int j = 0; j < g->numVertices; j++) {
if (i == j) {
continue;
}
struct _Edge* new = (struct _Edge*)malloc(sizeof(struct _Edge));
if (new == NULL) abort();
new->adjVertex = j;
new->weight = 1;
ListInsert(edges, new);
}
if (g->isDigraph) {
v->inDegree = g->numVertices - 1;
v->outDegree = g->numVertices - 1;
} else {
v->outDegree = g->numVertices - 1;
}
}
if (g->isDigraph) {
g->numEdges = numVertices * (numVertices - 1);
} else {
g->numEdges = numVertices * (numVertices - 1) / 2;
}
return g;
}
void GraphDestroy(Graph** p) {
assert(*p != NULL);
Graph* g = *p;
List* vertices = g->verticesList;
if (ListIsEmpty(vertices) == 0) {
ListMoveToHead(vertices);
unsigned int i = 0;
for (; i < g->numVertices; ListMoveToNext(vertices), i++) {
struct _Vertex* v = ListGetCurrentItem(vertices);
List* edges = v->edgesList;
if (ListIsEmpty(edges) == 0) {
unsigned int i = 0;
ListMoveToHead(edges);
for (; i < ListGetSize(edges); ListMoveToNext(edges), i++) {
struct _Edge* e = ListGetCurrentItem(edges);
free(e);
}
}
ListDestroy(&(v->edgesList));
free(v);
}
}
ListDestroy(&(g->verticesList));
free(g);
*p = NULL;
}
Graph* GraphCopy(const Graph* g) {
// COMPLETAR !!
return NULL;
}
Graph* GraphFromFile(FILE* f) {
// COMPLETAR !!
return NULL;
}
// Graph
int GraphIsDigraph(const Graph* g) { return g->isDigraph; }
int GraphIsComplete(const Graph* g) { return g->isComplete; }
int GraphIsWeighted(const Graph* g) { return g->isWeighted; }
unsigned int GraphGetNumVertices(const Graph* g) { return g->numVertices; }
unsigned int GraphGetNumEdges(const Graph* g) { return g->numEdges; }
//
// For a graph
//
double GraphGetAverageDegree(const Graph* g) {
assert(g->isDigraph == 0);
return 2.0 * (double)g->numEdges / (double)g->numVertices;
}
static unsigned int _GetMaxDegree(const Graph* g) {
List* vertices = g->verticesList;
if (ListIsEmpty(vertices)) return 0;
unsigned int maxDegree = 0;
ListMoveToHead(vertices);
unsigned int i = 0;
for (; i < g->numVertices; ListMoveToNext(vertices), i++) {
struct _Vertex* v = ListGetCurrentItem(vertices);
if (v->outDegree > maxDegree) {
maxDegree = v->outDegree;
}
}
return maxDegree;
}
//
// For a graph
//
unsigned int GraphGetMaxDegree(const Graph* g) {
assert(g->isDigraph == 0);
return _GetMaxDegree(g);
}
//
// For a digraph
//
unsigned int GraphGetMaxOutDegree(const Graph* g) {
assert(g->isDigraph == 1);
return _GetMaxDegree(g);
}
// Vertices
//
// returns an array of size (outDegree + 1)
// element 0, stores the number of adjacent vertices
// and is followed by indices of the adjacent vertices
//
unsigned int* GraphGetAdjacentsTo(const Graph* g, unsigned int v) {
assert(v < g->numVertices);
// Node in the list of vertices
List* vertices = g->verticesList;
ListMove(vertices, v);
struct _Vertex* vPointer = ListGetCurrentItem(vertices);
unsigned int numAdjVertices = vPointer->outDegree;
unsigned int* adjacent =
(unsigned int*)calloc(1 + numAdjVertices, sizeof(unsigned int));
if (numAdjVertices > 0) {
adjacent[0] = numAdjVertices;
List* adjList = vPointer->edgesList;
ListMoveToHead(adjList);
for (unsigned int i = 0; i < numAdjVertices; ListMoveToNext(adjList), i++) {
struct _Edge* ePointer = ListGetCurrentItem(adjList);
adjacent[i + 1] = ePointer->adjVertex;
}
}
return adjacent;
}
//
// returns an array of size (outDegree + 1)
// element 0, stores the number of adjacent vertices
// and is followed by the distances to the adjacent vertices
//
double* GraphGetDistancesToAdjacents(const Graph* g, unsigned int v) {
assert(v < g->numVertices);
// Node in the list of vertices
List* vertices = g->verticesList;
ListMove(vertices, v);
struct _Vertex* vPointer = ListGetCurrentItem(vertices);
unsigned int numAdjVertices = vPointer->outDegree;
double* distance = (double*)calloc(1 + numAdjVertices, sizeof(double));
if (numAdjVertices > 0) {
distance[0] = numAdjVertices;
List* adjList = vPointer->edgesList;
ListMoveToHead(adjList);
for (unsigned int i = 0; i < numAdjVertices; ListMoveToNext(adjList), i++) {
struct _Edge* ePointer = ListGetCurrentItem(adjList);
distance[i + 1] = ePointer->weight;
}
}
return distance;
}
//
// For a graph
//
unsigned int GraphGetVertexDegree(Graph* g, unsigned int v) {
assert(g->isDigraph == 0);
assert(v < g->numVertices);
ListMove(g->verticesList, v);
struct _Vertex* p = ListGetCurrentItem(g->verticesList);
return p->outDegree;
}
//
// For a digraph
//
unsigned int GraphGetVertexOutDegree(Graph* g, unsigned int v) {
assert(g->isDigraph == 1);
assert(v < g->numVertices);
ListMove(g->verticesList, v);
struct _Vertex* p = ListGetCurrentItem(g->verticesList);
return p->outDegree;
}
//
// For a digraph
//
unsigned int GraphGetVertexInDegree(Graph* g, unsigned int v) {
assert(g->isDigraph == 1);
assert(v < g->numVertices);
ListMove(g->verticesList, v);
struct _Vertex* p = ListGetCurrentItem(g->verticesList);
return p->inDegree;
}
// Edges
static int _addEdge(Graph* g, unsigned int v, unsigned int w, double weight) {
struct _Edge* edge = (struct _Edge*)malloc(sizeof(struct _Edge));
edge->adjVertex = w;
edge->weight = weight;
ListMove(g->verticesList, v);
struct _Vertex* vertex = ListGetCurrentItem(g->verticesList);
int result = ListInsert(vertex->edgesList, edge);
if (result == -1) {
return 0;
} else {
g->numEdges++;
vertex->outDegree++;
ListMove(g->verticesList, w);
struct _Vertex* destVertex = ListGetCurrentItem(g->verticesList);
destVertex->inDegree++;
}
if (g->isDigraph == 0) {
// Bidirectional edge
struct _Edge* edge = (struct _Edge*)malloc(sizeof(struct _Edge));
edge->adjVertex = v;
edge->weight = weight;
ListMove(g->verticesList, w);
struct _Vertex* vertex = ListGetCurrentItem(g->verticesList);
result = ListInsert(vertex->edgesList, edge);
if (result == -1) {
return 0;
} else {
// g->numEdges++; // Do not count the same edge twice on a undirected
// graph !!
vertex->outDegree++;
}
}
return 1;
}
int GraphAddEdge(Graph* g, unsigned int v, unsigned int w) {
assert(g->isWeighted == 0);
assert(v != w);
assert(v < g->numVertices);
assert(w < g->numVertices);
return _addEdge(g, v, w, 1.0);
}
int GraphAddWeightedEdge(Graph* g, unsigned int v, unsigned int w,
double weight) {
assert(g->isWeighted == 1);
assert(v != w);
assert(v < g->numVertices);
assert(w < g->numVertices);
return _addEdge(g, v, w, weight);
}
int GraphRemoveEdge(Graph* g, unsigned int v, unsigned int w) {
// COMPLETAR !!
return 0;
}
// CHECKING
int GraphCheckInvariants(const Graph* g) {
// COMPLETAR !!
return 0;
}
// DISPLAYING on the console
void GraphDisplay(const Graph* g) {
printf("---\n");
if (g->isWeighted) {
printf("Weighted ");
}
if (g->isComplete) {
printf("COMPLETE ");
}
if (g->isDigraph) {
printf("Digraph\n");
printf("Max Out-Degree = %d\n", GraphGetMaxOutDegree(g));
} else {
printf("Graph\n");
printf("Max Degree = %d\n", GraphGetMaxDegree(g));
}
printf("Vertices = %2d | Edges = %2d\n", g->numVertices, g->numEdges);
List* vertices = g->verticesList;
ListMoveToHead(vertices);
unsigned int i = 0;
for (; i < g->numVertices; ListMoveToNext(vertices), i++) {
printf("%2d ->", i);
struct _Vertex* v = ListGetCurrentItem(vertices);
if (ListIsEmpty(v->edgesList)) {
printf("\n");
} else {
List* edges = v->edgesList;
unsigned int i = 0;
ListMoveToHead(edges);
for (; i < ListGetSize(edges); ListMoveToNext(edges), i++) {
struct _Edge* e = ListGetCurrentItem(edges);
if (g->isWeighted) {
printf(" %2d(%4.2f)", e->adjVertex, e->weight);
} else {
printf(" %2d", e->adjVertex);
}
}
printf("\n");
}
}
printf("---\n");
}
void GraphListAdjacents(const Graph* g, unsigned int v) {
printf("---\n");
unsigned int* array = GraphGetAdjacentsTo(g, v);
printf("Vertex %d has %d adjacent vertices -> ", v, array[0]);
for (unsigned int i = 1; i <= array[0]; i++) {
printf("%d ", array[i]);
}
printf("\n");
free(array);
printf("---\n");
}

View File

@ -0,0 +1,94 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira, Joao Manuel Rodrigues - June 2021, Nov 2023
//
// Graph - Using a list of adjacency lists representation
//
#ifndef _GRAPH_
#define _GRAPH_
#include <stdio.h>
typedef struct _GraphHeader Graph;
Graph* GraphCreate(unsigned int numVertices, int isDigraph, int isWeighted);
Graph* GraphCreateComplete(unsigned int numVertices, int isDigraph);
void GraphDestroy(Graph** p);
Graph* GraphCopy(const Graph* g);
Graph* GraphFromFile(FILE* f);
// Graph
int GraphIsDigraph(const Graph* g);
int GraphIsComplete(const Graph* g);
int GraphIsWeighted(const Graph* g);
unsigned int GraphGetNumVertices(const Graph* g);
unsigned int GraphGetNumEdges(const Graph* g);
//
// For a graph
//
double GraphGetAverageDegree(const Graph* g);
//
// For a graph
//
unsigned int GraphGetMaxDegree(const Graph* g);
//
// For a digraph
//
unsigned int GraphGetMaxOutDegree(const Graph* g);
// Vertices
unsigned int* GraphGetAdjacentsTo(const Graph* g, unsigned int v);
// Vertices distances
double* GraphGetDistancesToAdjacents(const Graph* g, unsigned int v);
//
// For a graph
//
unsigned int GraphGetVertexDegree(Graph* g, unsigned int v);
//
// For a digraph
//
unsigned int GraphGetVertexOutDegree(Graph* g, unsigned int v);
//
// For a digraph
//
unsigned int GraphGetVertexInDegree(Graph* g, unsigned int v);
// Edges
int GraphAddEdge(Graph* g, unsigned int v, unsigned int w);
int GraphAddWeightedEdge(Graph* g, unsigned int v, unsigned int w,
double weight);
int GraphRemoveEdge(Graph* g, unsigned int v, unsigned int w);
// CHECKING
int GraphCheckInvariants(const Graph* g);
// DISPLAYING on the console
void GraphDisplay(const Graph* g);
void GraphListAdjacents(const Graph* g, unsigned int v);
#endif // _GRAPH_

View File

@ -0,0 +1,26 @@
# To compile all programs, run:
# make
#
# AED, ua, 2023
CFLAGS += -g -Wall -Wextra
TARGETS = example1
all: $(TARGETS)
example1: example1.o Graph.o SortedList.o
# Dependencies of source files (obtained with gcc -MM)
example1.o: example1.c Graph.h
Graph.o: Graph.c Graph.h SortedList.h
SortedList.o: SortedList.c SortedList.h
clean:
rm -f *.o
rm -f $(TARGETS)

View File

@ -0,0 +1,340 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira, April 2020, November 2023
//
// Adapted from Tomás Oliveira e Silva, AED, September 2015
//
// SORTED LIST implementation based on an linked list
//
// ***************** COMPLETAR AS FUNCOES !!! *******************
#include "SortedList.h"
#include <assert.h>
#include <stdlib.h>
struct _ListNode {
void* item;
struct _ListNode* next;
};
struct _SortedList {
int size; // current List size
struct _ListNode* head; // the head of the List
struct _ListNode* tail; // the tail of the List
struct _ListNode* current; // the current node
int currentPos; // the current node index
compFunc compare;
};
List* ListCreate(compFunc compF) {
List* l = (List*)malloc(sizeof(List));
assert(l != NULL);
l->size = 0;
l->head = NULL;
l->tail = NULL;
l->current = NULL;
l->currentPos = -1; // Default: before the head of the list
l->compare = compF;
return l;
}
void ListDestroy(List** p) {
assert(*p != NULL);
List* l = *p;
ListClear(l);
free(l);
*p = NULL;
}
void ListClear(List* l) {
assert(l != NULL);
struct _ListNode* p = l->head;
struct _ListNode* aux;
while (p != NULL) {
aux = p;
p = aux->next;
free(aux);
}
l->size = 0;
l->head = NULL;
l->tail = NULL;
l->current = NULL;
l->currentPos = -1; // Default: before the head of the list
}
unsigned int ListGetSize(const List* l) {
assert(l != NULL);
return l->size;
}
int ListIsEmpty(const List* l) {
assert(l != NULL);
return (l->size == 0) ? 1 : 0;
}
// Current node functions
int ListGetCurrentIndex(const List* l) {
assert(l != NULL);
return l->currentPos;
}
void* ListGetCurrentItem(const List* l) {
assert(l != NULL && l->current != NULL);
return l->current->item;
}
void ListModifyCurrentValue(const List* l, void* p) {
assert(l != NULL && l->current != NULL);
l->current->item = p;
}
// The move and search functions return 0 on success and -1 on failure (on
// success the current node is changed, on failure it is not changed)
// Search
//
// starting at the current node, search for the first node with a value of *p
// on failure the current node is not changed
//
int ListSearch(List* l, const void* p) {
int i = (l->currentPos < 0) ? 0 : l->currentPos;
struct _ListNode* sn = (l->currentPos < 0) ? l->head : l->current;
while (i < l->size && l->compare(p, sn->item) > 0) {
i++;
sn = sn->next;
}
if (i == l->size) {
return -1;
} // failure
if (l->compare(p, sn->item) < 0) {
return -1;
} // failure
l->current = sn;
l->currentPos = i;
return 0; // success
}
// Move to functions
int ListMove(List* l, int newPos) {
if (newPos < -1 || newPos > l->size) {
return -1;
} // failure
if (newPos == -1 || newPos == l->size) {
l->current = NULL;
} else if (newPos == 0) {
l->current = l->head;
} else if (newPos == l->size - 1) {
l->current = l->tail;
} else {
if (l->currentPos == -1 || l->currentPos == l->size ||
newPos < l->currentPos) {
l->current = l->head;
l->currentPos = 0;
}
for (int i = l->currentPos; i < newPos; i++) {
l->current = l->current->next;
}
}
l->currentPos = newPos;
return 0; // success
}
int ListMoveToNext(List* l) { return ListMove(l, l->currentPos + 1); }
int ListMoveToPrevious(List* l) { return ListMove(l, l->currentPos - 1); }
int ListMoveToHead(List* l) { return ListMove(l, 0); }
int ListMoveToTail(List* l) { return ListMove(l, l->size - 1); }
// Insert function
//
// insert a node
// the current node is not changed
// return 0 on success
// return -1 on failure
//
int ListInsert(List* l, void* p) {
struct _ListNode* sn = (struct _ListNode*)malloc(sizeof(struct _ListNode));
assert(sn != NULL);
sn->item = p;
sn->next = NULL;
// Empty list
if (l->size == 0) {
l->head = l->tail = sn;
l->size = 1;
return 0;
}
// Search
int i = 0;
struct _ListNode* prev = NULL;
struct _ListNode* aux = l->head;
while (i < l->size && l->compare(p, aux->item) > 0) {
i++;
prev = aux;
aux = aux->next;
}
if (i == l->size) { // Append at the tail
l->tail->next = sn;
l->tail = sn;
l->size++;
return 0;
}
if (l->compare(p, aux->item) == 0) { // Already exists !!
free(sn);
return -1;
} // failure
if (i == 0) { // Append at the head
sn->next = l->head;
l->head = sn;
l->size++;
if (l->currentPos >= 0) {
l->currentPos++;
}
return 0;
}
sn->next = aux;
prev->next = sn;
l->size++;
if (l->currentPos >= i) {
l->currentPos++;
}
return 0;
}
// Remove functions
//
// remove the head of the list and make its next node the new head
// if the current node is the head, it is replaced by its next node
//
void* ListRemoveHead(List* l) {
assert(l->size > 0);
if (l->current == l->head) {
l->current = l->head->next;
l->currentPos++;
}
if (l->size == 1) {
void* p = l->head->item;
free(l->head);
l->head = NULL;
l->tail = NULL;
l->size = 0;
if (l->currentPos > 0) l->currentPos = 0;
return p;
} else {
struct _ListNode* sn = l->head->next;
void* p = l->head->item;
free(l->head);
l->head = sn;
if (l->currentPos > 0) l->currentPos--;
l->size--;
return p;
}
}
//
// remove the tail of the list and make its previous node the new tail
// if the current node is the tail, it is replaced by it by its next node
//
void* ListRemoveTail(List* l) {
assert(l->size > 0);
if (l->current == l->tail) {
l->current = NULL;
l->currentPos++;
}
if (l->size == 1) {
void* p = l->head->item;
free(l->head);
l->head = NULL;
l->tail = NULL;
l->current = NULL;
if (l->currentPos > 0) l->currentPos = 0;
l->size = 0;
return p;
} else {
struct _ListNode* sn = l->head;
while (sn->next != l->tail) sn = sn->next;
sn->next = NULL;
void* p = l->tail->item;
free(l->tail);
l->tail = sn;
if (l->currentPos == l->size) l->currentPos--;
l->size--;
return p;
}
}
//
// remove the current node and make its next node the current node
//
void* ListRemoveCurrent(List* l) {
assert(l->currentPos >= 0 && l->currentPos < l->size);
if (l->currentPos == 0)
return ListRemoveHead(l);
else if (l->currentPos == l->size - 1)
return ListRemoveTail(l);
else {
struct _ListNode* sn = l->head;
while (sn->next != l->current) sn = sn->next;
sn->next = l->current->next;
void* p = l->current->item;
free(l->current);
l->current = sn->next;
l->size--;
return p;
}
}
// Tests
void ListTestInvariants(const List* l) {
assert(l->size >= 0);
if (l->size == 0)
assert(l->head == NULL && l->tail == NULL);
else
assert(l->head != NULL && l->tail != NULL);
if (l->size == 1) assert(l->head == l->tail);
assert(-1 <= l->currentPos && l->currentPos <= l->size);
if (l->currentPos == -1 || l->currentPos == l->size)
assert(l->current == NULL);
struct _ListNode* sn = l->head;
for (int i = 0; i < l->size; i++) {
if (i == l->size - 1)
assert(sn == l->tail && sn->next == NULL);
else
assert(sn->next != NULL);
if (i == l->currentPos) assert(sn == l->current);
sn = sn->next;
}
}

View File

@ -0,0 +1,70 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira, April 2020, November 2023
//
// Adapted from Tomás Oliveira e Silva, AED, September 2015
//
// SORTED LIST implementation based on a linked list
//
#ifndef _SORTED_LIST_
#define _SORTED_LIST_
typedef struct _SortedList List;
typedef int (*compFunc)(const void* p1, const void* p2);
List* ListCreate(compFunc compF);
void ListDestroy(List** p);
void ListClear(List* l);
unsigned int ListGetSize(const List* l);
int ListIsEmpty(const List* l);
// Current node functions
int ListGetCurrentIndex(const List* l);
void* ListGetCurrentItem(const List* l);
void ListModifyCurrentItem(const List* l, void* p);
// The move and search functions return 0 on success and -1 on failure (on
// success the current node is changed, on failure it is not changed)
// Search
int ListSearch(List* l, const void* p);
// Move to
int ListMove(List* l, int newPos);
int ListMoveToNext(List* l);
int ListMoveToPrevious(List* l);
int ListMoveToHead(List* l);
int ListMoveToTail(List* l);
// Insert
int ListInsert(List* l, void* p);
// Remove
void* ListRemoveHead(List* l);
void* ListRemoveTail(List* l);
void* ListRemoveCurrent(List* l);
// Tests
void ListTestInvariants(const List* l);
#endif // _SORTED_LIST_

View File

@ -0,0 +1,50 @@
//
// Joaquim Madeira, AlgC, June 2021
// João Manuel Rodrigues, AlgC, June 2021
//
// Graph EXAMPLE
//
#include "Graph.h"
int main(void) {
// What kind of graph is g01?
Graph* g01 = GraphCreate(6, 0, 0);
GraphAddEdge(g01, 1, 2);
GraphAddEdge(g01, 1, 4);
GraphAddEdge(g01, 3, 4);
printf("The first graph:\n");
GraphDisplay(g01);
for (int i = 0; i < 6; i++) {
GraphListAdjacents(g01, i);
}
printf("Remove edge (1,2)\n");
GraphDisplay(g01);
for (int i = 0; i < 6; i++) {
GraphListAdjacents(g01, i);
}
Graph* dig01 = GraphCreate(6, 1, 0);
GraphAddEdge(dig01, 1, 2);
GraphAddEdge(dig01, 1, 4);
GraphAddEdge(dig01, 3, 4);
printf("The second graph:\n");
GraphDisplay(dig01);
printf("Remove edge (1,2)\n");
GraphDisplay(dig01);
Graph* g03 = GraphCreate(6, 0, 1);
GraphAddWeightedEdge(g03, 1, 2, 3);
GraphAddWeightedEdge(g03, 1, 4, 5);
GraphAddWeightedEdge(g03, 3, 4, 10);
printf("The third graph:\n");
GraphDisplay(g03);
printf("Remove edge (1,2)\n");
GraphDisplay(g03);
GraphDestroy(&g01);
GraphDestroy(&dig01);
GraphDestroy(&g03);
return 0;
}

View File

@ -0,0 +1,458 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira, Joao Manuel Rodrigues - June 2021, Nov 2023
//
// Graph - Using a list of adjacency lists representation
//
#include "Graph.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "SortedList.h"
struct _Vertex {
unsigned int id;
unsigned int inDegree;
unsigned int outDegree;
List* edgesList;
};
struct _Edge {
unsigned int adjVertex;
double weight;
};
struct _GraphHeader {
int isDigraph;
int isComplete;
int isWeighted;
unsigned int numVertices;
unsigned int numEdges;
List* verticesList;
};
// The comparator for the VERTICES LIST
int graphVerticesComparator(const void* p1, const void* p2) {
unsigned int v1 = ((struct _Vertex*)p1)->id;
unsigned int v2 = ((struct _Vertex*)p2)->id;
int d = v1 - v2;
return (d > 0) - (d < 0);
}
// The comparator for the EDGES LISTS
int graphEdgesComparator(const void* p1, const void* p2) {
unsigned int v1 = ((struct _Edge*)p1)->adjVertex;
unsigned int v2 = ((struct _Edge*)p2)->adjVertex;
int d = v1 - v2;
return (d > 0) - (d < 0);
}
Graph* GraphCreate(unsigned int numVertices, int isDigraph, int isWeighted) {
Graph* g = (Graph*)malloc(sizeof(struct _GraphHeader));
if (g == NULL) abort();
g->isDigraph = isDigraph;
g->isComplete = 0;
g->isWeighted = isWeighted;
g->numVertices = numVertices;
g->numEdges = 0;
g->verticesList = ListCreate(graphVerticesComparator);
for (unsigned int i = 0; i < numVertices; i++) {
struct _Vertex* v = (struct _Vertex*)malloc(sizeof(struct _Vertex));
if (v == NULL) abort();
v->id = i;
v->inDegree = 0;
v->outDegree = 0;
v->edgesList = ListCreate(graphEdgesComparator);
ListInsert(g->verticesList, v);
}
assert(g->numVertices == ListGetSize(g->verticesList));
return g;
}
Graph* GraphCreateComplete(unsigned int numVertices, int isDigraph) {
Graph* g = GraphCreate(numVertices, isDigraph, 0);
g->isComplete = 1;
List* vertices = g->verticesList;
ListMoveToHead(vertices);
unsigned int i = 0;
for (; i < g->numVertices; ListMoveToNext(vertices), i++) {
struct _Vertex* v = ListGetCurrentItem(vertices);
List* edges = v->edgesList;
for (unsigned int j = 0; j < g->numVertices; j++) {
if (i == j) {
continue;
}
struct _Edge* new = (struct _Edge*)malloc(sizeof(struct _Edge));
if (new == NULL) abort();
new->adjVertex = j;
new->weight = 1;
ListInsert(edges, new);
}
if (g->isDigraph) {
v->inDegree = g->numVertices - 1;
v->outDegree = g->numVertices - 1;
} else {
v->outDegree = g->numVertices - 1;
}
}
if (g->isDigraph) {
g->numEdges = numVertices * (numVertices - 1);
} else {
g->numEdges = numVertices * (numVertices - 1) / 2;
}
return g;
}
void GraphDestroy(Graph** p) {
assert(*p != NULL);
Graph* g = *p;
List* vertices = g->verticesList;
if (ListIsEmpty(vertices) == 0) {
ListMoveToHead(vertices);
unsigned int i = 0;
for (; i < g->numVertices; ListMoveToNext(vertices), i++) {
struct _Vertex* v = ListGetCurrentItem(vertices);
List* edges = v->edgesList;
if (ListIsEmpty(edges) == 0) {
unsigned int i = 0;
ListMoveToHead(edges);
for (; i < ListGetSize(edges); ListMoveToNext(edges), i++) {
struct _Edge* e = ListGetCurrentItem(edges);
free(e);
}
}
ListDestroy(&(v->edgesList));
free(v);
}
}
ListDestroy(&(g->verticesList));
free(g);
*p = NULL;
}
Graph* GraphCopy(const Graph* g) {
// COMPLETAR !!
return NULL;
}
Graph* GraphFromFile(FILE* f) {
// COMPLETAR !!
return NULL;
}
// Graph
int GraphIsDigraph(const Graph* g) { return g->isDigraph; }
int GraphIsComplete(const Graph* g) { return g->isComplete; }
int GraphIsWeighted(const Graph* g) { return g->isWeighted; }
unsigned int GraphGetNumVertices(const Graph* g) { return g->numVertices; }
unsigned int GraphGetNumEdges(const Graph* g) { return g->numEdges; }
//
// For a graph
//
double GraphGetAverageDegree(const Graph* g) {
assert(g->isDigraph == 0);
return 2.0 * (double)g->numEdges / (double)g->numVertices;
}
static unsigned int _GetMaxDegree(const Graph* g) {
List* vertices = g->verticesList;
if (ListIsEmpty(vertices)) return 0;
unsigned int maxDegree = 0;
ListMoveToHead(vertices);
unsigned int i = 0;
for (; i < g->numVertices; ListMoveToNext(vertices), i++) {
struct _Vertex* v = ListGetCurrentItem(vertices);
if (v->outDegree > maxDegree) {
maxDegree = v->outDegree;
}
}
return maxDegree;
}
//
// For a graph
//
unsigned int GraphGetMaxDegree(const Graph* g) {
assert(g->isDigraph == 0);
return _GetMaxDegree(g);
}
//
// For a digraph
//
unsigned int GraphGetMaxOutDegree(const Graph* g) {
assert(g->isDigraph == 1);
return _GetMaxDegree(g);
}
// Vertices
//
// returns an array of size (outDegree + 1)
// element 0, stores the number of adjacent vertices
// and is followed by indices of the adjacent vertices
//
unsigned int* GraphGetAdjacentsTo(const Graph* g, unsigned int v) {
assert(v < g->numVertices);
// Node in the list of vertices
List* vertices = g->verticesList;
ListMove(vertices, v);
struct _Vertex* vPointer = ListGetCurrentItem(vertices);
unsigned int numAdjVertices = vPointer->outDegree;
unsigned int* adjacent =
(unsigned int*)calloc(1 + numAdjVertices, sizeof(unsigned int));
if (numAdjVertices > 0) {
adjacent[0] = numAdjVertices;
List* adjList = vPointer->edgesList;
ListMoveToHead(adjList);
for (unsigned int i = 0; i < numAdjVertices; ListMoveToNext(adjList), i++) {
struct _Edge* ePointer = ListGetCurrentItem(adjList);
adjacent[i + 1] = ePointer->adjVertex;
}
}
return adjacent;
}
//
// returns an array of size (outDegree + 1)
// element 0, stores the number of adjacent vertices
// and is followed by the distances to the adjacent vertices
//
double* GraphGetDistancesToAdjacents(const Graph* g, unsigned int v) {
assert(v < g->numVertices);
// Node in the list of vertices
List* vertices = g->verticesList;
ListMove(vertices, v);
struct _Vertex* vPointer = ListGetCurrentItem(vertices);
unsigned int numAdjVertices = vPointer->outDegree;
double* distance = (double*)calloc(1 + numAdjVertices, sizeof(double));
if (numAdjVertices > 0) {
distance[0] = numAdjVertices;
List* adjList = vPointer->edgesList;
ListMoveToHead(adjList);
for (unsigned int i = 0; i < numAdjVertices; ListMoveToNext(adjList), i++) {
struct _Edge* ePointer = ListGetCurrentItem(adjList);
distance[i + 1] = ePointer->weight;
}
}
return distance;
}
//
// For a graph
//
unsigned int GraphGetVertexDegree(Graph* g, unsigned int v) {
assert(g->isDigraph == 0);
assert(v < g->numVertices);
ListMove(g->verticesList, v);
struct _Vertex* p = ListGetCurrentItem(g->verticesList);
return p->outDegree;
}
//
// For a digraph
//
unsigned int GraphGetVertexOutDegree(Graph* g, unsigned int v) {
assert(g->isDigraph == 1);
assert(v < g->numVertices);
ListMove(g->verticesList, v);
struct _Vertex* p = ListGetCurrentItem(g->verticesList);
return p->outDegree;
}
//
// For a digraph
//
unsigned int GraphGetVertexInDegree(Graph* g, unsigned int v) {
assert(g->isDigraph == 1);
assert(v < g->numVertices);
ListMove(g->verticesList, v);
struct _Vertex* p = ListGetCurrentItem(g->verticesList);
return p->inDegree;
}
// Edges
static int _addEdge(Graph* g, unsigned int v, unsigned int w, double weight) {
struct _Edge* edge = (struct _Edge*)malloc(sizeof(struct _Edge));
edge->adjVertex = w;
edge->weight = weight;
ListMove(g->verticesList, v);
struct _Vertex* vertex = ListGetCurrentItem(g->verticesList);
int result = ListInsert(vertex->edgesList, edge);
if (result == -1) {
return 0;
} else {
g->numEdges++;
vertex->outDegree++;
ListMove(g->verticesList, w);
struct _Vertex* destVertex = ListGetCurrentItem(g->verticesList);
destVertex->inDegree++;
}
if (g->isDigraph == 0) {
// Bidirectional edge
struct _Edge* edge = (struct _Edge*)malloc(sizeof(struct _Edge));
edge->adjVertex = v;
edge->weight = weight;
ListMove(g->verticesList, w);
struct _Vertex* vertex = ListGetCurrentItem(g->verticesList);
result = ListInsert(vertex->edgesList, edge);
if (result == -1) {
return 0;
} else {
// g->numEdges++; // Do not count the same edge twice on a undirected
// graph !!
vertex->outDegree++;
}
}
return 1;
}
int GraphAddEdge(Graph* g, unsigned int v, unsigned int w) {
assert(g->isWeighted == 0);
assert(v != w);
assert(v < g->numVertices);
assert(w < g->numVertices);
return _addEdge(g, v, w, 1.0);
}
int GraphAddWeightedEdge(Graph* g, unsigned int v, unsigned int w,
double weight) {
assert(g->isWeighted == 1);
assert(v != w);
assert(v < g->numVertices);
assert(w < g->numVertices);
return _addEdge(g, v, w, weight);
}
int GraphRemoveEdge(Graph* g, unsigned int v, unsigned int w) {
// COMPLETAR !!
return 0;
}
// CHECKING
int GraphCheckInvariants(const Graph* g) {
// COMPLETAR !!
return 0;
}
// DISPLAYING on the console
void GraphDisplay(const Graph* g) {
printf("---\n");
if (g->isWeighted) {
printf("Weighted ");
}
if (g->isComplete) {
printf("COMPLETE ");
}
if (g->isDigraph) {
printf("Digraph\n");
printf("Max Out-Degree = %d\n", GraphGetMaxOutDegree(g));
} else {
printf("Graph\n");
printf("Max Degree = %d\n", GraphGetMaxDegree(g));
}
printf("Vertices = %2d | Edges = %2d\n", g->numVertices, g->numEdges);
List* vertices = g->verticesList;
ListMoveToHead(vertices);
unsigned int i = 0;
for (; i < g->numVertices; ListMoveToNext(vertices), i++) {
printf("%2d ->", i);
struct _Vertex* v = ListGetCurrentItem(vertices);
if (ListIsEmpty(v->edgesList)) {
printf("\n");
} else {
List* edges = v->edgesList;
unsigned int i = 0;
ListMoveToHead(edges);
for (; i < ListGetSize(edges); ListMoveToNext(edges), i++) {
struct _Edge* e = ListGetCurrentItem(edges);
if (g->isWeighted) {
printf(" %2d(%4.2f)", e->adjVertex, e->weight);
} else {
printf(" %2d", e->adjVertex);
}
}
printf("\n");
}
}
printf("---\n");
}
void GraphListAdjacents(const Graph* g, unsigned int v) {
printf("---\n");
unsigned int* array = GraphGetAdjacentsTo(g, v);
printf("Vertex %d has %d adjacent vertices -> ", v, array[0]);
for (unsigned int i = 1; i <= array[0]; i++) {
printf("%d ", array[i]);
}
printf("\n");
free(array);
printf("---\n");
}

View File

@ -0,0 +1,94 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira, Joao Manuel Rodrigues - June 2021, Nov 2023
//
// Graph - Using a list of adjacency lists representation
//
#ifndef _GRAPH_
#define _GRAPH_
#include <stdio.h>
typedef struct _GraphHeader Graph;
Graph* GraphCreate(unsigned int numVertices, int isDigraph, int isWeighted);
Graph* GraphCreateComplete(unsigned int numVertices, int isDigraph);
void GraphDestroy(Graph** p);
Graph* GraphCopy(const Graph* g);
Graph* GraphFromFile(FILE* f);
// Graph
int GraphIsDigraph(const Graph* g);
int GraphIsComplete(const Graph* g);
int GraphIsWeighted(const Graph* g);
unsigned int GraphGetNumVertices(const Graph* g);
unsigned int GraphGetNumEdges(const Graph* g);
//
// For a graph
//
double GraphGetAverageDegree(const Graph* g);
//
// For a graph
//
unsigned int GraphGetMaxDegree(const Graph* g);
//
// For a digraph
//
unsigned int GraphGetMaxOutDegree(const Graph* g);
// Vertices
unsigned int* GraphGetAdjacentsTo(const Graph* g, unsigned int v);
// Vertices distances
double* GraphGetDistancesToAdjacents(const Graph* g, unsigned int v);
//
// For a graph
//
unsigned int GraphGetVertexDegree(Graph* g, unsigned int v);
//
// For a digraph
//
unsigned int GraphGetVertexOutDegree(Graph* g, unsigned int v);
//
// For a digraph
//
unsigned int GraphGetVertexInDegree(Graph* g, unsigned int v);
// Edges
int GraphAddEdge(Graph* g, unsigned int v, unsigned int w);
int GraphAddWeightedEdge(Graph* g, unsigned int v, unsigned int w,
double weight);
int GraphRemoveEdge(Graph* g, unsigned int v, unsigned int w);
// CHECKING
int GraphCheckInvariants(const Graph* g);
// DISPLAYING on the console
void GraphDisplay(const Graph* g);
void GraphListAdjacents(const Graph* g, unsigned int v);
#endif // _GRAPH_

View File

@ -0,0 +1,126 @@
//
// Joaquim Madeira, AlgC, May 2020
// João Manuel Rodrigues, AlgC, May 2020
//
// GraphDFSRec - RECURSIVE Depth-First Search
//
#include "GraphDFSRec.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "Graph.h"
#include "IntegersStack.h"
struct _GraphDFSRec {
unsigned int* marked;
int* predecessor;
Graph* graph;
unsigned int startVertex;
};
static void _dfs(GraphDFSRec* traversal, unsigned int vertex) {
traversal->marked[vertex] = 1;
unsigned int* neighbors = GraphGetAdjacentsTo(traversal->graph, vertex);
for (unsigned int i = 1; i <= neighbors[0]; i++) {
unsigned int w = neighbors[i];
if (traversal->marked[w] == 0) {
traversal->predecessor[w] = vertex;
_dfs(traversal, w);
}
}
free(neighbors);
}
GraphDFSRec* GraphDFSRecExecute(Graph* g, unsigned int startVertex) {
assert(g != NULL);
assert(startVertex < GraphGetNumVertices(g));
GraphDFSRec* result = (GraphDFSRec*)malloc(sizeof(struct _GraphDFSRec));
assert(result != NULL);
unsigned int numVertices = GraphGetNumVertices(g);
result->marked = (unsigned int*)calloc(numVertices, sizeof(unsigned int));
assert(result->marked != NULL);
result->predecessor = (int*)malloc(numVertices * sizeof(int));
assert(result->predecessor != NULL);
for (unsigned int i = 0; i < numVertices; i++) {
result->predecessor[i] = -1;
}
result->predecessor[startVertex] = 0;
result->graph = g;
result->startVertex = startVertex;
// START THE RECURSIVE TRAVERSAL
_dfs(result, startVertex);
return result;
}
void GraphDFSRecDestroy(GraphDFSRec** p) {
assert(*p != NULL);
GraphDFSRec* aux = *p;
free(aux->marked);
free(aux->predecessor);
free(*p);
*p = NULL;
}
// Getting the result
unsigned int GraphDFSRecHasPathTo(const GraphDFSRec* p, unsigned int v) {
assert(v < GraphGetNumVertices(p->graph));
return p->marked[v];
}
Stack* GraphDFSRecPathTo(const GraphDFSRec* p, unsigned int v) {
assert(v < GraphGetNumVertices(p->graph));
Stack* s = StackCreate(GraphGetNumVertices(p->graph));
if (p->marked[v] == 0) {
return s;
}
// Store the path
for (unsigned int current = v; current != p->startVertex;
current = p->predecessor[current]) {
StackPush(s, current);
}
StackPush(s, p->startVertex);
return s;
}
// DISPLAYING on the console
void GraphDFSRecShowPath(const GraphDFSRec* p, unsigned int v) {
assert(v < GraphGetNumVertices(p->graph));
Stack* s = GraphDFSRecPathTo(p, v);
while (StackIsEmpty(s) == 0) {
printf("%d ", StackPop(s));
}
StackDestroy(&s);
}
void GraphDFSRecDisplay(const GraphDFSRec* p) {
// COMPLETAR !!
}

View File

@ -0,0 +1,32 @@
//
// Joaquim Madeira, AlgC, May 2020
// João Manuel Rodrigues, AlgC, May 2020
//
// GraphDFS - RECURSIVE Depth-First Search
//
#ifndef _GRAPH_DFS_REC_
#define _GRAPH_DFS_REC_
#include "Graph.h"
#include "IntegersStack.h"
typedef struct _GraphDFSRec GraphDFSRec;
GraphDFSRec* GraphDFSRecExecute(Graph* g, unsigned int startVertex);
void GraphDFSRecDestroy(GraphDFSRec** p);
// Getting the result
unsigned int GraphDFSRecHasPathTo(const GraphDFSRec* p, unsigned int v);
Stack* GraphDFSRecPathTo(const GraphDFSRec* p, unsigned int v);
// DISPLAYING on the console
void GraphDFSRecShowPath(const GraphDFSRec* p, unsigned int v);
void GraphDFSRecDisplay(const GraphDFSRec* p);
#endif // _GRAPH_DFS_REC_

View File

@ -0,0 +1,65 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira, Joao Manuel Rodrigues - April 2020, November 2023
//
// Adapted from Tomás Oliveira e Silva, AED, September 2015
//
// Integers stack (First In Last Out) implementation based on an array
//
#include "IntegersStack.h"
#include <assert.h>
#include <stdlib.h>
struct _IntStack {
int max_size; // maximum stack size
int cur_size; // current stack size
int* data; // the stack data (stored in an array)
};
Stack* StackCreate(int size) {
assert(size >= 1 && size <= 1000000);
Stack* s = (Stack*)malloc(sizeof(Stack));
if (s == NULL) abort();
s->max_size = size;
s->cur_size = 0;
s->data = (int*)malloc(size * sizeof(int));
if (s->data == NULL) {
free(s);
abort();
}
return s;
}
void StackDestroy(Stack** p) {
assert(*p != NULL);
Stack* s = *p;
free(s->data);
free(s);
*p = NULL;
}
void StackClear(Stack* s) { s->cur_size = 0; }
int StackSize(const Stack* s) { return s->cur_size; }
int StackIsFull(const Stack* s) { return (s->cur_size == s->max_size); }
int StackIsEmpty(const Stack* s) { return (s->cur_size == 0); }
int StackPeek(const Stack* s) {
assert(s->cur_size > 0);
return s->data[s->cur_size - 1];
}
void StackPush(Stack* s, int i) {
assert(s->cur_size < s->max_size);
s->data[s->cur_size++] = i;
}
int StackPop(Stack* s) {
assert(s->cur_size > 0);
return s->data[--(s->cur_size)];
}

View File

@ -0,0 +1,34 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira, Joao Manuel Rodrigues - April 2020, November 2023
//
// Adapted from Tomás Oliveira e Silva, AED, September 2015
//
// Integers stack (First In Last Out) implementation based on an array
//
#ifndef _INTEGERS_STACK_
#define _INTEGERS_STACK_
typedef struct _IntStack Stack;
Stack* StackCreate(int size);
void StackDestroy(Stack** p);
void StackClear(Stack* s);
int StackSize(const Stack* s);
int StackIsFull(const Stack* s);
int StackIsEmpty(const Stack* s);
int StackPeek(const Stack* s);
void StackPush(Stack* s, int i);
int StackPop(Stack* s);
#endif // _INTEGERS_STACK_

View File

@ -0,0 +1,34 @@
# To compile all programs, run:
# make
#
# AED, ua, 2023
CFLAGS += -g -Wall -Wextra
TARGETS = example1 example2
all: $(TARGETS)
example1: example1.o Graph.o SortedList.o
example2: example2.o Graph.o GraphDFSRec.o IntegersStack.o SortedList.o
# Dependencies of source files (obtained with gcc -MM)
example1.o: example1.c Graph.h
example2.o: example2.c Graph.h GraphDFSRec.h
Graph.o: Graph.c Graph.h SortedList.h
GraphDFSRec.o: GraphDFSRec.c GraphDFSRec.h IntegersStack.h
IntegersStack.o: IntegersStack.c IntegersStack.h
SortedList.o: SortedList.c SortedList.h
clean:
rm -f *.o
rm -f $(TARGETS)

View File

@ -0,0 +1,340 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira, April 2020, November 2023
//
// Adapted from Tomás Oliveira e Silva, AED, September 2015
//
// SORTED LIST implementation based on an linked list
//
// ***************** COMPLETAR AS FUNCOES !!! *******************
#include "SortedList.h"
#include <assert.h>
#include <stdlib.h>
struct _ListNode {
void* item;
struct _ListNode* next;
};
struct _SortedList {
int size; // current List size
struct _ListNode* head; // the head of the List
struct _ListNode* tail; // the tail of the List
struct _ListNode* current; // the current node
int currentPos; // the current node index
compFunc compare;
};
List* ListCreate(compFunc compF) {
List* l = (List*)malloc(sizeof(List));
assert(l != NULL);
l->size = 0;
l->head = NULL;
l->tail = NULL;
l->current = NULL;
l->currentPos = -1; // Default: before the head of the list
l->compare = compF;
return l;
}
void ListDestroy(List** p) {
assert(*p != NULL);
List* l = *p;
ListClear(l);
free(l);
*p = NULL;
}
void ListClear(List* l) {
assert(l != NULL);
struct _ListNode* p = l->head;
struct _ListNode* aux;
while (p != NULL) {
aux = p;
p = aux->next;
free(aux);
}
l->size = 0;
l->head = NULL;
l->tail = NULL;
l->current = NULL;
l->currentPos = -1; // Default: before the head of the list
}
unsigned int ListGetSize(const List* l) {
assert(l != NULL);
return l->size;
}
int ListIsEmpty(const List* l) {
assert(l != NULL);
return (l->size == 0) ? 1 : 0;
}
// Current node functions
int ListGetCurrentIndex(const List* l) {
assert(l != NULL);
return l->currentPos;
}
void* ListGetCurrentItem(const List* l) {
assert(l != NULL && l->current != NULL);
return l->current->item;
}
void ListModifyCurrentValue(const List* l, void* p) {
assert(l != NULL && l->current != NULL);
l->current->item = p;
}
// The move and search functions return 0 on success and -1 on failure (on
// success the current node is changed, on failure it is not changed)
// Search
//
// starting at the current node, search for the first node with a value of *p
// on failure the current node is not changed
//
int ListSearch(List* l, const void* p) {
int i = (l->currentPos < 0) ? 0 : l->currentPos;
struct _ListNode* sn = (l->currentPos < 0) ? l->head : l->current;
while (i < l->size && l->compare(p, sn->item) > 0) {
i++;
sn = sn->next;
}
if (i == l->size) {
return -1;
} // failure
if (l->compare(p, sn->item) < 0) {
return -1;
} // failure
l->current = sn;
l->currentPos = i;
return 0; // success
}
// Move to functions
int ListMove(List* l, int newPos) {
if (newPos < -1 || newPos > l->size) {
return -1;
} // failure
if (newPos == -1 || newPos == l->size) {
l->current = NULL;
} else if (newPos == 0) {
l->current = l->head;
} else if (newPos == l->size - 1) {
l->current = l->tail;
} else {
if (l->currentPos == -1 || l->currentPos == l->size ||
newPos < l->currentPos) {
l->current = l->head;
l->currentPos = 0;
}
for (int i = l->currentPos; i < newPos; i++) {
l->current = l->current->next;
}
}
l->currentPos = newPos;
return 0; // success
}
int ListMoveToNext(List* l) { return ListMove(l, l->currentPos + 1); }
int ListMoveToPrevious(List* l) { return ListMove(l, l->currentPos - 1); }
int ListMoveToHead(List* l) { return ListMove(l, 0); }
int ListMoveToTail(List* l) { return ListMove(l, l->size - 1); }
// Insert function
//
// insert a node
// the current node is not changed
// return 0 on success
// return -1 on failure
//
int ListInsert(List* l, void* p) {
struct _ListNode* sn = (struct _ListNode*)malloc(sizeof(struct _ListNode));
assert(sn != NULL);
sn->item = p;
sn->next = NULL;
// Empty list
if (l->size == 0) {
l->head = l->tail = sn;
l->size = 1;
return 0;
}
// Search
int i = 0;
struct _ListNode* prev = NULL;
struct _ListNode* aux = l->head;
while (i < l->size && l->compare(p, aux->item) > 0) {
i++;
prev = aux;
aux = aux->next;
}
if (i == l->size) { // Append at the tail
l->tail->next = sn;
l->tail = sn;
l->size++;
return 0;
}
if (l->compare(p, aux->item) == 0) { // Already exists !!
free(sn);
return -1;
} // failure
if (i == 0) { // Append at the head
sn->next = l->head;
l->head = sn;
l->size++;
if (l->currentPos >= 0) {
l->currentPos++;
}
return 0;
}
sn->next = aux;
prev->next = sn;
l->size++;
if (l->currentPos >= i) {
l->currentPos++;
}
return 0;
}
// Remove functions
//
// remove the head of the list and make its next node the new head
// if the current node is the head, it is replaced by its next node
//
void* ListRemoveHead(List* l) {
assert(l->size > 0);
if (l->current == l->head) {
l->current = l->head->next;
l->currentPos++;
}
if (l->size == 1) {
void* p = l->head->item;
free(l->head);
l->head = NULL;
l->tail = NULL;
l->size = 0;
if (l->currentPos > 0) l->currentPos = 0;
return p;
} else {
struct _ListNode* sn = l->head->next;
void* p = l->head->item;
free(l->head);
l->head = sn;
if (l->currentPos > 0) l->currentPos--;
l->size--;
return p;
}
}
//
// remove the tail of the list and make its previous node the new tail
// if the current node is the tail, it is replaced by it by its next node
//
void* ListRemoveTail(List* l) {
assert(l->size > 0);
if (l->current == l->tail) {
l->current = NULL;
l->currentPos++;
}
if (l->size == 1) {
void* p = l->head->item;
free(l->head);
l->head = NULL;
l->tail = NULL;
l->current = NULL;
if (l->currentPos > 0) l->currentPos = 0;
l->size = 0;
return p;
} else {
struct _ListNode* sn = l->head;
while (sn->next != l->tail) sn = sn->next;
sn->next = NULL;
void* p = l->tail->item;
free(l->tail);
l->tail = sn;
if (l->currentPos == l->size) l->currentPos--;
l->size--;
return p;
}
}
//
// remove the current node and make its next node the current node
//
void* ListRemoveCurrent(List* l) {
assert(l->currentPos >= 0 && l->currentPos < l->size);
if (l->currentPos == 0)
return ListRemoveHead(l);
else if (l->currentPos == l->size - 1)
return ListRemoveTail(l);
else {
struct _ListNode* sn = l->head;
while (sn->next != l->current) sn = sn->next;
sn->next = l->current->next;
void* p = l->current->item;
free(l->current);
l->current = sn->next;
l->size--;
return p;
}
}
// Tests
void ListTestInvariants(const List* l) {
assert(l->size >= 0);
if (l->size == 0)
assert(l->head == NULL && l->tail == NULL);
else
assert(l->head != NULL && l->tail != NULL);
if (l->size == 1) assert(l->head == l->tail);
assert(-1 <= l->currentPos && l->currentPos <= l->size);
if (l->currentPos == -1 || l->currentPos == l->size)
assert(l->current == NULL);
struct _ListNode* sn = l->head;
for (int i = 0; i < l->size; i++) {
if (i == l->size - 1)
assert(sn == l->tail && sn->next == NULL);
else
assert(sn->next != NULL);
if (i == l->currentPos) assert(sn == l->current);
sn = sn->next;
}
}

View File

@ -0,0 +1,70 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira, April 2020, November 2023
//
// Adapted from Tomás Oliveira e Silva, AED, September 2015
//
// SORTED LIST implementation based on a linked list
//
#ifndef _SORTED_LIST_
#define _SORTED_LIST_
typedef struct _SortedList List;
typedef int (*compFunc)(const void* p1, const void* p2);
List* ListCreate(compFunc compF);
void ListDestroy(List** p);
void ListClear(List* l);
unsigned int ListGetSize(const List* l);
int ListIsEmpty(const List* l);
// Current node functions
int ListGetCurrentIndex(const List* l);
void* ListGetCurrentItem(const List* l);
void ListModifyCurrentItem(const List* l, void* p);
// The move and search functions return 0 on success and -1 on failure (on
// success the current node is changed, on failure it is not changed)
// Search
int ListSearch(List* l, const void* p);
// Move to
int ListMove(List* l, int newPos);
int ListMoveToNext(List* l);
int ListMoveToPrevious(List* l);
int ListMoveToHead(List* l);
int ListMoveToTail(List* l);
// Insert
int ListInsert(List* l, void* p);
// Remove
void* ListRemoveHead(List* l);
void* ListRemoveTail(List* l);
void* ListRemoveCurrent(List* l);
// Tests
void ListTestInvariants(const List* l);
#endif // _SORTED_LIST_

View File

@ -0,0 +1,51 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira, Joao Manuel Rodrigues - June 2021, Nov 2023
//
// Graph EXAMPLE
//
#include "Graph.h"
int main(void) {
// What kind of graph is g01?
Graph* g01 = GraphCreate(6, 0, 0);
GraphAddEdge(g01, 1, 2);
GraphAddEdge(g01, 1, 4);
GraphAddEdge(g01, 3, 4);
printf("The first graph:\n");
GraphDisplay(g01);
for (int i = 0; i < 6; i++) {
GraphListAdjacents(g01, i);
}
printf("Remove edge (1,2)\n");
GraphDisplay(g01);
for (int i = 0; i < 6; i++) {
GraphListAdjacents(g01, i);
}
Graph* dig01 = GraphCreate(6, 1, 0);
GraphAddEdge(dig01, 1, 2);
GraphAddEdge(dig01, 1, 4);
GraphAddEdge(dig01, 3, 4);
printf("The second graph:\n");
GraphDisplay(dig01);
printf("Remove edge (1,2)\n");
GraphDisplay(dig01);
Graph* g03 = GraphCreate(6, 0, 1);
GraphAddWeightedEdge(g03, 1, 2, 3);
GraphAddWeightedEdge(g03, 1, 4, 5);
GraphAddWeightedEdge(g03, 3, 4, 10);
printf("The third graph:\n");
GraphDisplay(g03);
printf("Remove edge (1,2)\n");
GraphDisplay(g03);
GraphDestroy(&g01);
GraphDestroy(&dig01);
GraphDestroy(&g03);
return 0;
}

View File

@ -0,0 +1,64 @@
//
// Algoritmos e Estruturas de Dados --- 2023/2024
//
// Joaquim Madeira, Joao Manuel Rodrigues - May 2020, Nov 2023
//
// Graph EXAMPLE
//
#include "Graph.h"
#include "GraphDFSRec.h"
int main(void) {
// Graph
Graph* g01 = GraphCreate(6, 0, 0);
GraphAddEdge(g01, 0, 5);
GraphAddEdge(g01, 2, 4);
GraphAddEdge(g01, 2, 3);
GraphAddEdge(g01, 1, 2);
GraphAddEdge(g01, 0, 1);
GraphAddEdge(g01, 3, 4);
GraphAddEdge(g01, 3, 5);
GraphAddEdge(g01, 0, 2);
GraphDisplay(g01);
// DFS traversal starting at vertex 0
GraphDFSRec* traversal = GraphDFSRecExecute(g01, 0);
printf("Path from 0 to 5: ");
GraphDFSRecShowPath(traversal, 5);
printf("\n");
GraphDFSRecDestroy(&traversal);
GraphDestroy(&g01);
// Digraph
g01 = GraphCreate(6, 1, 0);
GraphAddEdge(g01, 0, 5);
GraphAddEdge(g01, 2, 4);
GraphAddEdge(g01, 2, 3);
GraphAddEdge(g01, 1, 2);
GraphAddEdge(g01, 0, 1);
GraphAddEdge(g01, 3, 4);
GraphAddEdge(g01, 3, 5);
GraphAddEdge(g01, 0, 2);
GraphDisplay(g01);
// DFS traversal starting at vertex 1
traversal = GraphDFSRecExecute(g01, 1);
printf("Path from 1 to 5: ");
GraphDFSRecShowPath(traversal, 5);
printf("\n");
GraphDFSRecDestroy(&traversal);
GraphDestroy(&g01);
return 0;
}

View File

@ -0,0 +1,115 @@
// J. Madeira --- February 2022
#include "Fraction.h"
#include <cassert>
#include <iostream>
#include <numeric>
Fraction::Fraction(void) : numerator_(0), denominator_(1) {}
Fraction::Fraction(int numerator, int denominator) {
assert(denominator > 0);
numerator_ = numerator;
denominator_ = denominator;
Reduce();
}
int Fraction::GetNumerator(void) const { return numerator_; }
void Fraction::SetNumerator(int n) {
numerator_ = n;
Reduce();
}
int Fraction ::GetDenominator(void) const { return denominator_; }
void Fraction::SetDenominator(int n) {
assert(n > 0);
denominator_ = n;
Reduce();
}
// Comparison operators
bool Fraction::operator==(const Fraction& frac) const {
return (numerator_ == frac.numerator_) && (denominator_ == frac.denominator_);
}
bool Fraction::operator!=(const Fraction& frac) const {
return !(*this == frac);
}
bool Fraction::operator<(const Fraction& frac) const {
// Not the smartest way
return ToDouble() < frac.ToDouble();
}
// Unary operator
Fraction Fraction::operator-(void) const {
Fraction res(-numerator_, denominator_);
return res;
}
// Binary operators
Fraction Fraction::operator+(const Fraction& frac) const {
Fraction res(*this);
if (res.denominator_ == frac.denominator_) {
res.numerator_ += frac.numerator_;
} else {
res.numerator_ =
res.numerator_ * frac.denominator_ + frac.numerator_ * res.denominator_;
res.denominator_ *= frac.denominator_;
}
res.Reduce();
return res;
}
Fraction Fraction::operator-(const Fraction& frac) const {
return *this + (-frac);
}
Fraction Fraction::operator*(const Fraction& frac) const {
Fraction res(*this);
res.numerator_ *= frac.numerator_;
res.denominator_ *= frac.denominator_;
res.Reduce();
return res;
}
Fraction Fraction::operator/(const Fraction& frac) const {
assert(frac.numerator_ != 0);
Fraction res(*this);
res.numerator_ *= frac.denominator_;
res.denominator_ *= frac.numerator_;
// Ensure the denominator is POSITIVE
if (res.denominator_ < 0) {
res.numerator_ *= -1;
res.denominator_ *= -1;
}
res.Reduce();
return res;
}
double Fraction::ToDouble(void) const {
return (double)numerator_ / (double)denominator_;
}
std::ostream& operator<<(std::ostream& os, const Fraction& frac) {
os << frac.numerator_ << " / " << frac.denominator_;
return os;
}
void Fraction::Reduce(void) {
int gcd = std::gcd(numerator_, denominator_); // Since C++17
if (gcd != 1) {
numerator_ /= gcd;
denominator_ /= gcd;
}
}

View File

@ -0,0 +1,44 @@
// J. Madeira --- March 2023
#ifndef FRACTION_H_
#define FRACTION_H_
#include <iostream>
class Fraction {
public:
Fraction(void);
Fraction(int numerator, int denominator = 1);
int GetNumerator(void) const;
void SetNumerator(int n);
int GetDenominator(void) const;
void SetDenominator(int n);
// Comparison operators
bool operator==(const Fraction& frac) const;
bool operator!=(const Fraction& frac) const;
bool operator<(const Fraction& frac) const;
// Unary operator
Fraction operator-(void) const;
// Binary operators
Fraction operator+(const Fraction& frac) const;
Fraction operator-(const Fraction& frac) const;
Fraction operator*(const Fraction& frac) const;
Fraction operator/(const Fraction& frac) const;
double ToDouble(void) const;
friend std::ostream& operator<<(std::ostream& out, const Fraction& frac);
private:
void Reduce(void);
int numerator_;
int denominator_; // ALWAYS POSITIVE !!!
};
#endif // FRACTION_H_

View File

@ -0,0 +1,4 @@
Compile with
g++ TestFraction.cpp Fraction.cpp -Wall -Wextra -std=c++17

View File

@ -0,0 +1,52 @@
// J. Madeira --- February 2022
#include <iostream>
#include "Fraction.h"
int main(void) {
Fraction zero; // Has value ZERO
Fraction fraction_1;
Fraction fraction_2(5);
Fraction fraction_3(2, 4);
std::cout << "1st fraction: " << fraction_1 << " = " << fraction_1.ToDouble()
<< std::endl;
std::cout << "2nd fraction: " << fraction_2 << " = " << fraction_2.ToDouble()
<< std::endl;
std::cout << "3rd fraction: " << fraction_3 << " = " << fraction_3.ToDouble()
<< std::endl;
std::cout << fraction_1 << " is equal to " << fraction_3;
std::cout << " : " << std::boolalpha << (fraction_2 == fraction_3)
<< std::endl;
std::cout << fraction_2 << " is different from " << fraction_3;
std::cout << " : " << std::boolalpha << (fraction_2 != fraction_3)
<< std::endl;
// For this simple class, the assignment operator works
fraction_1 = fraction_2;
std::cout << fraction_1 << " is equal to " << fraction_2;
std::cout << " : " << std::boolalpha << (fraction_1 == fraction_2)
<< std::endl;
// Arithmetic operations
std::cout << fraction_2 << " + " << fraction_3;
std::cout << " = " << fraction_2 + fraction_3 << std::endl;
std::cout << fraction_1 << " - " << fraction_2;
std::cout << " = " << fraction_1 - fraction_2 << std::endl;
std::cout << fraction_1 << " * " << fraction_2;
std::cout << " = " << fraction_1 * fraction_2 << std::endl;
std::cout << fraction_2 << " / " << fraction_3;
std::cout << " = " << fraction_2 / fraction_3 << std::endl;
return 0;
}

View File

@ -0,0 +1,64 @@
// J. Madeira --- March 2023
#include "Counter.h"
#include <cassert>
#include <iostream>
#include <limits>
Counter::Counter(unsigned initial_value) { value_ = initial_value; }
unsigned Counter::GetValue(void) const { return value_; }
void Counter::Inc(void) {
if (value_ < std::numeric_limits<unsigned int>::max()) {
value_++;
}
}
void Counter::Dec(void) {
if (value_ > 0) {
value_--;
}
}
// Comparison operators
bool Counter::operator==(const Counter& c) const {
return (value_ == c.value_);
}
bool Counter::operator!=(const Counter& c) const { return !(*this == c); }
bool Counter::operator<(const Counter& c) const { return value_ < c.value_; }
// Postfix operators
// Extra parameter to allow for prefix and postfix notations
Counter Counter::operator++(int) {
Counter old_counter = *this;
Inc();
return old_counter;
}
Counter Counter::operator--(int) {
Counter old_counter = *this;
Dec();
return old_counter;
}
// Prefix operators
Counter& Counter::operator++(void) {
Inc();
return *this;
}
Counter& Counter::operator--(void) {
Dec();
return *this;
}
std::ostream& operator<<(std::ostream& os, const Counter& c) {
os << c.value_;
return os;
}

View File

@ -0,0 +1,37 @@
// J. Madeira --- March 2023
#ifndef COUNTER_H_
#define COUNTER_H_
#include <iostream>
class Counter {
public:
Counter(unsigned initial_value = 0);
unsigned GetValue(void) const;
void Inc(void);
void Dec(void);
// Comparison operators
bool operator==(const Counter& c) const;
bool operator!=(const Counter& c) const;
bool operator<(const Counter& c) const;
// Postfix operators
// Extra parameter to allow for prefix and postfix notations
Counter operator++(int);
Counter operator--(int);
// Prefix operators
Counter& operator++(void);
Counter& operator--(void);
friend std::ostream& operator<<(std::ostream& out, const Counter& c);
protected:
unsigned value_;
};
#endif // COUNTER_H_

View File

@ -0,0 +1,40 @@
// J. Madeira --- March 2023
#include "LimitedCounter.h"
#include <cassert>
#include <iostream>
#include <limits>
#include "Counter.h"
LimitedCounter::LimitedCounter(unsigned max_value, unsigned initial_value)
: Counter(initial_value) {
max_value_ = max_value;
}
void LimitedCounter::Inc(void) {
if (value_ < max_value_) {
value_++;
}
}
// Postfix operators
// Extra parameter to allow for prefix and postfix notations
LimitedCounter LimitedCounter::operator++(int) {
LimitedCounter old_LimitedCounter = *this;
Inc();
return old_LimitedCounter;
}
// Prefix operators
LimitedCounter& LimitedCounter::operator++(void) {
Inc();
return *this;
}
std::ostream& operator<<(std::ostream& os, const LimitedCounter& c) {
os << c.value_;
return os;
}

View File

@ -0,0 +1,33 @@
// J. Madeira --- March 2023
#ifndef LIMITED_COUNTER_H_
#define LIMITED_COUNTER_H_
#include <iostream>
#include "Counter.h"
class LimitedCounter : public Counter {
public:
LimitedCounter(unsigned max_value, unsigned initial_value = 0);
void Inc(void);
// Postfix operator
// Extra parameter to allow for prefix and postfix notations
LimitedCounter operator++(int);
// operator-- is inherited!!
// Prefix operator
LimitedCounter& operator++(void);
// operator-- is inherited!!
friend std::ostream& operator<<(std::ostream& out, const LimitedCounter& c);
private:
unsigned max_value_;
};
#endif // LIMITED_COUNTER_H_

View File

@ -0,0 +1,3 @@
The value_ attibute of the base class is now protected -> Efficiency!

View File

@ -0,0 +1,93 @@
// J. Madeira --- March 2023
#include <iostream>
#include "Counter.h"
#include "LimitedCounter.h"
int main(void) {
std::cout << "COUNTERS" << std::endl;
Counter counter_1;
Counter counter_2(5);
Counter counter_3(4);
std::cout << "1st counter: " << counter_1 << std::endl;
std::cout << "2nd counter: " << counter_2 << std::endl;
std::cout << "3rd counter: " << counter_3 << std::endl;
std::cout << counter_1 << " is equal to " << counter_3;
std::cout << " : " << std::boolalpha << (counter_1 == counter_3) << std::endl;
std::cout << counter_2 << " is different from " << counter_3;
std::cout << " : " << std::boolalpha << (counter_2 != counter_3) << std::endl;
std::cout << counter_2 << " is smaller than " << counter_3;
std::cout << " : " << std::boolalpha << (counter_2 < counter_3) << std::endl;
// For this simple class, the assignment operator works
counter_1 = counter_2;
std::cout << counter_1 << " is equal to " << counter_2;
std::cout << " : " << std::boolalpha << (counter_1 == counter_2) << std::endl;
// Increment / Decrement operations
std::cout << "1st counter: " << counter_1 << std::endl;
++counter_1;
std::cout << "After ++counter_1: " << counter_1 << std::endl;
counter_1++;
std::cout << "After counter_1++: " << counter_1 << std::endl;
Counter aux;
std::cout << "aux counter: " << aux << std::endl;
--counter_1;
std::cout << "After --aux: " << aux << std::endl;
std::cout << "LIMITED COUNTERS" << std::endl;
LimitedCounter c_1(10);
LimitedCounter c_2(5);
std::cout << "1st counter: " << c_1 << std::endl;
std::cout << "2nd counter: " << c_2 << std::endl;
for (int i = 0; i < 12; i++) {
c_1++;
++c_2;
}
std::cout << "1st counter: " << c_1 << std::endl;
std::cout << "2nd counter: " << c_2 << std::endl;
std::cout << c_1 << " is equal to " << c_2;
std::cout << " : " << std::boolalpha << (c_1 == c_2) << std::endl;
std::cout << c_2 << " is different from " << c_1;
std::cout << " : " << std::boolalpha << (c_2 != c_1) << std::endl;
std::cout << c_2 << " is smaller than " << c_1;
std::cout << " : " << std::boolalpha << (c_2 < c_1) << std::endl;
for (int i = 0; i < 12; i++) {
--c_1;
c_2--;
}
std::cout << "1st counter: " << c_1 << std::endl;
std::cout << "2nd counter: " << c_2 << std::endl;
return 0;
}

View File

@ -0,0 +1,33 @@
// Exercise 14-1 Animals.cpp
// Implementations of the Animal class and classes derived from Animal
#include "Animals.h"
// Constructor
Animal::Animal(std::string_view theName, unsigned wt)
: name(theName), weight(wt)
{}
// Return string describing the animal
std::string Animal::who() const
{
return "My name is " + name + ". My weight is " + std::to_string(weight) + " lbs.";
}
// Make like a sheep
std::string_view Sheep::sound() const
{
return "Baaaa!!";
}
// Make like a dog
std::string_view Dog::sound() const
{
return "Woof woof!!";
}
// Make like a cow
std::string_view Cow::sound() const
{
return "Mooooo!!";
}

View File

@ -0,0 +1,43 @@
// Exercise 14-1 Animals.h
// Animal classes
#ifndef ANIMALS_H
#define ANIMALS_H
#include <string>
#include <string_view>
class Animal
{
private:
std::string name; // Name of the animal
unsigned weight; // Weight of the animal
public:
Animal(std::string_view theName, unsigned wt); // Constructor
virtual ~Animal() = default; // Very important: a virtual destructor!
virtual std::string who() const; // Return string containing name and weight
virtual std::string_view sound() const = 0; // Return the sound of an animal
};
class Sheep : public Animal
{
public:
using Animal::Animal; // Inherit constructor
std::string_view sound() const override; // Return the sound of a sheep
};
class Dog : public Animal
{
public:
using Animal::Animal; // Inherit constructor
std::string_view sound() const override; // Return the sound of a dog
};
class Cow : public Animal
{
public:
using Animal::Animal; // Inherit constructor
std::string_view sound() const override; // Return the sound of a cow
};
#endif

View File

@ -0,0 +1,39 @@
-> Compile with -std=c++17, if needed.
-> std::string_view
Conceptually, string_view is only a view of the string and cannot be used to modify the actual string.
When a string_view is created, there's no need to copy the data (unlike when you create a copy of a string).
Furthermore, in terms of size on the heap, string_view is smaller than std::string
-> virtual function specifier
Virtual functions are member functions whose behavior can be overridden in derived classes.
As opposed to non-virtual functions, the overriding behavior is preserved even if there is no compile-time information about the actual type of the class.
That is to say, if a derived class is handled using pointer or reference to the base class, a call to an overridden virtual function would invoke the behavior defined in the derived class.
Such a function call is known as virtual function call or virtual call.
Virtual function call is suppressed if the function is selected using qualified name lookup (that is, if the function's name appears to the right of the scope resolution operator ::).
-> std::shared_ptr
Defined in header <memory>
A smart pointer that retains shared ownership of an object through a pointer.
Several shared_ptr objects may own the same object.
The object is destroyed and its memory deallocated when either of the following happens:
- the last remaining shared_ptr owning the object is destroyed;
- the last remaining shared_ptr owning the object is assigned another pointer via operator= or reset().
-> final specifier
Specifies that a virtual function cannot be overridden in a derived class or that a class cannot be derived from.

View File

@ -0,0 +1,63 @@
// Exercise 14-1 Exercising Zoo and Animal classes
#include <cstdlib> // For basic random number generation (std::rand() and srand())
#include <ctime> // For std::time() (used to seed the pseudo-random number generator)
#include <iostream>
#include "Zoo.h"
// Function to generate a random integer 0 to count-1
unsigned random(size_t count) {
return static_cast<unsigned>(rand() / (RAND_MAX / count + 1));
}
int main() {
std::srand(
static_cast<unsigned>(std::time(0))); // Seed random number generator
const std::vector<std::string> dogNames{
"Fido", "Rover", "Lassie", "Lambikins", "Poochy",
"Spit", "Gnasher", "Samuel", "Wellington", "Patch"};
const std::vector<std::string> sheepNames{
"Bozo", "Killer", "Tasty", "Pete", "Chops",
"Blackie", "Whitey", "Eric", "Sean", "Shep"};
const std::vector<std::string> cowNames{"Dolly", "Daisy", "Shakey", "Amy",
"Dilly", "Dizzy", "Eleanor", "Zippy",
"Zappy", "Happy"};
const unsigned minDogWt{1}; // Minimum weight of a dog in pounds
const unsigned maxDogWt{120}; // Maximum weight of a dog in pounds
const unsigned minSheepWt{80}; // Minimum weight of a dog in pounds
const unsigned maxSheepWt{150}; // Maximum weight of a dog in pounds
const unsigned minCowWt{800}; // Minimum weight of a dog in pounds
const unsigned maxCowWt{1500}; // Maximum weight of a dog in pounds
std::vector<AnimalPtr> animals; // Stores smart pointers to animals
size_t nAnimals{}; // Number of animals to be created
std::cout << "How many animals in the zoo? ";
std::cin >> nAnimals;
Zoo zoo; // Create an empty Zoo
// Create random animals and add them to the Zoo
for (size_t i{}; i < nAnimals; ++i) {
switch (random(3)) {
case 0: // Create a sheep
zoo.addAnimal(std::make_shared<Sheep>(
sheepNames[random(sheepNames.size())],
minSheepWt + random(maxSheepWt - minSheepWt + 1)));
break;
case 1: // Create a dog
zoo.addAnimal(
std::make_shared<Dog>(dogNames[random(dogNames.size())],
minDogWt + random(maxDogWt - minDogWt + 1)));
break;
case 2: // Create a cow
zoo.addAnimal(
std::make_shared<Cow>(cowNames[random(cowNames.size())],
minCowWt + random(maxCowWt - minCowWt + 1)));
break;
}
}
zoo.showAnimals(); // Display the animals
}

View File

@ -0,0 +1,23 @@
// Exercise 14-1 Zoo.cpp
// Implementations of the Zoo class that stores pointers to Animals
#include "Zoo.h"
#include "Animals.h"
#include <iostream>
// Constructor from a vector of animals
Zoo::Zoo(const std::vector<AnimalPtr>& new_animals) : animals {new_animals} {}
// Add an animal to the zoo
void Zoo::addAnimal(AnimalPtr animal)
{
animals.push_back(animal);
}
// Output the animals and the sound they make
void Zoo::showAnimals() const
{
for (auto animal : animals)
{
std::cout << animal->who() << ' ' << animal->sound() << std::endl;
}
}

View File

@ -0,0 +1,26 @@
// Exercise 14-1 Animals.h
// The Zoo class representing a collection of animals
#ifndef ZOO_H
#define ZOO_H
#include "Animals.h"
#include <vector>
#include <memory>
using AnimalPtr = std::shared_ptr<Animal>; // Define a type alias for convenience
class Zoo
{
private:
std::vector<AnimalPtr> animals; // Stores pointers to the animals
public:
Zoo() = default; // Default constructor for an empty zoo
Zoo(const std::vector<AnimalPtr>& new_animals); // Constructor from a vector of animals
virtual ~Zoo() = default; // Add a virtual destructr to allow classes to safely derive from Zoo;
// possible examples of Zoo specializations include SafariPark, PettingZoo, ...
void addAnimal(AnimalPtr animal); // Add an animal to the zoo
void showAnimals() const; // Output the animals and the sound they make
};
#endif

View File

@ -0,0 +1,115 @@
// J. Madeira --- February 2022
#include "Fraction.h"
#include <cassert>
#include <iostream>
#include <numeric>
Fraction::Fraction(void) : numerator_(0), denominator_(1) {}
Fraction::Fraction(int numerator, int denominator) {
assert(denominator > 0);
numerator_ = numerator;
denominator_ = denominator;
Reduce();
}
int Fraction::GetNumerator(void) const { return numerator_; }
void Fraction::SetNumerator(int n) {
numerator_ = n;
Reduce();
}
int Fraction ::GetDenominator(void) const { return denominator_; }
void Fraction::SetDenominator(int n) {
assert(n > 0);
denominator_ = n;
Reduce();
}
// Comparison operators
bool Fraction::operator==(const Fraction& frac) const {
return (numerator_ == frac.numerator_) && (denominator_ == frac.denominator_);
}
bool Fraction::operator!=(const Fraction& frac) const {
return !(*this == frac);
}
bool Fraction::operator<(const Fraction& frac) const {
// Not the smartest way
return ToDouble() < frac.ToDouble();
}
// Unary operator
Fraction Fraction::operator-(void) const {
Fraction res(-numerator_, denominator_);
return res;
}
// Binary operators
Fraction Fraction::operator+(const Fraction& frac) const {
Fraction res(*this);
if (res.denominator_ == frac.denominator_) {
res.numerator_ += frac.numerator_;
} else {
res.numerator_ =
res.numerator_ * frac.denominator_ + frac.numerator_ * res.denominator_;
res.denominator_ *= frac.denominator_;
}
res.Reduce();
return res;
}
Fraction Fraction::operator-(const Fraction& frac) const {
return *this + (-frac);
}
Fraction Fraction::operator*(const Fraction& frac) const {
Fraction res(*this);
res.numerator_ *= frac.numerator_;
res.denominator_ *= frac.denominator_;
res.Reduce();
return res;
}
Fraction Fraction::operator/(const Fraction& frac) const {
assert(frac.numerator_ != 0);
Fraction res(*this);
res.numerator_ *= frac.denominator_;
res.denominator_ *= frac.numerator_;
// Ensure the denominator is POSITIVE
if (res.denominator_ < 0) {
res.numerator_ *= -1;
res.denominator_ *= -1;
}
res.Reduce();
return res;
}
double Fraction::ToDouble(void) const {
return (double)numerator_ / (double)denominator_;
}
std::ostream& operator<<(std::ostream& os, const Fraction& frac) {
os << frac.numerator_ << " / " << frac.denominator_;
return os;
}
void Fraction::Reduce(void) {
int gcd = std::gcd(numerator_, denominator_); // Since C++17
if (gcd != 1) {
numerator_ /= gcd;
denominator_ /= gcd;
}
}

View File

@ -0,0 +1,44 @@
// J. Madeira --- March 2023
#ifndef FRACTION_H_
#define FRACTION_H_
#include <iostream>
class Fraction {
public:
Fraction(void);
Fraction(int numerator, int denominator = 1);
int GetNumerator(void) const;
void SetNumerator(int n);
int GetDenominator(void) const;
void SetDenominator(int n);
// Comparison operators
bool operator==(const Fraction& frac) const;
bool operator!=(const Fraction& frac) const;
bool operator<(const Fraction& frac) const;
// Unary operator
Fraction operator-(void) const;
// Binary operators
Fraction operator+(const Fraction& frac) const;
Fraction operator-(const Fraction& frac) const;
Fraction operator*(const Fraction& frac) const;
Fraction operator/(const Fraction& frac) const;
double ToDouble(void) const;
friend std::ostream& operator<<(std::ostream& out, const Fraction& frac);
private:
void Reduce(void);
int numerator_;
int denominator_; // ALWAYS POSITIVE !!!
};
#endif // FRACTION_H_

View File

@ -0,0 +1,48 @@
// Pair.h
// Just one generic type --- Adapted form Horton's example --- J. Madeira
#ifndef PAIR_H
#define PAIR_H
#include <ostream>
template <typename T>
class Pair {
public: // To simplify the example
T first_;
T second_;
Pair();
Pair(const T& f, const T& s);
bool operator==(const Pair& other) const;
bool operator<(const Pair& other) const;
};
// Default constructor
template <typename T>
Pair<T>::Pair() : first_{}, second_{} {}
// Constructor
template <typename T>
Pair<T>::Pair(const T& f, const T& s) : first_{f}, second_{s} {}
// Comparison operators
template <typename T>
bool Pair<T>::operator==(const Pair& other) const {
return first_ == other.first_ && second_ == other.second_;
}
template <typename T>
bool Pair<T>::operator<(const Pair& other) const {
return first_ < other.first_ ||
(first_ == other.first_ && second_ < other.second_);
}
template <typename T>
std::ostream& operator<<(std::ostream& stream, const Pair<T>& pair) {
return stream << '(' << pair.first_ << ", " << pair.second_ << ')';
}
#endif

View File

@ -0,0 +1,4 @@
If needed, compile with
g++ -Wall -Wextra -std=c++17 main.cpp Fraction.cpp

View File

@ -0,0 +1,72 @@
// Adapted from Horton's example
#include <iostream>
#include <string>
#include "Fraction.h"
#include "Pair.h"
int main(void) {
Pair<int> pair_1 = Pair<int>(123, 456);
std::cout << "1st pair is " << pair_1 << std::endl;
Pair<int> pair_2 = Pair<int>(654, 321);
std::cout << "2nd pair is " << pair_2 << std::endl;
Pair<int> pair_3 = Pair<int>(654, 322);
std::cout << "3rd pair is " << pair_3 << std::endl;
std::cout << (pair_1 < pair_2 && pair_2 < pair_3
? "operator< seems to be working"
: "oops")
<< std::endl;
std::cout << (pair_1 == pair_2 ? "oops" : "operator== works as well")
<< std::endl;
std::cout << (pair_2 == pair_3 ? "oops" : "operator== works as well")
<< std::endl;
Pair<std::string> pair_4 = Pair<std::string>("Joaquim", "Madeira");
std::cout << "4th pair is " << pair_4 << std::endl;
Pair<std::string> pair_5 = Pair<std::string>("Joao", "Ribeiro");
std::cout << "5th pair is " << pair_5 << std::endl;
Pair<std::string> pair_6 = Pair<std::string>("Joaquim", "Martins");
std::cout << "6th pair is " << pair_6 << std::endl;
std::cout << (pair_5 < pair_4 && pair_4 < pair_6
? "operator< seems to be working"
: "oops")
<< std::endl;
std::cout << (pair_4 == pair_5 ? "oops" : "operator== works as well")
<< std::endl;
std::cout << (pair_5 == pair_6 ? "oops" : "operator== works as well")
<< std::endl;
std::cout << "Can also use objects of the Fracion (or any) class..."
<< std::endl;
std::cout << "As long as the required operators are defined" << std::endl;
Pair<Fraction> pair_7 = Pair<Fraction>(Fraction(1, 2), Fraction(1, 4));
std::cout << "7th pair is " << pair_7 << std::endl;
Pair<Fraction> pair_8 = Pair<Fraction>(Fraction(1, 2), Fraction(1, 3));
std::cout << "8th pair is " << pair_8 << std::endl;
std::cout << (pair_7 < pair_8
? "pair_7 is lexicographically smaller than pair_8"
: "pair_7 is NOT lexicographically smaller than pair_8")
<< std::endl;
std::cout << (pair_8 < pair_7
? "pair_8 is lexicographically smaller than pair_7"
: "pair_8 is NOT lexicographically smaller than pair_7")
<< std::endl;
return 0;
}

View File

@ -0,0 +1,39 @@
#include <array>
#include <iostream>
using namespace std;
int main(void) {
array<unsigned, 4> a = {0, 1, 2, 3};
// display array size " 4"
cout << " " << a.size() << endl;
// display contents " 0 1 2 3"
for (const auto& e : a) {
cout << " " << e;
}
cout << endl;
// display first element " 0"
cout << " " << a.front();
cout << endl;
// change first element
a.front() = 9;
// display last element " 3"
cout << " " << a.back();
cout << endl;
// change last element
a.back() = 0;
// display contents " 9 1 2 0"
for (const auto& e : a) {
cout << " " << e;
}
cout << endl;
return (0);
}

View File

@ -0,0 +1,59 @@
#include <deque>
#include <iostream>
using namespace std;
int main(void) {
deque<unsigned> d = {0, 1, 2, 3};
// display deque size " 4"
cout << " " << d.size() << endl;
// display contents " 0 1 2 3"
for (const auto& e : d) {
cout << " " << e;
}
cout << endl;
// display first element " 0"
cout << " " << d.front();
cout << endl;
// change first element
d.front() = 9;
// display last element " 3"
cout << " " << d.back();
cout << endl;
// change last element
d.back() = 0;
// display contents " 9 1 2 0"
for (const auto& e : d) {
cout << " " << e;
}
cout << endl;
// At the front
d.pop_front();
d.pop_front();
d.push_front(0);
d.push_front(1);
// At the back
d.pop_back();
d.push_back(0);
d.push_back(1);
// At any position
d.at(2) = 9;
// display contents
for (const auto& e : d) {
cout << " " << e;
}
cout << endl;
return (0);
}

View File

@ -0,0 +1,35 @@
#include <iostream>
#include <map>
using namespace std;
int main(void) {
map<string, unsigned> m;
// display map size
cout << " " << m.size() << endl;
m["February"] = 2;
m["April"] = 4;
m.insert({"May", 5});
m.insert({"January", 1});
m.insert({"March", 3});
m["December"] = 12;
cout << "key = January value = " << m.at("January") << endl;
cout << "key = February value = " << m["February"] << endl;
cout << endl;
for (const auto& e : m) {
cout << "key = " << e.first << " value = " << e.second << endl;
}
cout << endl;
for (auto it = m.begin(); it != m.end(); ++it) {
cout << "key = " << (*it).first << " value = " << (*it).second << endl;
}
return (0);
}

View File

@ -0,0 +1,24 @@
#include <iostream>
#include <queue>
using namespace std;
int main(void) {
queue<unsigned> q;
// display queue size
cout << " " << q.size() << endl;
queue<size_t> uint_queue;
for (size_t i = 0; i < 10; ++i) {
uint_queue.push(i);
}
while (!uint_queue.empty()) {
std::cout << uint_queue.front() << std::endl;
uint_queue.pop();
}
return (0);
}

View File

@ -0,0 +1,34 @@
#include <iostream>
#include <set>
using namespace std;
int main(void) {
set<string> s;
// display set size
cout << " " << s.size() << endl;
s.insert("February");
s.insert("April");
s.insert("May");
s.insert("January");
s.insert("March");
s.insert("December");
s.insert("March");
s.erase("March");
cout << endl;
for (const auto& e : s) {
cout << e << endl;
}
cout << endl;
for (auto it = s.begin(); it != s.end(); ++it) {
cout << (*it) << endl;
}
return (0);
}

View File

@ -0,0 +1,24 @@
#include <iostream>
#include <stack>
using namespace std;
int main(void) {
stack<unsigned> s;
// display stack size
cout << " " << s.size() << endl;
stack<size_t> uint_stack;
for (size_t i = 0; i < 10; ++i) {
uint_stack.push(i);
}
while (!uint_stack.empty()) {
std::cout << uint_stack.top() << std::endl;
uint_stack.pop();
}
return (0);
}

View File

@ -0,0 +1,59 @@
#include <iostream>
#include <vector>
using namespace std;
int main(void) {
vector<unsigned> v = {0, 1, 2, 3};
// display vector size " 4"
cout << " " << v.size() << endl;
// display contents " 0 1 2 3"
for (const auto& e : v) {
cout << " " << e;
}
cout << endl;
// display first element " 0"
cout << " " << v.front();
cout << endl;
// change first element
v.front() = 9;
// display last element " 3"
cout << " " << v.back();
cout << endl;
// change last element
v.back() = 0;
// display contents " 9 1 2 0"
for (const auto& e : v) {
cout << " " << e;
}
cout << endl;
// At the front
v.erase(v.begin());
v.erase(v.begin());
v.insert(v.begin(), 0);
v.insert(v.begin(), 1);
// At the back
v.pop_back();
v.push_back(0);
v.push_back(1);
// At any position
v.at(2) = 9;
// display contents
for (const auto& e : v) {
cout << " " << e;
}
cout << endl;
return (0);
}

View File

@ -0,0 +1,50 @@
//
// AED --- 2023/2024
//
// J Madeira, Dec 2023
//
// Using a queue container to reverse the digits of a positive integer number
//
#include <iostream>
#include <queue>
using namespace std;
int main(void) {
int original_number = 0;
cout << "Write a positive integer value : ";
cin >> original_number;
if (original_number <= 0) {
cout << "*** Should be a positive integer!! ***" << endl;
return 0;
}
cout << "The original number : " << original_number << endl;
// Getting each digit and pushing it into a queue
queue<unsigned int> q;
unsigned int digit;
while (original_number > 0) {
digit = original_number % 10;
q.push(digit);
original_number /= 10;
}
// New number with digits in reverse order
unsigned int new_number = 0;
while (!q.empty()) {
digit = q.front();
q.pop();
new_number = new_number * 10 + digit;
}
cout << "In reverse order : " << new_number << endl;
return 0;
}

View File

@ -0,0 +1,45 @@
//
// AED --- 2023/2024
//
// J Madeira, Dec 2023
//
// Using a deque container to check if a string is a palindrome
//
#include <deque>
#include <iostream>
using namespace std;
int main(void) {
string original_string;
cout << "Write a character string without blank spaces : ";
cin >> original_string;
// Getting each char and pushing it into the back of a deque
deque<char> d;
for (char c : original_string) {
d.push_back(c);
}
// Is it a palindrome ?
// Read from both ends of the deque
bool answer = true;
while (answer && (d.size() > 1)) {
if (d.front() != d.back()) {
answer = false;
} else {
d.pop_front();
d.pop_back();
}
}
cout << "The string \"" << original_string << "\""
<< (answer ? " is " : " is NOT ") << "a palindrome !!" << endl;
return 0;
}

View File

@ -0,0 +1,114 @@
// J. Madeira --- February 2022
#include "Fraction.h"
#include <cassert>
#include <iostream>
#include <numeric>
Fraction::Fraction(void) : numerator_(0), denominator_(1) {}
Fraction::Fraction(int numerator, int denominator) {
assert(denominator > 0);
numerator_ = numerator;
denominator_ = denominator;
Reduce();
}
int Fraction::GetNumerator(void) const { return numerator_; }
void Fraction::SetNumerator(int n) {
numerator_ = n;
Reduce();
}
int Fraction ::GetDenominator(void) const { return denominator_; }
void Fraction::SetDenominator(int n) {
assert(n > 0);
denominator_ = n;
Reduce();
}
// Comparison operators
bool Fraction::operator==(const Fraction& frac) const {
return (numerator_ == frac.numerator_) && (denominator_ == frac.denominator_);
}
bool Fraction::operator!=(const Fraction& frac) const {
return !(*this == frac);
}
bool Fraction::operator<(const Fraction& frac) const {
return (numerator_ * frac.denominator_) < (frac.numerator_ * denominator_);
}
// Unary operator
Fraction Fraction::operator-(void) const {
Fraction res(-numerator_, denominator_);
return res;
}
// Binary operators
Fraction Fraction::operator+(const Fraction& frac) const {
Fraction res(*this);
if (res.denominator_ == frac.denominator_) {
res.numerator_ += frac.numerator_;
} else {
res.numerator_ =
res.numerator_ * frac.denominator_ + frac.numerator_ * res.denominator_;
res.denominator_ *= frac.denominator_;
}
res.Reduce();
return res;
}
Fraction Fraction::operator-(const Fraction& frac) const {
return *this + (-frac);
}
Fraction Fraction::operator*(const Fraction& frac) const {
Fraction res(*this);
res.numerator_ *= frac.numerator_;
res.denominator_ *= frac.denominator_;
res.Reduce();
return res;
}
Fraction Fraction::operator/(const Fraction& frac) const {
assert(frac.numerator_ != 0);
Fraction res(*this);
res.numerator_ *= frac.denominator_;
res.denominator_ *= frac.numerator_;
// Ensure the denominator is POSITIVE
if (res.denominator_ < 0) {
res.numerator_ *= -1;
res.denominator_ *= -1;
}
res.Reduce();
return res;
}
double Fraction::ToDouble(void) const {
return (double)numerator_ / (double)denominator_;
}
std::ostream& operator<<(std::ostream& os, const Fraction& frac) {
os << frac.numerator_ << " / " << frac.denominator_;
return os;
}
void Fraction::Reduce(void) {
int gcd = std::gcd(numerator_, denominator_); // Since C++17
if (gcd != 1) {
numerator_ /= gcd;
denominator_ /= gcd;
}
}

View File

@ -0,0 +1,44 @@
// J. Madeira --- March 2023
#ifndef FRACTION_H_
#define FRACTION_H_
#include <iostream>
class Fraction {
public:
Fraction(void);
Fraction(int numerator, int denominator = 1);
int GetNumerator(void) const;
void SetNumerator(int n);
int GetDenominator(void) const;
void SetDenominator(int n);
// Comparison operators
bool operator==(const Fraction& frac) const;
bool operator!=(const Fraction& frac) const;
bool operator<(const Fraction& frac) const;
// Unary operator
Fraction operator-(void) const;
// Binary operators
Fraction operator+(const Fraction& frac) const;
Fraction operator-(const Fraction& frac) const;
Fraction operator*(const Fraction& frac) const;
Fraction operator/(const Fraction& frac) const;
double ToDouble(void) const;
friend std::ostream& operator<<(std::ostream& out, const Fraction& frac);
private:
void Reduce(void);
int numerator_;
int denominator_; // ALWAYS POSITIVE !!!
};
#endif // FRACTION_H_

View File

@ -0,0 +1,4 @@
Compile with
g++ set_example.cpp Fraction.cpp -Wall -Wextra -std=c++17

View File

@ -0,0 +1,64 @@
//
// AED --- 2023/2024
//
// J Madeira, Dec 2023
//
// Using a set container to store Fraction objects and count repetitions
//
#include <cassert>
#include <cstdlib> // For basic random number generation (std::rand() and srand())
#include <ctime> // For std::time() (used to seed the pseudo-random number generator)
#include <iostream>
#include <set>
#include "Fraction.h"
using namespace std;
// Function to generate a random integer 0 to count-1
unsigned int random(size_t count) {
return static_cast<unsigned int>(rand() / (RAND_MAX / count + 1));
}
const unsigned int RANGE = 10;
int main(void) {
std::srand(
static_cast<unsigned int>(std::time(0))); // Seed random number generator
unsigned int n;
cout << "Number of fractions to create : ";
cin >> n;
cout << endl;
unsigned int repeated_values = 0;
set<Fraction> fractions_set;
for (unsigned int i = 0; i < n; ++i) {
Fraction frac = Fraction(1 + random(RANGE), 1 + random(RANGE));
auto result = fractions_set.insert(frac);
if (result.second == false) { // Tried to insert a repeated fraction value
++repeated_values;
}
}
// Checking
assert(n == (fractions_set.size() + repeated_values));
cout << "Number of created fractions : " << n << endl;
cout << "Number of repeated values : " << repeated_values << endl;
cout << "Number of different values : " << fractions_set.size() << endl;
unsigned int aux = 0;
for (const auto& f : fractions_set) {
cout << f << " --- ";
aux++;
if (aux % 6 == 0) {
cout << endl;
}
}
return 0;
}

View File

@ -0,0 +1,114 @@
// J. Madeira --- February 2022
#include "Fraction.h"
#include <cassert>
#include <iostream>
#include <numeric>
Fraction::Fraction(void) : numerator_(0), denominator_(1) {}
Fraction::Fraction(int numerator, int denominator) {
assert(denominator > 0);
numerator_ = numerator;
denominator_ = denominator;
Reduce();
}
int Fraction::GetNumerator(void) const { return numerator_; }
void Fraction::SetNumerator(int n) {
numerator_ = n;
Reduce();
}
int Fraction ::GetDenominator(void) const { return denominator_; }
void Fraction::SetDenominator(int n) {
assert(n > 0);
denominator_ = n;
Reduce();
}
// Comparison operators
bool Fraction::operator==(const Fraction& frac) const {
return (numerator_ == frac.numerator_) && (denominator_ == frac.denominator_);
}
bool Fraction::operator!=(const Fraction& frac) const {
return !(*this == frac);
}
bool Fraction::operator<(const Fraction& frac) const {
return (numerator_ * frac.denominator_) < (frac.numerator_ * denominator_);
}
// Unary operator
Fraction Fraction::operator-(void) const {
Fraction res(-numerator_, denominator_);
return res;
}
// Binary operators
Fraction Fraction::operator+(const Fraction& frac) const {
Fraction res(*this);
if (res.denominator_ == frac.denominator_) {
res.numerator_ += frac.numerator_;
} else {
res.numerator_ =
res.numerator_ * frac.denominator_ + frac.numerator_ * res.denominator_;
res.denominator_ *= frac.denominator_;
}
res.Reduce();
return res;
}
Fraction Fraction::operator-(const Fraction& frac) const {
return *this + (-frac);
}
Fraction Fraction::operator*(const Fraction& frac) const {
Fraction res(*this);
res.numerator_ *= frac.numerator_;
res.denominator_ *= frac.denominator_;
res.Reduce();
return res;
}
Fraction Fraction::operator/(const Fraction& frac) const {
assert(frac.numerator_ != 0);
Fraction res(*this);
res.numerator_ *= frac.denominator_;
res.denominator_ *= frac.numerator_;
// Ensure the denominator is POSITIVE
if (res.denominator_ < 0) {
res.numerator_ *= -1;
res.denominator_ *= -1;
}
res.Reduce();
return res;
}
double Fraction::ToDouble(void) const {
return (double)numerator_ / (double)denominator_;
}
std::ostream& operator<<(std::ostream& os, const Fraction& frac) {
os << frac.numerator_ << " / " << frac.denominator_;
return os;
}
void Fraction::Reduce(void) {
int gcd = std::gcd(numerator_, denominator_); // Since C++17
if (gcd != 1) {
numerator_ /= gcd;
denominator_ /= gcd;
}
}

View File

@ -0,0 +1,44 @@
// J. Madeira --- March 2023
#ifndef FRACTION_H_
#define FRACTION_H_
#include <iostream>
class Fraction {
public:
Fraction(void);
Fraction(int numerator, int denominator = 1);
int GetNumerator(void) const;
void SetNumerator(int n);
int GetDenominator(void) const;
void SetDenominator(int n);
// Comparison operators
bool operator==(const Fraction& frac) const;
bool operator!=(const Fraction& frac) const;
bool operator<(const Fraction& frac) const;
// Unary operator
Fraction operator-(void) const;
// Binary operators
Fraction operator+(const Fraction& frac) const;
Fraction operator-(const Fraction& frac) const;
Fraction operator*(const Fraction& frac) const;
Fraction operator/(const Fraction& frac) const;
double ToDouble(void) const;
friend std::ostream& operator<<(std::ostream& out, const Fraction& frac);
private:
void Reduce(void);
int numerator_;
int denominator_; // ALWAYS POSITIVE !!!
};
#endif // FRACTION_H_

View File

@ -0,0 +1,4 @@
Compile with
g++ map_example.cpp Fraction.cpp -Wall -Wextra -std=c++17

View File

@ -0,0 +1,64 @@
//
// AED --- 2023/2024
//
// J Madeira, Dec 2023
//
// Using a map container to count Fraction object (key) occurrences (value)
//
#include <cassert>
#include <cstdlib> // For basic random number generation (std::rand() and srand())
#include <ctime> // For std::time() (used to seed the pseudo-random number generator)
#include <iostream>
#include <map>
#include "Fraction.h"
using namespace std;
// Function to generate a random integer 0 to count-1
unsigned int random(size_t count) {
return static_cast<unsigned int>(rand() / (RAND_MAX / count + 1));
}
const unsigned int RANGE = 10;
int main(void) {
std::srand(
static_cast<unsigned int>(std::time(0))); // Seed random number generator
unsigned int n;
cout << "Number of fractions to create : ";
cin >> n;
cout << endl;
map<Fraction, unsigned int> fraction_counter;
for (unsigned int i = 0; i < n; ++i) {
Fraction frac = Fraction(1 + random(RANGE), 1 + random(RANGE));
++fraction_counter[frac]; // Great way of doing it !!
// If not in the map, insert with value 0
// and then update
}
// Checking
unsigned int total_count = 0;
for (const auto& item : fraction_counter) {
total_count += item.second;
}
assert(n == total_count);
cout << "Number of created fractions : " << n << endl;
cout << "Number of different values : " << fraction_counter.size() << endl;
unsigned int aux = 0;
for (const auto& item : fraction_counter) {
cout << item.first << " -> " << item.second << " ";
aux++;
if (aux % 6 == 0) {
cout << endl;
}
}
return 0;
}