[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 |
|
||||
| [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 |
|
||||
| [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)
|
||||
|
|
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