Data Structures and Algorithms Manual

This page provides code and pseudocode for all of the main data structures and algorithms discussed in COMP2521.

Search Algorithms

Linear search

int linearSearch(int arr[], int size, int val) {
	for (int i = 0; i < size; i++) {
		if (arr[i] == val) {
			return i;
		}
	}
	return -1;
}

Binary search

int binarySearch(int arr[], int size, int val) {
	int lo = 0;
	int hi = size - 1;
	while (lo <= hi) {
		int mid = (lo + hi) / 2;
		if (val < arr[mid]) {
			hi = mid - 1;
		} else if (val > arr[mid]) {
			lo = mid + 1;
		} else {
			return mid;
		}
	}
	return -1;
}

Sorting Algorithms

Macros

The code below uses these macros to compare items.

Note that these macros assume that the arrays contain integers - they would be defined differently when sorting other types of items.

#define lt(x, y) (x <  y) // less than
#define le(x, y) (x <= y) // less than or equal to
#define ge(x, y) (x >= y) // greater than or equal to
#define gt(x, y) (x >  y) // greater than

Selection sort

void selectionSort(Item items[], int lo, int hi) {
	for (int i = lo; i < hi; i++) {
		int min = i;
		for (int j = i + 1; j <= hi; j++) {
			if (lt(items[j], items[min])) {
				min = j;
			}
		}
		swap(items, i, min);
	}
}

Bubble sort

void bubbleSort(Item items[], int lo, int hi) {
	for (int i = hi; i > lo; i--) {
		bool swapped = false;
		for (int j = lo; j < i; j++) {
			if (gt(items[j], items[j + 1])) {
				swap(items, j, j + 1);
				swapped = true;
			}
		}
		if (!swapped) break;
	}
}

Insertion sort

void insertionSort(Item items[], int lo, int hi) {
	for (int i = lo + 1; i <= hi; i++) {
		Item item = items[i];
		int j = i;
		for (; j > lo && lt(item, items[j - 1]); j--) {
			items[j] = items[j - 1];
		}
		items[j] = item;
	}
}

Shell sort

void shellSort(Item items[], int lo, int hi) {
	int size = hi - lo + 1;
	int h;
	for (h = 1; h <= (size - 1) / 9; h = (3 * h) + 1);

	for (; h > 0; h /= 3) {
		for (int i = lo + h; i <= hi; i++) {
			Item item = items[i];
			int j = i;
			for (; j >= lo + h && lt(item, items[j - h]); j -= h) {
				items[j] = items[j - h];
			}
			items[j] = item;
		}
	}
}

Merge sort

void mergeSort(Item items[], int lo, int hi) {
	if (lo >= hi) return;
	int mid = (lo + hi) / 2;
	mergeSort(items, lo, mid);
	mergeSort(items, mid + 1, hi);
	merge(items, lo, mid, hi);
}

void merge(Item items[], int lo, int mid, int hi) {
	Item *tmp = malloc((hi - lo + 1) * sizeof(Item));

	int i = lo;
	int j = mid + 1;
	int k = 0;

	while (i <= mid && j <= hi) {
		if (le(items[i], items[j])) {
			tmp[k++] = items[i++];
		} else {
			tmp[k++] = items[j++];
		}
	}

	while (i <= mid) tmp[k++] = items[i++];
	while (j <= hi) tmp[k++] = items[j++];

	for (i = lo, k = 0; i <= hi; i++, k++) {
		items[i] = tmp[k];
	}

	free(tmp);
}

Quick sort

Naive quick sort
void naiveQuickSort(Item items[], int lo, int hi) {
	if (lo >= hi) return;
	int pivotIndex = partition(items, lo, hi);
	naiveQuickSort(items, lo, pivotIndex - 1);
	naiveQuickSort(items, pivotIndex + 1, hi);
}

int partition(Item items[], int lo, int hi) {
	Item pivot = items[lo];

	int l = lo + 1;
	int r = hi;
	while (true) {
		while (l < r && le(items[l], pivot)) l++;
		while (l < r && ge(items[r], pivot)) r--;
		if (l == r) break;
		swap(items, l, r);
	}

	if (lt(pivot, items[l])) l--;
	swap(items, lo, l);
	return l;
}
Median-of-three quick sort
void medianOfThreeQuickSort(Item items[], int lo, int hi) {
	if (lo >= hi) return;
	medianOfThree(items, lo, hi);
	int pivotIndex = partition(items, lo, hi);
	medianOfThreeQuickSort(items, lo, pivotIndex - 1);
	medianOfThreeQuickSort(items, pivotIndex + 1, hi);
}

void medianOfThree(Item a[], int lo, int hi) {
	int mid = (lo + hi) / 2;
	if (gt(a[mid], a[lo])) swap(a, mid, lo);
	if (gt(a[lo], a[hi])) swap(a, lo, hi);
	if (gt(a[mid], a[lo])) swap(a, mid, lo);
}

Radix sort

radixSort(A):
    Input: array A of keys
           where each key consists of m symbols
           from an "alphabet" of size R

    initialise R buckets // one for each symbol

    for i from m down to 1:
        empty all buckets
        for each key in A:
            append key to bucket key[i]

        clear A
        for each bucket (in order):
            for each key in bucket:
                append key to A

Abstract Data Types

Stack ADT

Interface
typedef struct stack *Stack;

typedef int Item;

/**
 * Creates a new empty stack
 */
Stack StackNew(void);

/**
 * Frees all memory allocated to the stack
 */
void StackFree(Stack s);

/**
 * Adds an item to the top of the stack
 */
void StackPush(Stack s, Item item);

/**
 * Removes an item from the top of the stack and returns it
 * Assumes that the stack is not empty
 */
Item StackPop(Stack s);

/**
 * Returns the number of items in the stack
 */
int StackSize(Stack s);

/**
 * Returns the item at the top of the stack without removing it
 * Assumes that the stack is not empty
 */
Item StackPeek(Stack s);
Implementation
struct stack {
	struct node *head;
	int size;
};

struct node {
	Item item;
	struct node *next;
};

Stack StackNew(void) {
	Stack s = malloc(sizeof(struct stack));
	if (s == NULL) {
		fprintf(stderr, "error: out of memory\n");
		exit(EXIT_FAILURE);
	}

	s->head = NULL;
	s->size = 0;
	return s;
}

void StackFree(Stack s) {
	struct node *curr = s->head;
	while (curr != NULL) {
		struct node *temp = curr;
		curr = curr->next;
		free(temp);
	}
	free(s);
}

void StackPush(Stack s, Item item) {
	struct node *new = malloc(sizeof(struct node));
	if (new == NULL) {
		fprintf(stderr, "error: out of memory\n");
		exit(EXIT_FAILURE);
	}

	new->item = item;
	new->next = s->head;

	s->head = new;
	s->size++;
}

Item StackPop(Stack s) {
	if (s->size == 0) {
		fprintf(stderr, "error: stack is empty!\n");
		return 0;
	}

	struct node *newHead = s->head->next;
	Item it = s->head->item;

	free(s->head);
	s->head = newHead;
	s->size--;

	return it;
}

int StackSize(Stack s) {
	return s->size;
}

Item StackPeek(Stack s) {
	if (s->size == 0) {
		fprintf(stderr, "error: stack is empty!\n");
		return 0;
	}

	return s->head->item;
}

Queue ADT

Interface
typedef int Item;

typedef struct queue *Queue;

/**
 * Creates a new empty queue
 */
Queue QueueNew(void);

/**
 * Frees all memory allocated to the queue
 */
void QueueFree(Queue q);

/**
 * Adds an item to the end of the queue
 */
void QueueEnqueue(Queue q, Item it);

/**
 * Removes an item from the front of the queue and returns it
 * Assumes that the queue is not empty
 */
Item QueueDequeue(Queue q);

/**
 * Returns the number of items in the queue
 */
int QueueSize(Queue q);

/**
 * Returns the item at the front of the queue without removing it
 * Assumes that the queue is not empty
 */
Item QueuePeek(Queue q);
Implementation
struct queue {
	struct node *head;
	struct node *tail;
	int size;
};

struct node {
	Item item;
	struct node *next;
};

static struct node *newNode(Item it);

Queue QueueNew(void) {
	Queue q = malloc(sizeof(struct queue));
	if (q == NULL) {
		fprintf(stderr, "error: out of memory\n");
		exit(EXIT_FAILURE);
	}

	q->head = NULL;
	q->tail = NULL;
	q->size = 0;
	return q;
}

void QueueFree(Queue q) {
	struct node *curr = q->head;
	while (curr != NULL) {
		struct node *temp = curr;
		curr = curr->next;
		free(temp);
	}
	free(q);
}

void QueueEnqueue(Queue q, Item it) {
	struct node *n = newNode(it);
	if (q->size == 0) {
		q->head = n;
	} else {
		q->tail->next = n;
	}
	q->tail = n;
	q->size++;
}

static struct node *newNode(Item it) {
	struct node *n = malloc(sizeof(*n));
	if (n == NULL) {
		fprintf(stderr, "error: out of memory\n");
		exit(EXIT_FAILURE);
	}

	n->item = it;
	n->next = NULL;
	return n;
}

Item QueueDequeue(Queue q) {
	if (q->size == 0) {
		fprintf(stderr, "error: queue is empty!\n");
		return 0;
	}

	struct node *newHead = q->head->next;
	Item it = q->head->item;

	free(q->head);
	q->head = newHead;
	if (newHead == NULL) {
		q->tail = NULL;
	}
	q->size--;

	return it;
}

int QueueSize(Queue q) {
	return q->size;
}

Item QueuePeek(Queue q) {
	if (q->size == 0) {
		fprintf(stderr, "error: queue is empty!\n");
		return 0;
	}

	return q->head->item;
}

Binary Search Trees

struct node {
	int value;
	struct node *left;
	struct node *right;
};

/**
 * Returns an empty BST
 */
struct node *bstNew(void) {
	return NULL;
}

/**
 * Frees all memory allocated to the BST
 */
void bstFree(struct node *tree) {
	if (tree == NULL) {
		return;
	}

	bstFree(tree->left);
	bstFree(tree->right);
	free(tree);
}

/**
 * Inserts a value into the BST and returns the root of the udpated BST
 */
struct node *bstInsert(struct node *tree, int val) {
	if (tree == NULL) {
		return newNode(val);
	} else if (val < tree->value) {
		tree->left = bstInsert(tree->left, val);
	} else if (val > tree->value) {
		tree->right = bstInsert(tree->right, val);
	}
	return tree;
}

static struct node *newNode(int val) {
	struct node *new = malloc(sizeof(struct node));
	if (new == NULL) {
		fprintf(stderr, "error: out of memory\n");
		exit(EXIT_FAILURE);
	}

	new->value = val;
	new->left = NULL;
	new->right = NULL;
	return new;
}

/**
 * Returns true if the given value is in the BST, and false otherwise
 */
bool bstSearch(struct node *tree, int val) {
	if (tree == NULL) {
		return false;
	} else if (val < tree->value) {
		return bstSearch(tree->left, val);
	} else if (val > tree->value) {
		return bstSearch(tree->right, val);
	} else { // (val == tree->value)
		return true;
	}
}

/**
 * Deletes a value from the BST and returns the root of the updated BST
 */
struct node *bstDelete(struct node *tree, int val) {
	if (tree == NULL) {
		return tree;
	} else if (val < tree->value) {
		tree->left = bstDelete(tree->left, val);
	} else if (val > tree->value) {
		tree->right = bstDelete(tree->right, val);
	} else {
		struct node *new;
		if (tree->left == NULL) {
			new = tree->right;
		} else if (tree->right == NULL) {
			new = tree->left;
		} else {
			new = bstJoin(tree->left, tree->right);
		}
		
		free(tree);
		tree = new;
	}

	return tree;
}

static struct node *bstJoin(struct node *t1, struct node *t2) {
	if (t1 == NULL) {
		return t2;
	}
	if (t2 == NULL) {
		return t1;
	}

	struct node *curr = t2;
	struct node *parent = NULL;
	while (curr->left != NULL) {
		parent = curr;
		curr = curr->left;
	}

	if (parent != NULL) {
		parent->left = curr->right;
		curr->right = t2;
	}

	curr->left = t1;
	return curr;
}

/**
 * Prints the pre-order traversal of the BST
 */
void bstPreOrder(struct node *tree) {
	if (tree == NULL) return;

	showBstNode(tree);
	bstPreOrder(tree->left);
	bstPreOrder(tree->right);
}

/**
 * Prints the in-order traversal of the BST
 */
void bstInOrder(struct node *tree) {
	if (tree == NULL) return;

	bstInOrder(tree->left);
	showBstNode(tree);
	bstInOrder(tree->right);
}

/**
 * Prints the post-order traversal of the BST
 */
void bstPostOrder(struct node *tree) {
	if (tree == NULL) return;

	bstPostOrder(tree->left);
	bstPostOrder(tree->right);
	showBstNode(tree);
}

static void showBstNode(struct node *tree) {
	if (tree == NULL) return;
	printf("%d ", tree->value);
}

AVL Trees

avlInsert(t, v):
	Input:  AVL tree t, item v
	Output: t with v inserted

	if t is empty:
		return new node containing v
	else if v < t->item:
		t->left = avlInsert(t->left, v)
	else if v > t->item:
		t->right = avlInsert(t->right, v)
	else:
		return t

	return avlRebalance(t)

avlRebalance(t):
	Input:  possibly unbalanced tree t
	Output: balanced t

	bal = balance(t)
	if bal > 1:
		if balance(t->left) < 0:
			t->left = rotateLeft(t->left)
		t = rotateRight(t)
	else if bal < -1:
		if balance(t->right) > 0:
			t->right = rotateRight(t->right)
		t = rotateLeft(t)

	return t

balance(t):
	Input:  tree t
	Output: balance factor of t

	return height(t->left) - height(t->right)

avlDelete(t, v):
	Input:  AVL tree t, item v
	Output: t with v deleted

	if t is empty:
		return empty tree
	else if v < t->item:
		t->left = avlDelete(t->left, v)
	else if v > t->item:
		t->right = avlDelete(t->right, v)
	else:
		if t->left is empty:
			temp = t->right
			free(t)
			return temp
		else if t->right is empty:
			temp = t->left
			free(t)
			return temp
		else:
			successor = minimum value in t->right
			t->item = successor
			t->right = avlDelete(t->right, successor)

	return avlRebalance(t)

Graphs

Interface

typedef struct graph *Graph;

typedef int Vertex;

/**
 * Returns a new graph with nV vertices
 */
Graph GraphNew(int nV);

/**
 * Frees all memory allocated to the graph
 */
void GraphFree(Graph g);

/**
 * Returns the number of vertices in the graph
 */
int GraphNumVertices(Graph g);

/**
 * Returns the number of edges in the graph
 */
int GraphNumEdges(Graph g);

/**
 * Returns true if there is an edge between v and w,
 * and false otherwise
 */
bool GraphIsAdjacent(Graph g, Vertex v, Vertex w);

/**
 * Adds an edge between v and w
 */
void GraphInsertEdge(Graph g, Vertex v, Vertex w);

/**
 * Removes an edge between v and w
 */
void GraphRemoveEdge(Graph g, Vertex v, Vertex w);

/**
 * Displays the graph
 */
void GraphShow(Graph g);

Adjacency Matrix Implementation

struct graph {
	int nV;
	int nE;
	bool **edges;
};

static bool validVertex(Graph g, Vertex v);

Graph GraphNew(int nV) {
	Graph g = malloc(sizeof(struct graph));
	if (g == NULL) {
		fprintf(stderr, "error: out of memory\n");
		exit(EXIT_FAILURE);
	}

	g->nV = nV;
	g->nE = 0;

	g->edges = malloc(nV * sizeof(bool *));
	for (int i = 0; i < g->nV; i++) {
		g->edges[i] = calloc(nV, sizeof(bool));
	}

	return g;
}

void GraphFree(Graph g) {
	for (int i = 0; i < g->nV; i++) {
		free(g->edges[i]);
	}
	free(g->edges);
	free(g);
}

int GraphNumVertices(Graph g) {
	return g->nV;
}

int GraphNumEdges(Graph g) {
	return g->nE;
}

bool GraphIsAdjacent(Graph g, Vertex v, Vertex w) {
	assert(validVertex(g, v));
	assert(validVertex(g, w));

	return g->edges[v][w];
}

void GraphInsertEdge(Graph g, Vertex v, Vertex w) {
	assert(validVertex(g, v));
	assert(validVertex(g, w));

	if (g->edges[v][w]) {
		return;
	}
	
	g->edges[v][w] = true;
	g->edges[w][v] = true;
	g->nE++;
}

void GraphRemoveEdge(Graph g, Vertex v, Vertex w) {
	assert(validVertex(g, v));
	assert(validVertex(g, w));

	if (!g->edges[v][w]) {
		return;
	}

	g->edges[v][w] = false;
	g->edges[w][v] = false;
	g->nE--;
}

void GraphShow(Graph g) {
	printf("Number of vertices: %d\n", g->nV);
	printf("Number of edges: %d\n", g->nE);
	printf("Edges:\n");
	for (int i = 0; i < g->nV; i++) {
		printf("%2d:", i);
		for (int j = 0; j < g->nV; j++) {
			if (g->edges[i][j]) {
				printf(" %d", j);
			}
		}
		printf("\n");
	}
	printf("\n");
}

static bool validVertex(Graph g, Vertex v) {
	return (v >= 0 && v < g->nV);
}

Adjacency List Implementation

struct graph {
	int nV;
	int nE;
	struct adjNode **edges;
};

struct adjNode {
	Vertex v;
	struct adjNode *next;
};

struct adjNode *adjListInsert(struct adjNode *l, int v);
struct adjNode *adjListDelete(struct adjNode *l, int v);
static bool validVertex(Graph g, Vertex v);

Graph GraphNew(int nV) {
	Graph g = malloc(sizeof(struct graph));
	g->edges = calloc(nV, sizeof(struct adjNode *));
	g->nV = nV;
	g->nE = 0;
	return g;
}

