[AED] TP topics
- tema08 - tema09 - tema10 - tema11 Signed-off-by: TiagoRG <tiago.rgarcia@ua.pt>
This commit is contained in:
parent
0f10829e21
commit
2f21d989b3
Binary file not shown.
|
@ -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)
|
||||||
|
|
Binary file not shown.
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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_
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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");
|
||||||
|
}
|
|
@ -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_
|
1
2ano/1semestre/aed/teoricas/tema09/19_AED_Dicionarios_I/02_Counting_Words/.gitignore
vendored
Normal file
1
2ano/1semestre/aed/teoricas/tema09/19_AED_Dicionarios_I/02_Counting_Words/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
*.txt
|
|
@ -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;
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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_
|
|
@ -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
|
Binary file not shown.
|
@ -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;
|
||||||
|
}
|
|
@ -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");
|
||||||
|
}
|
|
@ -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_
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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_
|
|
@ -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.
|
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
GRAFOS_EM_FICHEIRO
|
|
@ -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");
|
||||||
|
}
|
|
@ -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_
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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_
|
|
@ -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;
|
||||||
|
}
|
Binary file not shown.
|
@ -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");
|
||||||
|
}
|
|
@ -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_
|
|
@ -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 !!
|
||||||
|
}
|
|
@ -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_
|
|
@ -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)];
|
||||||
|
}
|
|
@ -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_
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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_
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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_
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
Compile with
|
||||||
|
|
||||||
|
g++ TestFraction.cpp Fraction.cpp -Wall -Wextra -std=c++17
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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_
|
|
@ -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;
|
||||||
|
}
|
|
@ -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_
|
|
@ -0,0 +1,3 @@
|
||||||
|
|
||||||
|
The value_ attibute of the base class is now protected -> Efficiency!
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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!!";
|
||||||
|
}
|
|
@ -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
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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_
|
|
@ -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
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
If needed, compile with
|
||||||
|
|
||||||
|
g++ -Wall -Wextra -std=c++17 main.cpp Fraction.cpp
|
|
@ -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;
|
||||||
|
}
|
Binary file not shown.
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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_
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
Compile with
|
||||||
|
|
||||||
|
g++ set_example.cpp Fraction.cpp -Wall -Wextra -std=c++17
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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_
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
Compile with
|
||||||
|
|
||||||
|
g++ map_example.cpp Fraction.cpp -Wall -Wextra -std=c++17
|
|
@ -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;
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue