Time Complexity

The amount of steps required for an algorithm to execute.

Big O Notation

Maximum time for n input size. (Upper bound - worst case)
Big-O Complexity Chart

Omega Notation

Minimum time for n input size. (Lower bound)

  • Ω(n²)
  • Ω(n log n)
  • Ω(n)
  • Ω(log n)
  • Ω(1)

If both are the same, use θ

Searching Algorithms

AlgorithmAverage Time ComplexityRAM
Linear SearchO(n) | Ω(1)0
Binary SearchO(log n) | Ω(1)0

Check every element until n is found.

#include <stdio.h>
int main(void) {
	int n = 1; // number to find
	int numbers[] = {20, 500, 10, 5, 100, 1, 50};
	const int numbersLength = 7;
	for (int i = 0; i < numbersLength; i++) {
		if (numbers[i] == n) {
			printf("Found n\n");
			return 0;
		}
	}
	printf("Not found\n");
	return 1;
}
#include <stdio.h>
int main()
{
  int c;
	int n = 10 // Number of elements in array
	int array[] = {0, 1, 2, 3, 4, 5, 6, 7, 8 ,9}
	int search = 2; // Number to find
  int first = 0;
  int last = n - 1;
  int middle = (first+last)/2; // Average
  while (first <= last) {
    if (array[middle] < search)
      first = middle + 1;
    else if (array[middle] == search) {
      printf("%d found at location %d.\n", search, middle+1);
      break;
    }
    else
      last = middle - 1;
    middle = (first + last)/2;
  }
  if (first > last)
    printf("Not found! %d isn't present in the list.\n", search);
  return 0;
}

Sorting Algorithms

AlgorithmAverage Time ComplexityRAMUse If
Selection SortO(n²) | Ω(n²)1 varNever
Bubble SortO(n²) | Ω(n)0
Insertion SortO(n²) (Faster if the data is kind of sorted)0
Merge SortO(n log n) | Ω(n log n)More RAMLarge Arrays
Quick SortO(n log n)Less RAMSmall arrays

Data Structures

Queue (Concept)

First In, First Out

  • enqueue - Add an item to the end of the line.
  • dequeue - Remove an item from the beginning of the line.

Stack (Concept)

Last In, First Out
The last item added will be used first, so the first ones added might never be used.

  • push - Add an item to the top of the stack
  • pop - Remove an item from the top of the stack

Arrays

SearchO(log n)
Multiple data typesNo
ResizableNo
RAM UsageLow
double prices[] = {5.0, 3.2, 13.0};
double values[5]; //Initialize an empty array, that can hold 5 items.
printf("%lf€", prices[0]);
printf("%d", sizeof(prices)); //3

2D Arrays

int numbers[2][3] = {
	{1, 2, 3},
	{4, 5, 6}
};
numbers[0][1]; //2
numbers[1][2]; //6
numbers[0][1] = 10; //Set [0][1] to 10.
// Note: Calculate the rows and columns automatically.
int rows = sizeof(numbers)/sizeof(numbers[0]);
int columns = sizeof(numbers[0])/sizeof(numbers[0][0]);

Array of Strings

char cars[][10] = {"Mustang", "Corvette", "Camaro"};
//cars[0] = "Tesla"; ERROR! String arrays don't support item assignment.
strcpy(cars[0], "Tesla"); //Does the same, but <string.h> must be included.

Resize Array

Manually

#include <stdlib.h>
int *list = malloc(3 * sizeof(int));
// Works like an array
list[0] = 1;
list[1] = 2;
list[2] = 3;
//Same as
*list = 1;
*(list + 1) = 2;
*(list + 2) = 3;
// ...
// Add 1 extra byte to the array
int *tmp = malloc(4 * sizeof(int));
if (tmp == NULL) {
	free(list);
	return 1;
}
for (int i = 0; i < 3; i++){
	tmp[i] = list[i];
}
tmp[3] = 4;
free(list);
list = tmp;
// ...
free(list);
return 0;

Realloc

int *list = malloc(3 * sizeof(int));
list[0] = 1;
list[1] = 2;
list[2] = 3;
// ...
int *tmp = realloc(list, 4 * sizeof(int)); //tmp is only needed to verify if realloc was successful, if realloc was assigned to int and failed, the 3 bytes from the list would be lost as list = NULL.
if (tmp == NULL) {
	free(list);
	return 1;
}
list = tmp;
list[3] = 4;
// ...
free(list);
return 0;

Linked Lists

Linked ListTC
Add element unorderedO(1)
Add element orderedO(n)
SearchO(n)
InsertO(n)
Multiple data typesYes
ResizableYes
RAM UsageMedium

Each element has its value, and a pointer to the next element. The last element’s pointer is NULL. (NULL == 0x0)

[N] -> [N] -> [N] -> [N] ...
typedef struct node {
	int number;        // Value
	struct node *next; // Pointer to the next node
} node;

Unordered Linked List Example

Both code blocks are unsafe (if there’s no memory, the program won’t free)

typedef struct node {
	int number;
	struct node *next;
} node;
int main(void) {
	node *list = NULL;
	node *n = malloc(sizeof(node)); // n points at the first node of the linked list.
	(*n).number = 1; // First element = 1.
	//Or
	n->number = 1;   // Same.
	n->next = NULL;  // Point to NULL so it won't point to a garbage value.
	list = n;
	node *n = malloc(sizeof(node)); // n points at the first node of the linked list.
	n->number = 2;
	n->next = list;
	list = n;
}
#include <stdlib.h>
#include <stdio.h>
typedef struct node {
	int number;
	struct node *next;
} node;
int main(void) {
	node *list = NULL;
	int numbers[] = {1, 2, 3};
	// Create linked list with the elements in the array.
	for (int i = 0; i < 3; i++) {
			node *n = malloc(sizeof(node));
			if (n == NULL) return 1;
			n->number = number;
			n->next = NULL;
			n->next = list;
			list = n;
		}
	// Print all elements from linked list.
	node *ptr = list;
	while (ptr != NULL) {
		printf("%i\\n", ptr->number);
		ptr = ptr->next; // Borrow the value in the list->n, which has the next node address.
	}
	// Free the list's memory.
	ptr = list;
	while (ptr != NULL) {
		node *next = ptr->next;
		free(ptr);
		ptr = next;
	}
}

Trees

BalancedUnbalancedTC
Add elementO(n)O(n)
SearchO(log n)O(n)
InsertO(log n)O(n)
Multiple data typesYesYes
ResizableYesYes
RAM UsageHighHigh

If organized correctly, allows for binary search.
Each node has 2 pointers.

       [N]    - root / parent
     /     \\
   [N]     [N]  - parent and children
   / \\     / \\
 [N] [N] [N] [N] - leaf / child
typedef struct treenode {
	int value;
	struct treenode *left;
  struct treenode *right;
} treenode;
treenode *createnode(int value) {
	treenode* result = malloc(sizeof(treenode));
	if (result != NULL) {
		result->left = NULL;
		result->right = NULL;
		result->value = value;
	}
	return result;
}
void printtabs(int numtabs){
	for (int i=0; i < numtabs; i++){
		printf("\\t");
	}
}
void printtree_rec(treenode *root, int level){
	if (root == NULL) {
		printtabs(level);
		printf("----<empty>-----\\n");
		return;
	}
	// Preordered
	printtabs(level);
	printf("value = %d\\n", root->value);
	printtabs(level);
	printf("left\\n");
	printtree_rec(root->left, level + 1);
	printtabs(level);
	printf("right\\n");
	printtree_rec(root->right, level + 1);
	printtabs(level);
	printf("done\\n");
}
void printtree(treenode *root) {
	printtree_rec(root, 0);
}
int main(void){
	treenode *n1 = createnode(10);
	treenode *n2 = createnode(11);
	treenode *n3 = createnode(12);
	treenode *n4 = createnode(13);
	treenode *n5 = createnode(14);
	// n1 will be the root
	n1->left = n2;
	n1->right = n3;
	n3->left = n4;
	n4->right = n5;
	printtree(n1);
	free(n1);
	free(n2);
	free(n3);
	free(n4);
	free(n5);
}

Hash Tables

AverageWorstBest
Add elementO(1)O(n)
SearchO(1)O(n)
InsertO(1)O(n)
Multiple data typesYesYes
ResizableYesYes
RAM UsageVery HighVery High

An array, in which every element is a linked list.
The array is used to ‘choose’ a linked list, and the linked list stores a dynamic number of elements.

[0] -> [N] -> [N] -> [N]
[1] -> [N] -> [N]
[2] -> [N]
[3] -> [N] -> [N]
[4] -> [N] -> [N] -> [N]
[5] -> [N] -> [N]
0-5 - Array element
N - Linked list node

Trie

TrieAverageWorst
Add elementO(1)O(1)
SearchO(1)O(1)
InsertO(1)O(1)
Multiple data typesYesYes
ResizableYesYes
RAM UsageExtremely HighExtremely High

Tree, but every node is an array.
Useful to store words. Every element in an array is linked to the next letter.
Trie

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#define ALPHABET_SIZE 26
struct TrieNode {
    struct TrieNode *children[ALPHABET_SIZE];
    bool isendofword;
};
typedef struct TrieNode TrieNode;
TrieNode *createNode() {
    TrieNode *node = (TrieNode *)malloc(sizeof(TrieNode));
    node->isendofword = false;
    for (int i = 0; i < ALPHABET_SIZE; i++) {
        node->children[i] = NULL;
    }
    return node;
}
void insert(TrieNode *root, const char *key) {
    TrieNode *current = root;
    for (int i = 0; i < strlen(key); i++) {
        int index = key[i] - 'a';
        if (current->children[index] == NULL) {
            current->children[index] = createNode();
        }
        current = current->children[index];
    }
    current->isendofword = true;
}
bool search(TrieNode *root, const char *key) {
    TrieNode *current = root;
    for (int i = 0; i < strlen(key); i++) {
        int index = key[i] - 'a';
        if (current->children[index] == NULL) {
            return false;
        }
        current = current->children[index];
    }
    return (current != NULL && current->isendofword);
}
bool isempty(TrieNode *root) {
    for (int i = 0; i < ALPHABET_SIZE; i++) {
        if (root->children[i] != NULL) {
            return false;
        }
    }
    return true;
}
TrieNode *deleteHelper(TrieNode *root, const char *key, int depth) {
    if (root == NULL) {
        return NULL;
    }
    if (depth == strlen(key)) {
        if (root->isendofword) {
            root->isendofword = false;
        }
        if (isempty(root)) {
            free(root);
            root = NULL;
        }
        return root;
    }
    int index = key[depth] - 'a';
    root->children[index] = deleteHelper(root->children[index], key, depth + 1);
    if (isempty(root) && !root->isendofword) {
        free(root);
        root = NULL;
    }
    return root;
}
void deletekey(TrieNode *root, const char *key) {
    deleteHelper(root, key, 0);
}
int main() {
    TrieNode *root = createNode();
    insert(root, "example");
    insert(root, "word");
    printf("%s\n", search(root, "word") ? "Found" : "Not Found");
    printf("%s\n", search(root, "example") ? "Found" : "Not Found");
    printf("%s\n", search(root, "trude") ? "Found" : "Not Found");
    deletekey(root, "word");
    printf("%s\n", search(root, "word") ? "Found" : "Not Found");
    return 0;
}