void GraphFree(Graph g) {
	for (int i = 0; i < g->nV; i++) {
		struct adjNode *curr = g->edges[i];
		while (curr != NULL) {
			struct adjNode *temp = curr;
			curr = curr->next;
			free(temp);
		}
	}
	free(g->edges);
	free(g);
}

int GraphNumVertices(Graph g) {
	return g->nV;
}

int GraphNumEdges(Graph g) {
	return g->nE;
}

bool GraphIsAdjacent(Graph g, Vertex v, Vertex w) {
	assert(validVertex(g, v));
	assert(validVertex(g, w));

	struct adjNode *curr = g->edges[v];
	for (; curr != NULL && w >= curr->v; curr = curr->next) {
		if (curr->v == w) {
			return true;
		}
	}
	return false;
}

void GraphInsertEdge(Graph g, Vertex v, Vertex w) {
	assert(validVertex(g, v));
	assert(validVertex(g, w));

	if (!GraphIsAdjacent(g, v, w)) {
		g->edges[v] = adjListInsert(g->edges[v], w);
		g->edges[w] = adjListInsert(g->edges[w], v);
		g->nE++;
	}
}

static struct adjNode *adjListInsert(struct adjNode *l, int v) {
    if (l == NULL || v < l->v) {
        struct adjNode *new = newAdjNode(v);
        new->next = l;
        return new;
    } else if (v == l->v) {
        return l;
    } else {
        l->next = adjListInsert(l->next, v);
        return l;
    }
}

static struct adjNode *newAdjNode(int v) {
    struct adjNode *n = malloc(sizeof(*n));
    if (n == NULL) {
        fprintf(stderr, "error: out of memory\n");
        exit(EXIT_FAILURE);
    }
    n->v = v;
    n->next = NULL;
    return n;
}

void GraphRemoveEdge(Graph g, Vertex v, Vertex w) {
	assert(validVertex(g, v));
	assert(validVertex(g, w));

	if (GraphIsAdjacent(g, v, w)) {
		g->edges[v] = adjListDelete(g->edges[v], w);
		g->edges[w] = adjListDelete(g->edges[w], v);
		g->nE++;
	}
}

static struct adjNode *adjListDelete(struct adjNode *l, int v) {
    if (l == NULL || v < l->v) {
        return l;
    } else if (v == l->v) {
        struct adjNode *newHead = l->next;
        free(l);
        return newHead;
    } else {
        l->next = adjListDelete(l->next, v);
        return l;
    }
}

void GraphShow(Graph g) {
	printf("Number of vertices: %d\n", g->nV);
	printf("Number of edges: %d\n", g->nE);
	printf("Edges:\n");
	for (int i = 0; i < g->nV; i++) {
		printf("%2d:", i);
		for (struct adjNode *curr = g->edges[i]; curr != NULL; curr = curr->next) {
			printf(" %d", curr->v);
		}
		printf("\n");
	}
	printf("\n");
}

static bool validVertex(Graph g, Vertex v) {
	return (v >= 0 && v < g->nV);
}

Array of Edges Implementation

#define DEFAULT_CAPACITY 16

struct graph {
	int nV;
	int nE;
	struct edge *edges;
	int maxE;
};

struct edge {
	Vertex v;
	Vertex w;
};

static bool validVertex(Graph g, Vertex v);

Graph GraphNew(int nV) {
	Graph g = malloc(sizeof(struct graph));
	g->nV = nV;
	g->nE = 0;

	g->edges = malloc(DEFAULT_CAPACITY * sizeof(struct edge));
	g->maxE = DEFAULT_CAPACITY;
	return g;
}

void GraphFree(Graph g) {
	free(g->edges);
	free(g);
}

int GraphNumVertices(Graph g) {
	return g->nV;
}

int GraphNumEdges(Graph g) {
	return g->nE;
}

bool GraphIsAdjacent(Graph g, Vertex v, Vertex w) {
	assert(validVertex(g, v));
	assert(validVertex(g, w));

	for (int i = 0; i < g->nE; i++) {
		if ((g->edges[i].v == v && g->edges[i].w == w) ||
				(g->edges[i].v == w && g->edges[i].w == v)) {
			return true;
		}
	}
	return false;
}

void GraphInsertEdge(Graph g, Vertex v, Vertex w) {
	assert(validVertex(g, v));
	assert(validVertex(g, w));

	if (!GraphIsAdjacent(g, v, w)) {
		if (g->nE == g->maxE) {
			g->maxE *= 2;
			g->edges = realloc(g->edges, g->maxE * sizeof(struct edge));
		}
		g->edges[g->nE] = (struct edge){v, w};
		g->nE++;
	}
}

void GraphRemoveEdge(Graph g, Vertex v, Vertex w) {
	assert(validVertex(g, v));
	assert(validVertex(g, w));

	for (int i = 0; i < g->nE; i++) {
		if ((g->edges[i].v == v && g->edges[i].w == w) ||
				(g->edges[i].v == w && g->edges[i].w == v)) {
			g->edges[i] = g->edges[g->nE - 1];
			g->nE--;
			return;
		}
	}
}

void GraphShow(Graph g) {
	printf("Number of vertices: %d\n", g->nV);
	printf("Number of edges: %d\n", g->nE);
	printf("Edges:\n");
	for (int i = 0; i < g->nE; i++) {
		printf("(%d, %d)\n", g->edges[i].v, g->edges[i].w);
	}
	printf("\n");
}

static bool validVertex(Graph g, Vertex v) {
	return (v >= 0 && v < g->nV);
}

Breadth-First Search (BFS)

void bfs(Graph g, Vertex src) {
	int *predecessor = malloc(g->nV * sizeof(int));
	Queue q = QueueNew();

	for (int i = 0; i < g->nV; i++) {
		predecessor[i] = -1;
	}

	predecessor[src] = src;
	QueueEnqueue(q, src);

	while (QueueSize(q) > 0) {
		Vertex v = QueueDequeue(q);

		struct adjNode *curr = g->edges[v];
		for (; curr != NULL; curr = curr->next) {
			Vertex w = curr->v;

			if (predecessor[w] == -1) {
				predecessor[w] = v;
				QueueEnqueue(q, w);
			}
		}
	}

	free(predecessor);
	QueueFree(q);
}

Depth-First Search (DFS)

void dfs(Graph g, Vertex src) {
	bool *visited = calloc(g->nV, sizeof(bool));
	dfsRec(g, src, visited);
	free(visited);
}

static void dfsRec(Graph g, Vertex v, bool *visited) {
	visited[v] = true;

	struct adjNode *curr = g->edges[v];
	for (; curr != NULL; curr = curr->next) {
		Vertex w = curr->v;
		if (!visited[w]) {
			dfsRec(g, w, visited);
		}
	}
}

Warshall's Algorithm

warshall(A):
	Input: n x n adjacency matrix A
	Output: n x n reachability matrix

	create tc matrix which is a copy of A

	for each vertex k in G: // from 0 to n - 1
		for each vertex s in G:
			for each vertex t in G:
				if tc[s][k] and tc[k][t]:
					tc[s][t] = true

	return tc

Dijkstra's Algorithm

dijkstraSSSP(G, src):
	Input: graph G, source vertex src

	create dist array, initialised to inf
	create pred array, initialised to -1
	create vSet containing all vertices of G

	dist[src] = 0
	while vSet is not empty:
		find vertex v in vSet such that dist[v] is minimal
		remove v from vSet
		for each edge (v, w, weight) in G:
			if dist[v] + weight < dist[w]:
				dist[w] = dist[v] + weight
				pred[w] = v

Kruskal's Algorithm

kruskalMst(G):
	Input:  graph G with V vertices
	Output: minimum spanning tree of G

	mst = empty graph with V vertices
	sortedEdges = sort edges of G by weight
	for each edge (v, w, weight) in sortedEdges:
		if there is no path between v and w in mst:
			add edge (v, w, weight) to mst
		if mst has V - 1 edges:
			return mst

	no mst

Prim's Algorithm

primMst(G):
	Input:  graph G with V vertices
	Output: minimum spanning tree of G

	mst = empty graph with V vertices
	usedV = {0}
	unusedE = edges of G
	while |usedV| < V :
		find cheapest edge e (s, t, weight) in unusedE such that
				s in usedV and t not in usedV

		add e to mst
		add t to usedV
		remove e from unusedE

	return mst

Hash Tables

Interface

typedef struct hashTable *HashTable;

/**
 * Creates a new empty hash table
 */
HashTable HashTableNew(void);

/**
 * Frees all memory allocated to the hash table
 */
void HashTableFree(HashTable ht);

/**
 * Inserts a key-value pair into the hash table. If the key already
 * exists, the existing value is replaced with the given value.
 */
void HashTableInsert(HashTable ht, int key, int value);

/**
 * Deletes a key-value pair from the hash table, if it exists
 */
void HashTableDelete(HashTable ht, int key);

/**
 * Returns true if the hash table contains the given key, and false
 * otherwise
 */
bool HashTableContains(HashTable ht, int key);

/**
 * Returns the value associated with the given key in the hash table.
 * Assumes that the key exists.
 */
int HashTableGet(HashTable ht, int key);

/**
 * Returns the number of key-value pairs in the hash table
 */
int HashTableSize(HashTable ht);

Separate Chaining Implementation

#define INITIAL_NUM_SLOTS 11

#define MAX_LOAD_FACTOR 2.0

struct hashTable {
	struct node **slots;
	int numSlots;
	int numItems;
};

struct node {
	int key;
	int value;
	struct node *next;
};

static void freeList(struct node *list);
static struct node *doInsert(HashTable ht, struct node *list,
                             int key, int value);
static struct node *newNode(int key, int value);
static struct node *doDelete(HashTable ht, struct node *list, int key);
static inline unsigned int hash(int key, int N);

HashTable HashTableNew(void) {
	HashTable ht = malloc(sizeof(*ht));
	if (ht == NULL) {
		fprintf(stderr, "error: out of memory\n");
		exit(EXIT_FAILURE);
	}

	ht->slots = calloc(INITIAL_NUM_SLOTS, sizeof(struct node *));
	ht->numSlots = INITIAL_NUM_SLOTS;
	ht->numItems = 0;

	return ht;
}

void HashTableFree(HashTable ht) {
	for (int i = 0; i < ht->numSlots; i++) {
		freeList(ht->slots[i]);
	}
	free(ht->slots);
	free(ht);
}

static void freeList(struct node *list) {
	struct node *curr = list;
	while (curr != NULL) {
		struct node *temp = curr;
		curr = curr->next;
		free(temp);
	}
}

void HashTableInsert(HashTable ht, int key, int value) {
	if (ht->numItems >= MAX_LOAD_FACTOR * ht->numSlots) {
		printf("warning: resize not implemented!\n");
	}

	int i = hash(key, ht->numSlots);
	ht->slots[i] = doInsert(ht, ht->slots[i], key, value);
}

static struct node *doInsert(HashTable ht, struct node *list,
                             int key, int value) {
	if (list == NULL) {
		ht->numItems++;
		return newNode(key, value);
	} else if (list->key == key) {
		list->value = value;
		return list;
	} else {
		list->next = doInsert(ht, list->next, key, value);
		return list;
	}
}

static struct node *newNode(int key, int value) {
	struct node *new = malloc(sizeof(struct node));
	if (new == NULL) {
		fprintf(stderr, "error: out of memory\n");
		exit(EXIT_FAILURE);
	}

	new->key = key;
	new->value = value;
	new->next = NULL;
	return new;
}

void HashTableDelete(HashTable ht, int key) {
	int i = hash(key, ht->numSlots);
	ht->slots[i] = doDelete(ht, ht->slots[i], key);
}

static struct node *doDelete(HashTable ht, struct node *list, int key) {
	if (list == NULL) {
		return NULL;
	} else if (list->key == key) {
		ht->numItems--;
		struct node *newHead = list->next;
		free(list);
		return newHead;
	} else {
		list->next = doDelete(ht, list->next, key);
		return list;
	}
}

bool HashTableContains(HashTable ht, int key) {
	int i = hash(key, ht->numSlots);
	struct node *curr;
	for (curr = ht->slots[i]; curr != NULL; curr = curr->next) {
		if (curr->key == key) {
			return true;
		}
	}
	return false;
}

int HashTableGet(HashTable ht, int key) {
	int i = hash(key, ht->numSlots);
	struct node *curr;
	for (curr = ht->slots[i]; curr != NULL; curr = curr->next) {
		if (curr->key == key) {
			return curr->value;
		}
	}
	
	printf("error: key %d does not exist!\n", key);
	return -1;
}

int HashTableSize(HashTable ht) {
	return ht->numItems;
}

static inline unsigned int hash(int key, int N) {
	const unsigned int magic = 0x45d9f3b;
	unsigned int h = 1U + INT_MAX;
	h += key;
	h = ((h >> 16) ^ h) * magic;
	h = ((h >> 16) ^ h) * magic;
	h = (h >> 16) ^ h;
	return h % N;
}

Priority Queues

Interface

typedef struct pq *Pq;

/**
 * Creates a new empty priority queue
 */
Pq PqNew(void);

/**
 * Frees all memory allocated to the priority queue
 */
void PqFree(Pq pq);

/**
 * Inserts an item with the given priority into the priority queue
 */
void PqInsert(Pq pq, int item, int priority);

/**
 * Deletes the item with the highest priority from the priority queue
 * and returns it
 */
int PqDelete(Pq pq);

/**
 * Returns the item with the highest priority from the priority queue
 */
int PqPeek(Pq pq);

Binary Heap Implementation

#define INITIAL_CAPACITY 8

struct pq {
	struct pqItem *items;
	int numItems;
	int capacity;
};

struct pqItem {
	int item;
	int priority;
};

static void resize(Pq pq);
static void fixUp(struct pqItem *items, int i);
static void swap(struct pqItem *items, int i, int j);
static void fixDown(struct pqItem *items, int i, int N);

Pq PqNew(void) {
	Pq pq = malloc(sizeof(struct pq));
	if (pq == NULL) {
		fprintf(stderr, "error: out of memory\n");
		exit(EXIT_FAILURE);
	}

	pq->numItems = 0;
	pq->capacity = INITIAL_CAPACITY;
	pq->items = malloc((pq->capacity + 1) * sizeof(struct pqItem));

	return pq;
}

void PqFree(Pq pq) {
	free(pq->items);
	free(pq);
}

void PqInsert(Pq pq, int item, int priority) {
	if (pq->numItems == pq->capacity) {
		resize(pq);
	}

	pq->numItems++;
	pq->items[pq->numItems] = (struct pqItem){
		.item = item,
		.priority = priority,
	};
	fixUp(pq->items, pq->numItems);
}

static void fixUp(struct pqItem *items, int i) {
	while (i > 1 && items[i].priority > items[i / 2].priority) {
		swap(items, i, i / 2);
		i = i / 2;
	}
}

static void swap(struct pqItem *items, int i, int j) {
	struct pqItem tmp = items[i];
	items[i] = items[j];
	items[j] = tmp;
}

static void resize(Pq pq) {
	pq->capacity *= 2;
	pq->items = realloc(pq->items, (pq->capacity + 1) * sizeof(struct pqItem));
	if (pq->items == NULL) {
		fprintf(stderr, "error: out of memory\n");
		exit(EXIT_FAILURE);
	}
}

int PqDelete(Pq pq) {
	if (pq->numItems == 0) {
		fprintf(stderr, "error: pq is empty\n");
		exit(EXIT_FAILURE);
	}

	int item = pq->items[1].item;
	pq->items[1] = pq->items[pq->numItems];
	pq->numItems--;
	fixDown(pq->items, 1, pq->numItems);
	return item;
}

static void fixDown(struct pqItem *items, int i, int N) {
	while (2 * i <= N) {
		int j = 2 * i;
		if (j < N && items[j + 1].priority > items[j].priority) {
			j++;
		}
		if (items[i].priority >= items[j].priority) {
			break;
		}
		swap(items, i, j);
		i = j;
	}
}

int PqPeek(Pq pq) {
	if (pq->numItems == 0) {
		fprintf(stderr, "error: pq is empty\n");
		exit(EXIT_FAILURE);
	}

	return pq->items[1];
}

Tries

#define ALPHABET_SIZE 26

typedef struct node *Trie;

struct node {
	struct node *children[ALPHABET_SIZE];
	bool finish;
};

/**
 * Creates a new empty trie
 */
Trie trieNew(void) {
	Trie t = calloc(1, sizeof(*t));
	if (t == NULL) {
		fprintf(stderr, "error: out of memory\n");
		exit(EXIT_FAILURE);
	}

	return t;
}

/**
 * Frees all memory allocated to the trie
 */
void trieFree(Trie t) {
	if (t == NULL) {
		return;
	}

	for (int i = 0; i < ALPHABET_SIZE; i++) {
		trieFree(t->children[i]);
	}
	free(t);
}

/**
 * Inserts a key into the trie
 * Returns the trie
 */
Trie trieInsert(Trie t, char *key) {
	if (t == NULL) {
		t = calloc(1, sizeof(*t));
	}

	if (key[0] == '\0') {
		t->finish = true;
		return t;
	}

	int i = key[0] - 'a';
	t->children[i] = trieInsert(t->children[i], &key[1]);
	return t;
}

/**
 * Returns true if the given key is in the trie, and false otherwise
 */
bool trieSearch(Trie t, char *key) {
	if (t == NULL) {
		return false;
	}

	if (key[0] == '\0') {
		return t->finish;
	}

	char first = key[0];
	int i = first - 'a';
	return trieSearch(t->children[i], &key[1]);
}

/**
 * Deletes a key from the trie if it exists
 * Returns the trie
 */
Trie trieDelete(Trie t, char *key) {
	if (t == NULL) {
		return t;
	} else if (key[0] == '\0') {
		t->finish = false;
	} else {
		int i = key[0] - 'a';
		t->children[i] = trieDelete(t->children[i], &key[1]);
	}

	if (t->finish) {
		return t;
	}

	for (int i = 0; i < ALPHABET_SIZE; i++) {
		if (t->children[i] != NULL) {
			return t;
		}
	}

	free(t);
	return NULL;
}