├── algorithms ├── graph_algorithms │ ├── traversal │ │ ├── advanced │ │ │ ├── README.md │ │ │ ├── test_graph.png │ │ │ ├── bfs_general.cpp │ │ │ └── dfs_general.cpp │ │ └── basic │ │ │ ├── bfs_maze.cpp │ │ │ └── dfs_maze.c │ ├── minimal_spanning_tree │ │ ├── prim │ │ │ ├── README.md │ │ │ └── test_graph.png │ │ └── kruskal │ │ │ ├── README.md │ │ │ ├── test_graph.png │ │ │ └── kruskal_mst.cpp │ └── shortest_path_problem │ │ ├── dijkstra │ │ ├── README.md │ │ └── test_graph.png │ │ ├── bellman–ford │ │ ├── README.md │ │ ├── test_graph.png │ │ └── bellman-ford_spp.c │ │ └── floyd-warshall │ │ ├── README.md │ │ ├── test_graph.png │ │ └── floyd-warshall_spp.c ├── backtracking │ ├── README.md │ ├── knight_tour.c │ └── queens_problem.c ├── divide_and_conquer │ ├── README.md │ ├── fast_power.c │ └── hanoi_tower.cpp ├── greedy_and_heuristic │ ├── README.md │ ├── random_search.cpp │ └── lectures.cpp ├── dynamic_programming │ ├── README.md │ ├── fibonacci_memoization.cpp │ └── alan_bob.cpp ├── numerical_algorithms │ ├── euclidean_algorithm.c │ ├── eratosthenes_sieve.c │ └── primes.c ├── sorting_and_searching │ ├── advanced_sorting │ │ ├── example.c │ │ └── advanced_sorting.h │ ├── easy_sorting │ │ ├── example.c │ │ ├── easy_sorting.c │ │ └── easy_sorting.h │ ├── searching │ │ ├── example.c │ │ ├── search_algorithms.h │ │ └── search_algorithms.c │ ├── non_comparison_sorting │ │ ├── example.cpp │ │ ├── non_comparison_sorting.h │ │ └── non_comparison_sorting.cpp │ ├── abstract_sorting │ │ ├── example.cpp │ │ └── abstract_sorting.hpp │ ├── list_sorting │ │ └── example.cpp │ └── comparator_sorting │ │ ├── c_style_comparator.c │ │ └── c++_style_comparator.cpp └── combinatorial_algorithms │ ├── newton_binomial.c │ ├── all_sums.c │ └── permutations.c ├── recap └── questions.pdf ├── .editorconfig ├── .gitignore ├── data_structures ├── tree │ ├── binary_search_tree │ │ ├── simple_bst │ │ │ ├── tests.h │ │ │ ├── example.cpp │ │ │ └── tests.cpp │ │ └── template_bst │ │ │ └── example.cpp │ ├── trie_tree │ │ ├── example.cpp │ │ ├── trie.cpp │ │ └── trie.h │ └── splay_tree │ │ └── example.cpp ├── hash_table │ ├── linear_probing │ │ ├── example.cpp │ │ ├── linear_probing_hash.h │ │ └── linear_probing_hash.cpp │ ├── separate_chaining │ │ ├── example.cpp │ │ ├── separate_chaining_hash.cpp │ │ └── separate_chaining_hash.h │ ├── template_hash_map │ │ └── example.cpp │ └── hash_table_tests.hpp ├── stack │ ├── static_stack │ │ ├── example.c │ │ ├── static_stack.c │ │ └── static_stack.h │ ├── template_stack │ │ ├── template_stack.hpp │ │ └── example.cpp │ └── dynamic_stack │ │ └── example.cpp ├── queue │ ├── static_queue │ │ ├── example.cpp │ │ └── static_queue.hpp │ ├── dynamic_queue │ │ └── example.cpp │ ├── template_queue │ │ ├── template_queue.hpp │ │ └── example.cpp │ └── priority_queue │ │ └── example.cpp └── deque │ └── example.cpp ├── LICENSE ├── CREDITS.txt ├── utils ├── student.h └── benchmark.hpp ├── README.md ├── CODING_STYLE.md └── schedule /algorithms/graph_algorithms/traversal/advanced/README.md: -------------------------------------------------------------------------------- 1 | ![](test_graph.png) 2 | -------------------------------------------------------------------------------- /algorithms/graph_algorithms/minimal_spanning_tree/prim/README.md: -------------------------------------------------------------------------------- 1 | ![](test_graph.png) 2 | -------------------------------------------------------------------------------- /algorithms/graph_algorithms/minimal_spanning_tree/kruskal/README.md: -------------------------------------------------------------------------------- 1 | ![](test_graph.png) 2 | -------------------------------------------------------------------------------- /algorithms/graph_algorithms/shortest_path_problem/dijkstra/README.md: -------------------------------------------------------------------------------- 1 | ![](test_graph.png) 2 | -------------------------------------------------------------------------------- /recap/questions.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanFilipov/FMI-DSA/HEAD/recap/questions.pdf -------------------------------------------------------------------------------- /algorithms/graph_algorithms/shortest_path_problem/bellman–ford/README.md: -------------------------------------------------------------------------------- 1 | ![](test_graph.png) 2 | -------------------------------------------------------------------------------- /algorithms/graph_algorithms/shortest_path_problem/floyd-warshall/README.md: -------------------------------------------------------------------------------- 1 | ![](test_graph.png) 2 | -------------------------------------------------------------------------------- /algorithms/graph_algorithms/traversal/advanced/test_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanFilipov/FMI-DSA/HEAD/algorithms/graph_algorithms/traversal/advanced/test_graph.png -------------------------------------------------------------------------------- /algorithms/graph_algorithms/minimal_spanning_tree/prim/test_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanFilipov/FMI-DSA/HEAD/algorithms/graph_algorithms/minimal_spanning_tree/prim/test_graph.png -------------------------------------------------------------------------------- /algorithms/graph_algorithms/minimal_spanning_tree/kruskal/test_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanFilipov/FMI-DSA/HEAD/algorithms/graph_algorithms/minimal_spanning_tree/kruskal/test_graph.png -------------------------------------------------------------------------------- /algorithms/graph_algorithms/shortest_path_problem/dijkstra/test_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanFilipov/FMI-DSA/HEAD/algorithms/graph_algorithms/shortest_path_problem/dijkstra/test_graph.png -------------------------------------------------------------------------------- /algorithms/graph_algorithms/shortest_path_problem/bellman–ford/test_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanFilipov/FMI-DSA/HEAD/algorithms/graph_algorithms/shortest_path_problem/bellman–ford/test_graph.png -------------------------------------------------------------------------------- /algorithms/graph_algorithms/shortest_path_problem/floyd-warshall/test_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanFilipov/FMI-DSA/HEAD/algorithms/graph_algorithms/shortest_path_problem/floyd-warshall/test_graph.png -------------------------------------------------------------------------------- /algorithms/backtracking/README.md: -------------------------------------------------------------------------------- 1 | # [Backtracking](https://en.wikipedia.org/wiki/Backtracking) 2 | 3 | ## Examples 4 | 5 | * [_Eight queens problem_](https://en.wikipedia.org/wiki/Eight_queens_puzzle) 6 | * [_Knight's tour problem_](https://en.wikipedia.org/wiki/Knight%27s_tour) 7 | * [DFS](https://github.com/IvanFilipov/FMI-DSA/tree/master/algorithms/graph_algorithms/traversal/advanced) 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | end_of_line = lf 11 | insert_final_newline = true 12 | 13 | # C/C++ source files 14 | [*.{c,h,cpp,hpp}] 15 | indent_style = tab 16 | indent_size = 4 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | *.smod 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | 31 | # Project files 32 | *.layout 33 | *.depend 34 | 35 | # Test project dir 36 | /example/* 37 | -------------------------------------------------------------------------------- /algorithms/divide_and_conquer/README.md: -------------------------------------------------------------------------------- 1 | # [Divide and conquer](https://en.wikipedia.org/wiki/Divide-and-conquer_algorithm) 2 | 3 | ## Examples 4 | * [_Fast power_](https://en.wikipedia.org/wiki/Exponentiation_by_squaring) 5 | * [_Tower of Hanoi_](https://en.wikipedia.org/wiki/Tower_of_Hanoi) 6 | * [Merge sort](https://github.com/IvanFilipov/FMI-DSA/blob/master/algorithms/sorting_and_searching/advanced_sorting/advanced_sorting.c) 7 | * [Quick sort](https://github.com/IvanFilipov/FMI-DSA/blob/master/algorithms/sorting_and_searching/advanced_sorting/advanced_sorting.c) 8 | -------------------------------------------------------------------------------- /data_structures/tree/binary_search_tree/simple_bst/tests.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file tests.h 7 | * @author Ivan Filipov 8 | * @date 02.2019 9 | * @brief Some tests for our custom BST. 10 | * 11 | * @see https://en.wikipedia.org/wiki/Binary_search_tree 12 | */ 13 | 14 | #pragma once 15 | 16 | /** run a fixed tree test */ 17 | void run_basic_tests(); 18 | 19 | /** run a randomized tree test */ 20 | void run_advanced_tests(); 21 | -------------------------------------------------------------------------------- /data_structures/hash_table/linear_probing/example.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file example.cpp 7 | * @author Ivan Filipov 8 | * @date 01.2019 9 | * @brief Example usage and test of our custom liner probing hash table. 10 | */ 11 | 12 | #include // std::cout 13 | 14 | #include "linear_probing_hash.h" // lin_pr_hash_table 15 | #include "../hash_table_tests.hpp" // run_tests() 16 | 17 | int main() { 18 | 19 | std::cout << "######## LINEAR PROBING HASH TABLE #########\n\n"; 20 | 21 | lin_pr_hash_table table; 22 | 23 | run_tests(table); 24 | 25 | return 0; 26 | } 27 | 28 | 29 | -------------------------------------------------------------------------------- /data_structures/hash_table/separate_chaining/example.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file example.cpp 7 | * @author Ivan Filipov 8 | * @date 01.2019 9 | * @brief Example usage and test of our custom separate chaining hash table. 10 | */ 11 | 12 | #include // std::cout 13 | 14 | #include "separate_chaining_hash.h" // sp_ch_hash_table 15 | #include "../hash_table_tests.hpp" // run_tests() 16 | 17 | int main() { 18 | 19 | std::cout << "######## SEPARATE CHAINING HASH TABLES #########\n\n"; 20 | 21 | sp_ch_hash_table table; 22 | 23 | run_tests(table); 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /data_structures/tree/binary_search_tree/simple_bst/example.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file main.cpp 7 | * @author Ivan Filipov 8 | * @date 02.2019 9 | * @brief Main for example usage 10 | * and running some tests 11 | * for our custom splay tree. 12 | */ 13 | 14 | #include 15 | 16 | #include "tests.h" 17 | 18 | int main() { 19 | 20 | std::printf("running basic tests on given binary search tree\n"); 21 | run_basic_tests(); 22 | 23 | std::printf("\n\nnow something unexpected ... \n"); 24 | std::printf("completely random tree!\n\n"); 25 | run_advanced_tests(); 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /algorithms/greedy_and_heuristic/README.md: -------------------------------------------------------------------------------- 1 | # [Heuristic](https://en.wikipedia.org/wiki/Heuristic) & [Greedy](https://en.wikipedia.org/wiki/Greedy_algorithm) 2 | 3 | ## Examples 4 | 5 | Heuristic: 6 | * [_Random search_](https://github.com/IvanFilipov/FMI-DSA/blob/master/algorithms/greedy_and_heuristic/random_search.cpp) 7 | 8 | Greedy: 9 | * [_Lectures_](https://github.com/IvanFilipov/FMI-DSA/blob/master/algorithms/greedy_and_heuristic/lectures.cpp)
10 | Task: We have lectures with their start-end times.
11 | We want to attend as many as we can, but we can't be
12 | at two lectures at the same time ofc. Which should we take?
13 | * [_Prim_](https://github.com/IvanFilipov/FMI-DSA/tree/master/algorithms/graph_algorithms/minimal_spanning_tree/prim) 14 | * [_Kruskal_](https://github.com/IvanFilipov/FMI-DSA/tree/master/algorithms/graph_algorithms/minimal_spanning_tree/kruskal) 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ivan Filipov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /CREDITS.txt: -------------------------------------------------------------------------------- 1 | This is at least a partial credits-file of people that have 2 | contributed to DSA examples repository. It is sorted by name and 3 | formatted to allow easy grepping and beautification by 4 | scripts. The fields are: name (N), gitHub profile (G), 5 | contributed with (C). 6 | Thanks, 7 | 8 | Ivan Filipov 9 | ------------ 10 | 11 | N: Alexander Kruztev 12 | G: https://github.com/kruztev 13 | C: ensuring that all examples can be compiled with Clang 14 | 15 | N: Gratsiela Gancheva 16 | G: https://github.com/ganchGra 17 | C: author of example implementation of priority queue 18 | 19 | N: Nikolay Babulkov 20 | G: https://github.com/nbabulkov 21 | C: author of examples about Prim's algo for MST, LSD radix sort 22 | 23 | N: Plamen Minev 24 | G: https://github.com/pminev 25 | C: corrections in the example of BST, 26 | author of example implementation of deque 27 | 28 | N: Vasilena Peycheva 29 | G: https://github.com/VasiPeycheva 30 | C: ensuring that all examples can be compiled with MSVC++, 31 | author of example about BFS/DFS-iterative/DFS-recursive, 32 | creator of all graph visial representations, 33 | implemented Trie (Prefix tree). 34 | 35 | -------------------------------------------------------------------------------- /algorithms/dynamic_programming/README.md: -------------------------------------------------------------------------------- 1 | # [Dynamic Programming](https://en.wikipedia.org/wiki/Dynamic_programming) 2 | 3 | ## Examples 4 | 5 | * [_Alan and Bob_](https://github.com/IvanFilipov/FMI-DSA/blob/master/algorithms/dynamic_programming/alan_bob.cpp) 6 | 7 | **Task:**
8 | We have two brothers - Alan and Bob.
9 | They have N presents to share.
10 | Each present "Pi" for i = 1..N has a value V(Pi).
11 | If Alan takes presents for value A and
12 | Bob for B, our task is to minimize
13 | the difference |A - B|.
14 | 15 | * [_Dijkstra_](https://github.com/IvanFilipov/FMI-DSA/tree/master/algorithms/graph_algorithms/shortest_path_problem/dijkstra) 16 | * [_Bellman–Ford_](https://github.com/IvanFilipov/FMI-DSA/tree/master/algorithms/graph_algorithms/shortest_path_problem/bellman%E2%80%93ford) 17 | * [_Floyd-Warshall_](https://github.com/IvanFilipov/FMI-DSA/tree/master/algorithms/graph_algorithms/shortest_path_problem/floyd-warshall) 18 | 19 | # [Memoization](https://en.wikipedia.org/wiki/Memoization) 20 | 21 | ## Examples 22 | 23 | * [_Fibonacci_](https://github.com/IvanFilipov/FMI-DSA/blob/master/algorithms/dynamic_programming/fibonacci_memoization.cpp) 24 | -------------------------------------------------------------------------------- /algorithms/divide_and_conquer/fast_power.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file fast_power.c 7 | * @author Ivan Filipov 8 | * @date 01.2019 9 | * @brief Fast power algorithm as 10 | * "Divide and conquer" example 11 | * @see https://en.wikipedia.org/wiki/Exponentiation_by_squaring 12 | */ 13 | 14 | #include 15 | 16 | /** 17 | * @brief calculates x ^ n, with fewer 18 | * multiplications than the regular algorithm 19 | * @param[in] x: the base 20 | * @param[in] n: the exponent 21 | */ 22 | double fast_pow(double x, unsigned int n) { 23 | 24 | printf("calculating %.5lf ^ %u...\n", x, n); 25 | 26 | if (n == 0) 27 | return 1; 28 | if (n == 1) 29 | return x; 30 | if (n & 1) // odd 31 | return x * fast_pow(x, n - 1); 32 | else // even 33 | return fast_pow(x * x, n / 2); 34 | } 35 | 36 | int main() { 37 | 38 | double base = 2.0; 39 | unsigned int power = 15; 40 | 41 | printf("power(%lf, %u) = %lf\n", base, power, fast_pow(base, power)); 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /algorithms/numerical_algorithms/euclidean_algorithm.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file euclidean_algorithm.c 7 | * @author Ivan Filipov 8 | * @date 09.2018 9 | * @brief Finding out the greatest common deviser 10 | * of two natural numbers using Euclid's algorithm. 11 | * 12 | * @see https://en.wikipedia.org/wiki/Euclidean_algorithm 13 | */ 14 | 15 | #include 16 | 17 | /// find GCD of two numbers recursively 18 | /// just following the mathematical definition 19 | int gcd_rec(int a, int b) { 20 | 21 | return (b == 0) ? a : gcd_rec(b, a % b); 22 | } 23 | 24 | /// an iterative version of the same algorithm 25 | int gcd_itr(int a, int b) { 26 | 27 | int temp; 28 | while (b != 0) { 29 | temp = a % b; 30 | a = b; 31 | b = temp; 32 | } 33 | 34 | return a; 35 | } 36 | 37 | int main() { 38 | 39 | int a = 272, b = 64; 40 | 41 | printf("using recursive approach : GCD(%d, %d) = %d\n", a, b, gcd_rec(a, b)); 42 | printf("using iterative approach : GCD(%d, %d) = %d\n", a, b, gcd_itr(a, b)); 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /data_structures/stack/static_stack/example.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file example.c 7 | * @author Ivan Filipov 8 | * @date 10.2018 9 | * @brief Example usage of our C-style stack data structure. 10 | * @see https://en.wikipedia.org/wiki/Stack_(abstract_data_type) 11 | */ 12 | 13 | #include // fprintf() 14 | 15 | #include "static_stack.h" // our stack declarations 16 | 17 | /// how many input from user to be read 18 | #define N_INPUTS 5 19 | 20 | int main() { 21 | // create the stack 22 | stack s; 23 | stack_init(&s); 24 | /* alternative : 25 | stack s = {}; 26 | */ 27 | // push some elements 28 | data_type el; 29 | int cnt = N_INPUTS; 30 | printf("please, enter %d elements :\n", N_INPUTS); 31 | do { 32 | scanf("%d", &el); 33 | if (el > 1000) el = el % 1000; 34 | stack_push(&s, el); 35 | } while (--cnt); 36 | // nicely output the elements using pop() 37 | printf("\nthe stack looks like :\n"); 38 | while (!stack_is_empty(&s)) 39 | printf("\n|%3d|\n|___|", stack_pop(&s)); 40 | putchar('\n'); 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /data_structures/stack/static_stack/static_stack.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file static_stack.c 7 | * @author Ivan Filipov 8 | * @date 10.2018 9 | * @brief A simple linear data structure - stack. 10 | * Implemented in plain C as an array adapter. 11 | * @see https://en.wikipedia.org/wiki/Stack_(abstract_data_type) 12 | */ 13 | 14 | #include // fprintf() 15 | 16 | #include "static_stack.h" // our stack declarations 17 | 18 | void stack_init(stack* const context) { 19 | 20 | context->top = 0; 21 | for (int i = 0; i < MAX_SSIZE; i++) 22 | context->data[i] = 0; 23 | } 24 | 25 | int stack_is_empty(const stack* const context) { 26 | 27 | return context->top == 0; 28 | } 29 | 30 | void stack_push(stack* const context, data_type el) { 31 | 32 | if (context->top == MAX_SSIZE) 33 | fprintf(stderr, "not enough space in the stack!"); 34 | else 35 | context->data[context->top++] = el; 36 | } 37 | 38 | data_type stack_pop(stack* const context) { 39 | 40 | if (stack_is_empty(context)) { 41 | fprintf(stderr, "empty stack!"); 42 | return 0; 43 | } 44 | 45 | return context->data[--context->top]; 46 | } 47 | -------------------------------------------------------------------------------- /data_structures/stack/static_stack/static_stack.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file static_stack.h 7 | * @author Ivan Filipov 8 | * @date 10.2018 9 | * @brief A simple linear data structure - stack. 10 | * Implemented in plain C as an array adapter. 11 | * @see https://en.wikipedia.org/wiki/Stack_(abstract_data_type) 12 | */ 13 | 14 | #pragma once 15 | 16 | /// maximum count of elements in stack 17 | #define MAX_SSIZE 100 18 | 19 | /// elements type 20 | typedef int data_type; 21 | 22 | /** stack data structure representation */ 23 | typedef struct { 24 | data_type data[MAX_SSIZE]; //!< simple array chunk, in which the data will be stored 25 | int top; //!< a pointer to the top element 26 | } stack; 27 | 28 | // notice that in C we don't have "this" pointer, that is why 29 | // our API gets "context" as an argument. 30 | 31 | /** Stack initialization. All elements are set to be zeros */ 32 | void stack_init(stack* const context); 33 | 34 | /** Check if a stack is empty. Returns 1 if empty */ 35 | int stack_is_empty(const stack* const context); 36 | 37 | /** Add new element on the top of the stack */ 38 | void stack_push(stack* const context, data_type el); 39 | 40 | /** Remove the top element, return its value */ 41 | data_type stack_pop(stack* const context); 42 | -------------------------------------------------------------------------------- /data_structures/tree/trie_tree/example.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file example.cpp 7 | * @author Vasilena Peycheva 8 | * @date 03.2019 9 | * @brief Example usage of 10 | * the trie tree. 11 | */ 12 | 13 | 14 | #include 15 | #include 16 | 17 | #include "trie.h" 18 | 19 | using dsa::trie; 20 | 21 | /// create dictionary from a given file with words 22 | void create_dictionary(trie& tree, const std::string& filename) { 23 | 24 | std::ifstream in(filename); 25 | if (!in.is_open()) return; 26 | 27 | std::string word; 28 | while (true) { 29 | std::getline(in, word); 30 | 31 | if (!in) return; 32 | 33 | if (tree.insert(word)) 34 | std::cout << "Successfully added <" + word + "> \n"; 35 | else 36 | std::cout << "Failed to add <" + word + "> \n"; 37 | } 38 | } 39 | 40 | /// create dictionary 41 | /// search for words from the dictionary 42 | void test() { 43 | 44 | trie trie; 45 | create_dictionary(trie, "dictionary.txt"); 46 | std::string word; 47 | 48 | while (true) { 49 | std::cin >> word; 50 | 51 | if (word == "exit") break; 52 | 53 | if (trie.search(word)) 54 | std::cout << " <" + word + "> is in the dictionary \n"; 55 | else 56 | std::cout << "no match \n"; 57 | } 58 | } 59 | 60 | int main() { 61 | 62 | /* run the test */ 63 | test(); 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /data_structures/stack/template_stack/template_stack.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file template_stack.hpp 7 | * @author Ivan Filipov 8 | * @date 11.2018 9 | * @brief A template generalizing the idea, 10 | * that the stack is only an adapter for linked list or array. 11 | */ 12 | 13 | #pragma once 14 | 15 | namespace dsa { 16 | /** 17 | * @class t_stack 18 | * @brief Stack interface for different underlaying containers. 19 | * @tparam T: type of elements stored 20 | * @tparam Container: type of the underlaying container. 21 | * 22 | * @note %Container must [have back, push/pop_back, empty, size] in its interface. 23 | */ 24 | template 25 | class t_stack { 26 | private: 27 | Container container; //!< the underlaying container 28 | 29 | public: 30 | // interface : 31 | /** Get the top element */ 32 | T& peek() { return container.back(); } 33 | /** Get the top element - read-only. */ 34 | const T& peek() const { return container.back(); } 35 | 36 | /** Add element on the top. */ 37 | void push(const T& el) { container.push_back(el); } 38 | 39 | /** Remove the top element */ 40 | void pop() { container.pop_back(); } 41 | 42 | /** Check if the stack is empty */ 43 | bool empty() const { return container.empty(); } 44 | 45 | /** Get the count of elements stored */ 46 | size_t size() const { return container.size(); } 47 | }; 48 | } // namespace dsa 49 | -------------------------------------------------------------------------------- /utils/student.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file student.h 7 | * @author Ivan Filipov 8 | * @date 10.2018 9 | */ 10 | 11 | #pragma once 12 | 13 | #include // std::string 14 | #include // std::ostream 15 | 16 | namespace dsa { 17 | /** 18 | * @struct student 19 | * @brief A utility class representing a student - name and faculty number. 20 | * Used in some examples as test class. 21 | */ 22 | struct student { 23 | std::string name; 24 | int fn; 25 | 26 | student(const std::string& n, int fnum) : 27 | name(n), fn(fnum) {} 28 | 29 | student() = default; 30 | student(const student&) = default; 31 | student& operator=(const student&) = default; 32 | ~student() = default; 33 | 34 | bool operator<(const student& rhs) const { 35 | 36 | return name < rhs.name; 37 | } 38 | bool operator>(const student& rhs) const { 39 | 40 | return name > rhs.name; 41 | } 42 | bool operator>=(const student& rhs) const { 43 | 44 | return name >= rhs.name; 45 | } 46 | bool operator==(const student& rhs) const { 47 | 48 | return (name == rhs.name) && (fn == rhs.fn); 49 | } 50 | }; 51 | } 52 | // can lead to linkage errors if included in more than one cpp 53 | static // means keep copy of this function in each cpp 54 | std::ostream& operator<<(std::ostream& os, const dsa::student& st) { 55 | 56 | return os << std::endl 57 | << "name : " << st.name 58 | << " fn : " << st.fn; 59 | } 60 | -------------------------------------------------------------------------------- /algorithms/sorting_and_searching/advanced_sorting/example.c: -------------------------------------------------------------------------------- 1 | 2 | /******************************************************************************* 3 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 4 | *******************************************************************************/ 5 | 6 | /** 7 | * @file example.c 8 | * @author Ivan Filipov 9 | * @date 12.2019 10 | * @brief An example usage of our sorting implementations. 11 | */ 12 | 13 | #include // printf() 14 | #include // memcpy() 15 | 16 | #include "advanced_sorting.h" 17 | 18 | /// a simple macro to get the # of elements in C array 19 | #define sizeof_arr(arr) (size_t)(sizeof(arr) / sizeof(int)) 20 | 21 | /// simply output the array's contain 22 | void print_arr(int arr[], size_t size) { 23 | 24 | for (size_t i = 0; i < size; i++) 25 | printf("%d ", arr[i]); 26 | 27 | printf("\n\n"); 28 | } 29 | 30 | int main() { 31 | 32 | int arr[] = { 255, -124, 5, 11, 2, 6, 7 , -42, 13, 88, 21, 9, 8 }; 33 | // copy arrays 34 | int arr_copy_f[sizeof_arr(arr)]; 35 | int arr_copy_s[sizeof_arr(arr)]; 36 | // actual copy the data 37 | memcpy(arr_copy_f, arr, sizeof(arr)); 38 | memcpy(arr_copy_s, arr, sizeof(arr)); 39 | 40 | printf("Starting array: \n"); 41 | print_arr(arr, sizeof_arr(arr)); 42 | 43 | printf("result of Merge sort: \n"); 44 | merge_sort(arr, sizeof_arr(arr)); 45 | print_arr(arr, sizeof_arr(arr)); 46 | 47 | printf("result of Quick sort: \n"); 48 | quick_sort(arr_copy_f, sizeof_arr(arr_copy_f)); 49 | print_arr(arr_copy_f, sizeof_arr(arr_copy_f)); 50 | 51 | printf("result of Heap sort: \n"); 52 | heap_sort(arr_copy_s, sizeof_arr(arr_copy_s)); 53 | print_arr(arr_copy_s, sizeof_arr(arr_copy_s)); 54 | 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /algorithms/sorting_and_searching/easy_sorting/example.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file example.c 7 | * @author Ivan Filipov 8 | * @date 12.2019 9 | * @brief An example usage of our sorting implementations. 10 | */ 11 | 12 | #include // printf() 13 | #include // memcpy() 14 | 15 | #include "easy_sorting.h" 16 | 17 | /// a simple macro to get the # of elements in C array 18 | #define sizeof_arr(arr) (size_t)(sizeof(arr) / sizeof(int)) 19 | 20 | /// simply output the array's contain 21 | void print_arr(int arr[], size_t size) { 22 | 23 | for (size_t i = 0; i < size; i++) 24 | printf("%d ", arr[i]); 25 | 26 | printf("\n\n"); 27 | } 28 | 29 | int main() { 30 | 31 | int arr[] = { 255, -124, 5, 11, 2, 6, 7 , -42, 13, 88, 21, 9, 8 }; 32 | // copy arrays 33 | int arr_copy_f[sizeof_arr(arr)]; 34 | int arr_copy_s[sizeof_arr(arr)]; 35 | // actual copy the data 36 | memcpy(arr_copy_f, arr, sizeof(arr)); 37 | memcpy(arr_copy_s, arr, sizeof(arr)); 38 | 39 | printf("Starting array : \n"); 40 | print_arr(arr, sizeof_arr(arr)); 41 | 42 | printf("result of Insertion sort : \n"); 43 | insertion_sort(arr, sizeof_arr(arr)); 44 | print_arr(arr, sizeof_arr(arr)); 45 | 46 | printf("result of Bubble sort : \n"); 47 | bubble_sort(arr_copy_f, sizeof_arr(arr_copy_f)); 48 | print_arr(arr_copy_f, sizeof_arr(arr_copy_f)); 49 | 50 | printf("result of Selection sort : \n"); 51 | selection_sort(arr_copy_s, sizeof_arr(arr_copy_s)); 52 | print_arr(arr_copy_s, sizeof_arr(arr_copy_s)); 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /algorithms/numerical_algorithms/eratosthenes_sieve.c: -------------------------------------------------------------------------------- 1 | 2 | /******************************************************************************* 3 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 4 | *******************************************************************************/ 5 | 6 | /** 7 | * @file eratosthenes_sieve.c 8 | * @author Ivan Filipov 9 | * @date 09.2018 10 | * @brief Example implementation of Eratosthenes algorithm 11 | * for finding out prime numbers below N. 12 | * 13 | * @see https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes 14 | */ 15 | 16 | #include // printf() 17 | #include // boolean type 18 | 19 | /// marker for prime number 20 | #define PRIME false 21 | /// marker for composite number 22 | #define COMPOSITE true 23 | /// shorter name for "unsigned long long" 24 | typedef unsigned long long ull_int; 25 | /// up to which number to find primes 26 | #define N 20000 27 | /// sieve[i] = PRIME <=> i is a prime number, 28 | /// otherwise sieve[i] = COMPOSITE 29 | bool sieve[N] = { PRIME, }; 30 | 31 | /// just outputs all prime numbers 32 | void print_primes() { 33 | 34 | for (ull_int i = 2; i < N; i++) 35 | if (sieve[i] == PRIME) 36 | printf("%llu is a prime!\n", i); 37 | } 38 | 39 | /// the algorithm is simple, for each prime number, 40 | /// mark with COMPOSITE all numbers which have this number as a multiplier 41 | void eratosthenes_sieve() { 42 | 43 | for (ull_int i = 2; i < N; i++) 44 | if (sieve[i] == PRIME) 45 | for (ull_int j = i * i; j < N; j += i) // the start value and the step are important 46 | sieve[j] = COMPOSITE; 47 | 48 | print_primes(); 49 | } 50 | 51 | int main() { 52 | 53 | /* run the algorithm and output all primes */ 54 | eratosthenes_sieve(); 55 | 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /data_structures/queue/static_queue/example.cpp: -------------------------------------------------------------------------------- 1 | 2 | /******************************************************************************* 3 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 4 | *******************************************************************************/ 5 | 6 | /** 7 | * @file example.cpp 8 | * @author Ivan Filipov 9 | * @date 10.2018 10 | * @brief Example usage of our custom static queue data structure. 11 | */ 12 | 13 | #include 14 | 15 | #include "static_queue.hpp" 16 | 17 | using dsa::static_queue; 18 | /// max number of elements 19 | const size_t MAXN = 8; 20 | /// how many elements to add/remove 21 | const int ECOUNT = 8; 22 | 23 | int main() { 24 | 25 | static_queue q; 26 | 27 | /* will throw an exception 28 | for (int i = 0; i < MAXN + 1; i++) 29 | q.enqueue(i); 30 | */ 31 | 32 | for (int i = 0; i < ECOUNT; i++) { 33 | q.push(i + 1); 34 | std::cout << "elem " << i + 1 35 | << " successfully added!\n"; 36 | } 37 | 38 | std::cout << "\ncurrent size : " 39 | << q.size() << std::endl; 40 | 41 | for (int i = 0; i < ECOUNT; i++) { 42 | std::cout << "elem " << q.pop() 43 | << " successfully removed!\n"; 44 | } 45 | 46 | std::cout << "\ncurrent size : " 47 | << q.size() << std::endl; 48 | 49 | 50 | for (int i = 0; i < ECOUNT - 3; i++) { 51 | q.push(i + 1); 52 | std::cout << "elem " << i + 1 53 | << " successfully added!\n"; 54 | } 55 | 56 | std::cout << "\ncurrent size : " 57 | << q.size() << std::endl; 58 | 59 | for (int i = 0; i < ECOUNT - 5; i++) { 60 | std::cout << "elem " << q.pop() 61 | << " successfully removed!\n"; 62 | } 63 | 64 | std::cout << "\ncurrent size : " 65 | << q.size() << std::endl; 66 | 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /algorithms/combinatorial_algorithms/newton_binomial.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file newton_binomial.c 7 | * @author Ivan Filipov 8 | * @date 10.2018 9 | * @brief An example for calculating "n over k". 10 | * 11 | * @see https://en.wikipedia.org/wiki/Binomial_coefficient 12 | * 13 | * If using the direct formula: N! / (K! *(N - K)!), the C/C++ types will overflow for a small N and K. 14 | * Calculates the binomial coefficient from Newton's binomial theorem: 15 | * ( n ) 16 | * ( ) = ? 17 | * ( k ), 18 | * using Pascal's triangle. 19 | * @note the result is the k-th element of triangle's n-th line. 20 | * 21 | */ 22 | 23 | #include // printf() 24 | 25 | /// the value of "N" 26 | #define N 5 27 | /// the value of "K" 28 | #define K 2 29 | 30 | /// an array to hold the last line from the triangle 31 | int line[N + 1]; 32 | 33 | /** 34 | * @brief calculates @c N "over" @c K 35 | * @retval the result of the calculation 36 | */ 37 | int calc_n_over_k() { 38 | // first element of the line is always 1 ... 39 | line[0] = 1; 40 | // generate lines 41 | for (int i = 1; i <= N; i++) { 42 | // generate lines members 43 | line[i] = 1; //... last also 44 | 45 | for (int j = i - 1; j > 0; j--) { 46 | // every other element is a sum 47 | // of the two numbers from the previous line 48 | line[j] += line[j - 1]; 49 | } 50 | } 51 | 52 | // the result is the k-th element in the n -th line 53 | return line[K]; 54 | } 55 | 56 | int main() { 57 | 58 | printf("( %d )\n", N); 59 | printf("( ) = %d\n", calc_n_over_k()); 60 | printf("( %d )\n", K); 61 | 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /algorithms/combinatorial_algorithms/all_sums.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file all_sums.c 7 | * @author Ivan Filipov 8 | * @date 10.2018 9 | * @brief An example for generating all possible ways of 10 | * presenting a natural number as a sum of other natural numbers. 11 | * 12 | * Example 4 = 13 | * = 3 + 1 14 | * = 2 + 2 15 | * = 2 + 1 + 1 16 | * = 1 + 1 + 1 + 1 17 | */ 18 | 19 | #include // printf 20 | 21 | /// maximum number of addends 22 | #define MAXN 100 23 | 24 | /// all addends -> addends[i] contains the "i" summand 25 | int addends[MAXN]; 26 | 27 | /// simple result output 28 | void print_sum(unsigned int len) { 29 | 30 | for (unsigned int i = 1; i < len; i++) 31 | printf("%d + ", addends[i]); 32 | 33 | printf("%d\n", addends[len]); 34 | } 35 | 36 | /** 37 | * @brief recursive algorithms for generating all sums 38 | * @param[in] n: the number to be broke up into summands 39 | * @param[in] pos: which is the current summand's position 40 | * 41 | * The idea is simple: just using that recursive definition - 42 | * sum(0) = {} 43 | * sum(n) = {k} + sum(n-k), k = n, n-1,..., 1. 44 | */ 45 | void creat_next_sum(unsigned int n, int pos) { 46 | 47 | if (n == 0) { 48 | // bottom case 49 | print_sum(pos - 1); 50 | return; 51 | } 52 | 53 | for (unsigned int k = n; k >= 1; k--) { 54 | // get a smaller number 55 | addends[pos] = k; 56 | // represent the smaller number as sum, too. 57 | if (addends[pos] <= addends[pos - 1]) 58 | creat_next_sum(n - k, pos + 1); 59 | } 60 | } 61 | 62 | int main() { 63 | 64 | /* number to be split */ 65 | unsigned int n = 4; 66 | 67 | /* initialize step */ 68 | addends[0] = n + 1; 69 | 70 | /* run the algorithm */ 71 | creat_next_sum(n, 1); 72 | 73 | return 0; 74 | } 75 | -------------------------------------------------------------------------------- /algorithms/dynamic_programming/fibonacci_memoization.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file fibonacci_memoization.cpp 7 | * @author Ivan Filipov 8 | * @date 02.2019 9 | * @brief Finding Fibonacci N-th member, 10 | * using memoization 11 | * as an example of dynamic programming. 12 | * 13 | * @see http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibtable.html 14 | */ 15 | 16 | #include // std::cout 17 | #include // std::array 18 | 19 | /// type for Fibonacci numbers 20 | typedef unsigned long long int fib_num; 21 | /// maximum Fibonacci members that can be calculated 22 | /// @note fib(50) = 12586269025, more values can be stored in 23 | /// an unsigned long long int, but don't be too greedy. 24 | const int MAX_N = 50; 25 | /// cached (already calculated) Fibonacci members, 26 | /// value of 0 means still not calculated 27 | std::array cached_members = { 0 }; 28 | 29 | /** 30 | * @brief calculates N-th member of Fibonacci sequence, using 31 | * cached values if possible, while calculating save all results for future use 32 | * @param[in] n: which member to be calculated 33 | * @retval the value of that member 34 | */ 35 | fib_num fib_memo(unsigned int n) { 36 | // get the cached value by reference, so writing to it will be possible 37 | fib_num& cached_val = cached_members[n]; 38 | // first numbers are known constants 39 | if (n < 2) 40 | cached_val = n; 41 | else if (cached_val == 0) // not calculated 42 | cached_val = fib_memo(n - 1) + fib_memo(n -2); // calculate this value 43 | 44 | return cached_val; 45 | } 46 | 47 | int main() { 48 | 49 | /* run some tests */ 50 | std::cout << fib_memo(20) << std::endl; 51 | 52 | std::cout << fib_memo(50) << std::endl; 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /algorithms/greedy_and_heuristic/random_search.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file random_search.cpp 7 | * @author Ivan Filipov 8 | * @date 02.2019 9 | * @brief Searching for an element in an array, 10 | * with random probes, 11 | * as an example of heuristic algorithm. 12 | */ 13 | 14 | #include // time() 15 | #include // rand(), srand() 16 | #include // printf() 17 | #include // vector 18 | 19 | 20 | /** 21 | * @brief search for an element in an array, 22 | * using random probes 23 | * @param[in] elem: the element to be searched 24 | * @param[in] nums: the array to be tested 25 | * @retval index of the element if found, -1 else 26 | * @note O(n) best case time complexity, worst case complexity is unknown 27 | */ 28 | 29 | int random_search(int elem, const std::vector& nums) { 30 | // used to end the algorithm if there is no such element 31 | std::vector tried(nums.size(), false); 32 | size_t tries = 0; 33 | size_t try_at; 34 | 35 | while (tries < nums.size()) { 36 | // get position 37 | try_at = rand() % nums.size(); 38 | // if the searched element is on it 39 | if (nums[try_at] == elem) 40 | return try_at; 41 | // if we haven't tried this index 42 | if (!tried[try_at]) { 43 | // mark it as tried 44 | tried[try_at] = true; 45 | tries++; 46 | } 47 | } 48 | // not found 49 | return -1; 50 | } 51 | 52 | int main() { 53 | 54 | srand(time(NULL)); 55 | 56 | std::vector nums = { 1, 21, 11, 23, 105, -94, -28, 44 }; 57 | int elem = 11; 58 | 59 | printf("searching for %d :\n", elem); 60 | 61 | int index = random_search(elem, nums); 62 | 63 | if (index == -1) 64 | printf("can't find such element\n"); 65 | else 66 | printf("found at index %d\n", index); 67 | 68 | 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /algorithms/dynamic_programming/alan_bob.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file alan_bob.cpp 7 | * @author Ivan Filipov 8 | * @date 01.2019 9 | * @brief Solution to "alan & bob" problem, 10 | * as an example of dynamic programming. 11 | * 12 | * @see README.md 13 | */ 14 | 15 | #include // std::printf() 16 | 17 | /// count of all presents 18 | const size_t NUM_PRE = 10; 19 | /// maximum cost of a single present 20 | const unsigned int MAXVAL = 200; 21 | /// all presents values 22 | const unsigned int values[NUM_PRE] = { 3, 2, 3, 2, 2, 23 | 77, 89, 23, 90, 11 }; 24 | /// maximum different presents sum, 25 | /// if all presents have @p MAXVAL 26 | const unsigned long int ALL_SUMS = NUM_PRE * MAXVAL; 27 | /// markers for achievable sums, if sum "i" is possible, then 28 | /// is_sum_possible[i] will be marked as true 29 | bool is_sum_possible[ALL_SUMS] = { false }; 30 | 31 | /// the solution to the task 32 | void solve() { 33 | 34 | // the sum of all presents' values 35 | unsigned long long sum_total = 0; 36 | //sum all the values of the presents 37 | for (size_t i = 0; i < NUM_PRE; i++) 38 | sum_total += values[i]; 39 | // sum 0 is always achievable 40 | is_sum_possible[0] = true; 41 | // creating all the possible sums 42 | for (size_t i = 0; i < NUM_PRE; i++) 43 | for (size_t j = sum_total; j + 1 > 0; j--) 44 | if (is_sum_possible[j]) // if this sum is possible 45 | is_sum_possible[j + values[i]] = true; // the sum + current present is also possible 46 | 47 | // the closest sum to sum_total / 2 is the searched one 48 | for (size_t i = sum_total / 2; i + 1 > 0; i--) 49 | if (is_sum_possible[i]) { 50 | printf("sum for Alan %u, sum for Bob %llu\n", i, sum_total - i); 51 | return; 52 | } 53 | } 54 | 55 | int main() { 56 | 57 | solve(); 58 | 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /data_structures/queue/dynamic_queue/example.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file example.cpp 7 | * @author Ivan Filipov 8 | * @date 10.2018 9 | * @brief Tests and example usage of our custom dynamic_queue. 10 | */ 11 | 12 | #include // std::cin, std::endl 13 | 14 | #include "dynamic_queue.hpp" // dsa::dynamic_queue 15 | 16 | using dsa::dynamic_queue; 17 | /// number of elements to add/remove 18 | const int MAXN = 8; 19 | 20 | int main() { 21 | 22 | dynamic_queue q; 23 | 24 | for (int i = 0; i < MAXN; i++) { 25 | q.push(i + 1); 26 | std::cout << "elem " << q.back() 27 | << " successfully added!\n"; 28 | } 29 | 30 | std::cout << "\ncurrent size : " 31 | << q.size() << std::endl; 32 | 33 | for (int i = 0; i < MAXN; i++) { 34 | std::cout << "elem " << q.front(); 35 | q.pop(); 36 | std::cout << " successfully removed!\n"; 37 | } 38 | 39 | std::cout << "\ncurrent size : " 40 | << q.size() << std::endl; 41 | 42 | 43 | for (int i = 0; i < MAXN - 3; i++) { 44 | q.push(i + 1); 45 | std::cout << "elem " << q.back() 46 | << " successfully added!\n"; 47 | } 48 | 49 | std::cout << "\ncurrent size : " 50 | << q.size() << std::endl; 51 | 52 | for (int i = 0; i < MAXN - 5; i++) { 53 | std::cout << "elem " << q.front(); 54 | q.pop(); 55 | std::cout << " successfully removed!\n"; 56 | } 57 | 58 | for (int i = 0; i < MAXN; i++) { 59 | q.push(i + 1); 60 | std::cout << "elem " << q.back() 61 | << " successfully added!\n"; 62 | } 63 | 64 | dynamic_queue q1 = q; 65 | 66 | while (!q1.empty()) { 67 | std::cout << "q1\'s front (" << q.front() 68 | << ") == q\'s front (" << q.front() << ")\n"; 69 | q1.pop(); 70 | q.pop(); 71 | } 72 | 73 | std::cout << "\ncurrent size : " 74 | << q.size() << std::endl; 75 | 76 | return 0; 77 | } 78 | -------------------------------------------------------------------------------- /data_structures/tree/splay_tree/example.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file main.cpp 7 | * @author Ivan Filipov 8 | * @date 02.2019 9 | * @brief Main for example usage 10 | * and running some tests 11 | * for our custom splay tree. 12 | */ 13 | 14 | #include // std::cout 15 | 16 | #include "../../../utils/student.h" 17 | #include "splay_tree.hpp" 18 | 19 | /** Run a fixed tree tests */ 20 | void run_basic_tests() { 21 | // create empty tree 22 | dsa::splay_tree tree; 23 | // add some elements 24 | tree.insert({ "Ivancho", 40000 }); 25 | tree.insert({ "Mariika", 25000 }); 26 | tree.insert({ "Gencho", 50001 }); 27 | tree.insert({ "Pencho", 25000 }); 28 | tree.insert({ "Genka", 42000 }); 29 | tree.insert({ "Penka", 25000 }); 30 | tree.insert({ "Kalin", 40000 }); 31 | tree.insert({ "Kalinka", 25000 }); 32 | 33 | std::cout << "\ntree's element count = " << tree.size() << std::endl; 34 | tree.print_sorted_keys(std::cout); 35 | 36 | std::cout << "\n\nsearching for Ivancho :" << std::endl; 37 | try { 38 | const dsa::student& s = tree.find({ "Ivancho", 40000 }); 39 | std::cout << "found! His fn is : " << s.fn << std::endl; 40 | } catch (...) { 41 | std::cout << "not found!" << std::endl; 42 | } 43 | 44 | std::cout << "\n\nsearching for Ivan :" << std::endl; 45 | try { 46 | const dsa::student& s = tree.find({ "Ivan", 200 }); 47 | std::cout << "found! His fn is : " << s.fn << std::endl; 48 | } catch (...) { 49 | std::cout << "not found!" << std::endl; 50 | } 51 | 52 | std::cout << "\nremoving \"Genka\"..."; 53 | try { 54 | tree.remove({ "Genka", 42000 }); 55 | std::cout << "done!" << std::endl; 56 | } catch(...) {} 57 | tree.print_sorted_keys(std::cout); 58 | std::cout << std::endl; 59 | } 60 | 61 | 62 | int main() { 63 | 64 | std::cout << "running basic tests on given splay tree" << std::endl; 65 | run_basic_tests(); 66 | 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /data_structures/tree/binary_search_tree/template_bst/example.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file main.cpp 7 | * @author Ivan Filipov 8 | * @date 02.2019 9 | * @brief Main for example usage 10 | * and running some tests 11 | * for our custom binary search tree. 12 | */ 13 | 14 | #include // std::cout 15 | 16 | #include "../../../../utils/student.h" 17 | #include "bs_tree.hpp" 18 | 19 | /** Run a fixed tree tests */ 20 | void run_basic_tests() { 21 | // create empty tree 22 | dsa::bs_tree tree; 23 | // add some elements 24 | tree.insert({ "Ivancho", 40000 }); 25 | tree.insert({ "Mariika", 25000 }); 26 | tree.insert({ "Gencho", 50001 }); 27 | tree.insert({ "Pencho", 25000 }); 28 | tree.insert({ "Genka", 42000 }); 29 | tree.insert({ "Penka", 25000 }); 30 | tree.insert({ "Kalin", 40000 }); 31 | tree.insert({ "Kalinka", 25000 }); 32 | 33 | std::cout << "\ntree's height = " << tree.get_height() << std::endl; 34 | tree.print_sorted_keys(std::cout); 35 | 36 | std::cout << "\n\nsearching for Ivancho :" << std::endl; 37 | try { 38 | const dsa::student& s = tree.find({ "Ivancho", 40000 }); 39 | std::cout << "found! His fn is : " << s.fn << std::endl; 40 | } catch (...) { 41 | std::cout << "not found!" << std::endl; 42 | } 43 | 44 | std::cout << "\n\nsearching for Ivan :" << std::endl; 45 | try { 46 | const dsa::student& s = tree.find({ "Ivan", 200 }); 47 | std::cout << "found! His fn is : " << s.fn << std::endl; 48 | } catch (...) { 49 | std::cout << "not found!" << std::endl; 50 | } 51 | 52 | std::cout << "\nremoving \"Genka\"..."; 53 | try { 54 | tree.remove({ "Genka", 42000 }); 55 | std::cout << "done!" << std::endl; 56 | } catch(...) {} 57 | tree.print_sorted_keys(std::cout); 58 | std::cout << std::endl; 59 | } 60 | 61 | 62 | int main() { 63 | 64 | std::cout << "running basic tests on given binary search tree" << std::endl; 65 | run_basic_tests(); 66 | 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /data_structures/deque/example.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file example.cpp 7 | * @author Plamen Minev 8 | * @date 04.2019 9 | * @brief Example usage and some tests 10 | * for our implementation of 11 | * the deque. 12 | */ 13 | 14 | #include // std::cout 15 | 16 | #include "deque.hpp" // dsa::deque 17 | 18 | int main() { 19 | 20 | dsa::deque mydeque; 21 | std::cout << "print 1!\n\n"; 22 | mydeque.print(std::cout); 23 | for (size_t i = 0; i < 50; i++) { 24 | mydeque.push_back(i); 25 | } 26 | std::cout << "print 2!\n\n"; 27 | mydeque.print(std::cout); 28 | for (size_t i = 0; i < 16; i++) { 29 | mydeque.pop_back(); 30 | } 31 | 32 | std::cout << "print 3!\n\n"; 33 | mydeque.print(std::cout); 34 | for (size_t i = 0; i < 50; i++) { 35 | mydeque.push_front(i); 36 | } 37 | 38 | std::cout << "print 4!\n\n"; 39 | mydeque.print(std::cout); 40 | for (size_t i = 0; i < 16; i++) { 41 | mydeque.pop_front(); 42 | } 43 | std::cout << "print 5!\n\n"; 44 | mydeque.print(std::cout); 45 | 46 | std::cout << "Front element " << mydeque.front()<<'\n'; 47 | std::cout << "Last element " << mydeque.back()<< '\n'; 48 | mydeque.pop_back(); 49 | std::cout << "Last element after pop_back " << mydeque.back() << '\n'; 50 | std::cout << "Last element using operator[]" << mydeque[mydeque.size()-1]<<'\n'; 51 | 52 | dsa::deque secondDeque(mydeque); 53 | std::cout << "\nCopied deque!\n"; 54 | secondDeque.print(std::cout); 55 | 56 | for (size_t i = 0; i < 5; i++) { 57 | secondDeque.pop_back(); 58 | } 59 | 60 | for (size_t i = 0; i < 5; i++) { 61 | secondDeque.pop_front(); 62 | } 63 | 64 | std::cout << "Manipulated second deque!\n"; 65 | secondDeque.print(std::cout); 66 | std::cout << "Debug print of first deque before operator=!\n"; 67 | mydeque.print(std::cout); 68 | 69 | mydeque = secondDeque; 70 | 71 | std::cout << "Debug print of first deque after operator=!\n"; 72 | mydeque.print(std::cout); 73 | return 0; 74 | } 75 | -------------------------------------------------------------------------------- /data_structures/stack/dynamic_stack/example.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file example.cpp 7 | * @author Ivan Filipov 8 | * @date 10.2018 9 | * @brief Example usage of our custom dynamic_stack. 10 | */ 11 | 12 | #include // std::cout, std::endl 13 | 14 | #include "dynamic_stack.hpp" // dsa::dynamic_stack 15 | 16 | using dsa::dynamic_stack; 17 | 18 | const int MAXN = 8; //!< number of additions/removals 19 | 20 | int main() { 21 | 22 | dynamic_stack s; 23 | 24 | for (int i = 0; i < MAXN; i++) { 25 | 26 | s.push(i + 1); 27 | std::cout << "elem " << s.top() 28 | << " successfully added!\n"; 29 | } 30 | std::cout << "current size : " << s.size() << std::endl << std::endl; 31 | 32 | for (int i = 0; i < MAXN; i++) { 33 | 34 | std::cout << "elem " << s.top(); 35 | s.pop(); 36 | std::cout << " successfully removed!\n"; 37 | } 38 | std::cout << "current size : " << s.size() << std::endl << std::endl; 39 | 40 | 41 | for (int i = 0; i < MAXN - 3; i++) { 42 | 43 | s.push(i + 1); 44 | std::cout << "elem " << s.top() 45 | << " successfully added!\n"; 46 | } 47 | std::cout << "current size : " << s.size() << std::endl << std::endl; 48 | 49 | for (int i = 0; i < MAXN - 5; i++) { 50 | 51 | std::cout << "elem " << s.top(); 52 | s.pop(); 53 | std::cout << " successfully removed!\n"; 54 | 55 | } 56 | std::cout << "current size : " << s.size() << std::endl << std::endl; 57 | 58 | std::cout << "top elem changed from " << s.top(); 59 | s.top() = 10; 60 | std::cout << " to " << s.top() << std::endl; 61 | 62 | for (int i = 0; i < MAXN - 3; i++) { 63 | 64 | s.push(i + 1); 65 | std::cout << "elem " << s.top() 66 | << " successfully added!\n"; 67 | } 68 | 69 | std::cout << "\na copy s1?\n"; 70 | dynamic_stack s1 = s; 71 | while (!s1.empty()) { 72 | 73 | std::cout << "s1\'s top (" << s1.top() 74 | << ") == s\'s top (" << s.top() 75 | << ")\n"; 76 | s1.pop(); 77 | s.pop(); 78 | } 79 | 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /algorithms/combinatorial_algorithms/permutations.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file permutations.c 7 | * @author Ivan Filipov 8 | * @date 10.2018 9 | * @brief An example for generating all permutations of a given set of numbers. 10 | * 11 | * @see https://en.wikipedia.org/wiki/Permutation 12 | */ 13 | 14 | #include // printf(), putchar() 15 | #include // bool type 16 | 17 | //@{ 18 | /** definitions for used and unused numbers */ 19 | #define USED true 20 | #define UNUSED false 21 | //@} 22 | 23 | /// the power of the set to permute 24 | #define MAXN 4 25 | 26 | /// the set we are permuting 27 | int given[MAXN] = { 3, 11, 23, 7}; 28 | 29 | /// buffer for generating the current permutation 30 | int cur_perm[MAXN]; 31 | 32 | /// markers for used in current permutation 33 | bool used[MAXN] = { UNUSED, }; 34 | 35 | /// outputs the current permutation 36 | void print_perm() { 37 | 38 | for (int i = 0; i < MAXN; i++) 39 | printf("%d ", cur_perm[i]); 40 | 41 | putchar('\n'); 42 | } 43 | 44 | /** 45 | * @brief Generates permutations recursively. 46 | * @param[in] i: element on which index we are permuting 47 | * 48 | * The following algorithm is implemented: 49 | * Place each possible element on the first position, then 50 | * permute the other n - 1 elements on the other positions, using 51 | * the same strategy. 52 | */ 53 | void perm(int i) { 54 | 55 | if (i >= MAXN) { 56 | // the bottom of the recursion, 57 | // when the last element is placed 58 | print_perm(); 59 | return; 60 | } 61 | 62 | for (int k = 0; k < MAXN; k++) { 63 | // trying to use the k-th element of the set 64 | if (used[k] == UNUSED) { 65 | used[k] = USED; // marking it as used 66 | cur_perm[i] = given[k]; // saving it's value 67 | perm(i + 1); // generating the n-1 permutation 68 | used[k] = UNUSED; // unmarking after coming back form the recursion call 69 | } 70 | } 71 | } 72 | 73 | int main() { 74 | 75 | /* run the algorithm stating from the element on index 0*/ 76 | perm(0); 77 | 78 | return 0; 79 | } 80 | -------------------------------------------------------------------------------- /data_structures/queue/template_queue/template_queue.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file template_queue.hpp 7 | * @author Ivan Filipov 8 | * @date 11.2019 9 | * @brief A template generalizing the idea, 10 | * that the queue is only an adapter for linked list or array. 11 | */ 12 | #pragma once 13 | 14 | namespace dsa { 15 | template 16 | /** 17 | * @class t_queue 18 | * @brief Queue interface for different underlaying containers. 19 | * @tparam T: type of elements stored 20 | * @tparam Container: type of the underlaying container. 21 | * 22 | * @note %Container must [have back, push_back, pop_front, empty, size] in its interface. 23 | */ 24 | class t_queue { 25 | private: 26 | Container container; //!< the underlaying container 27 | 28 | public: 29 | // interface : 30 | /** Get the first element */ 31 | T& front() { return container.front(); } 32 | 33 | /** Get the first element - read-only. */ 34 | const T& front() const { return container.front(); } 35 | 36 | /** Get the last element */ 37 | T& back() { return container.back(); } 38 | 39 | /** Get the first element read-only. */ 40 | const T& back() const { return container.back(); } 41 | 42 | /** Enqueue: add new element at the end */ 43 | void push(const T& el) { container.push_back(el); } 44 | 45 | /** Dequeue: remove first element */ 46 | void pop() { container.pop_front(); } 47 | 48 | /** Check if the stack is empty */ 49 | bool empty() const { return container.empty(); } 50 | 51 | /** Get the count of elements stored */ 52 | size_t size() const { return container.size(); } 53 | }; 54 | 55 | /// specialization for std::vector, because there is no 56 | /// pop_front in vector's interface 57 | template<> 58 | void t_queue>::pop() { 59 | 60 | std::swap(container[0], container[container.size() - 1]); 61 | container.pop_back(); 62 | } 63 | 64 | /// specialization for dsa::dynamic_array 65 | template<> 66 | void t_queue>::pop() { 67 | 68 | std::swap(container[0], container[container.size() - 1]); 69 | container.pop_back(); 70 | } 71 | } // namespace dsa 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ФМИ - СДП 2 | 3 | Код от семинарите по СДП 4 | 2016-2019г. спец. Информатика, ФМИ, СУ 5 |
[Код от практикумите](https://github.com/VasiPeycheva/Data-Structures-and-Algorithms--2018-2019) 6 |
[Примери за напреднали](https://github.com/IvanFilipov/modern_c_plus_plus) 7 | 8 | 9 | ## Организация на хранилището: 10 | * `/algorithms/` - тук ще намерите базови примери за основните раздели алгоритми - 11 | "Комбинаторика", "Търсене с върщане", "Разделяй и владей", "Динамично оптимиране" 12 | и подробни примери за "Сортиране", "Търсене" и алгоритми върху "Графи" 13 | 14 | * `/data_structures/` - в тази директория са поместени реализаци на основните 15 | и по често срещаните структури от данни - 16 | "Динамичен масив", "Свързан списък", "Стек", 17 | "Опашка", "Дървета", "Хеш - таблици" 18 | 19 | * `/recap/` - материали за преговор на наученото по ООП и УП 20 | 21 | * `/utils/` - общи помощни ресурси 22 | 23 | * `разписание` - примерно разписание на обхванатите теми по седмици 24 | и допълнителна информация за съдържанието на цялостното изложение 25 | 26 | *всички примери са компилирани и тествани с компилаторите на GNU, MS и Apple (gcc, g++, VC++, clang) 27 | # FMI - DSA 28 | 29 | Some examples and source code during the DSA (data structures and algorithms) 30 | @ Faculty of Mathematics and Informatics, Sofia University 31 |
[Code from practicums](https://github.com/VasiPeycheva/Data-Structures-and-Algorithms--2018-2019) 32 |
[Advanced examples](https://github.com/IvanFilipov/modern_c_plus_plus) 33 | 34 | ## repository organization: 35 | * `/algorithms/` - simple examples of basic algorithm classes - 36 | "Combinatorics", "Backtracking", "Divide and conquer", "Dynamic programming" 37 | and more detailed of "Sorting", "Searching" и "Graph algorithms" 38 | 39 | * `/data_structures/` - implementations for all basic data structures - 40 | "Dynamic array", "Linked list", "Stack",
41 | "Queue", "Trees", "Hash tables" 42 | 43 | * `/recap/` - some materials to review the learned during IP and OOP 44 | 45 | * `/utils/` - just utilities 46 | 47 | * `schedule` - all the themes that are included + plan about when talking for them will be 48 | 49 | *all examples are compiled and run using GNU, MS and Apple compilers (gcc, g++, VC++, clang) 50 | -------------------------------------------------------------------------------- /algorithms/sorting_and_searching/searching/example.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file example.c 7 | * @author Ivan Filipov 8 | * @date 12.2019 9 | * @brief Some tests and usage examples for our 10 | * searching algorithms implementations. 11 | * 12 | * @see https://en.wikipedia.org/wiki/Search_algorithm 13 | * @note Theory in searching_algorithms.h, implementation details in searching_algorithms.c 14 | */ 15 | 16 | #include // printf() 17 | #include // qsort() 18 | #include // sqrt() 19 | 20 | #include "search_algorithms.h" 21 | 22 | #define ARR_SIZE 12 //!< how many elements are there 23 | 24 | /// simply output an array's content 25 | void print_arr(int arr[], size_t size) { 26 | 27 | for (size_t i = 0; i < size; i++) 28 | printf("%d ", arr[i]); 29 | 30 | printf("\n\n"); 31 | } 32 | 33 | // ugly syntax function for qsort 34 | int cmp(const void* x, const void* y) { 35 | 36 | return *(int*)x - *(int*)y; 37 | } 38 | 39 | int main() { 40 | 41 | int arr[ARR_SIZE] = { -124, 5, 11, 2, 6, 7, -42, 13, 88, 21, 9, 8 }; 42 | 43 | printf("Starting array : \n"); 44 | print_arr(arr, ARR_SIZE); 45 | 46 | printf("after sorting with qsort : \n"); 47 | qsort(arr, ARR_SIZE, sizeof(int), cmp); 48 | print_arr(arr, ARR_SIZE); 49 | 50 | int searched_elem = 11; 51 | printf("result of Linear searching for elem %d : %d \n", 52 | searched_elem, linear_search(arr, ARR_SIZE, searched_elem)); 53 | 54 | printf("result of Binary searching for elem %d : %d \n", 55 | searched_elem, binary_search(arr, ARR_SIZE, searched_elem)); 56 | 57 | printf("result of Jump searching for elem %d with step %d : %d \n", 58 | searched_elem, (int)sqrt(ARR_SIZE), jump_search(arr, ARR_SIZE, searched_elem)); 59 | 60 | searched_elem = -11; 61 | printf("result of Linear searching for elem %d : %d \n", 62 | searched_elem, linear_search(arr, ARR_SIZE, searched_elem)); 63 | 64 | printf("result of Binary searching for elem %d : %d \n", 65 | searched_elem, binary_search(arr, ARR_SIZE, searched_elem)); 66 | 67 | printf("result of Jump searching for elem %d with step %d : %d \n", 68 | searched_elem, (int)sqrt(ARR_SIZE), jump_search(arr, ARR_SIZE, searched_elem)); 69 | 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /CODING_STYLE.md: -------------------------------------------------------------------------------- 1 | Some rules and coding conventions that this repository is following. 2 | Please read them carefully before make any contribution (pull request, push). 3 | 4 | #### Naming conventions 5 | 6 | The naming conventions for **everything** in this repository is ***snake_case***. 7 | Exceptions are the constants. Use uppercase snake_case for them. 8 | After pointer type variables use appendix *ptr*. 9 | For references and pointers to objects write *&, \** immediately after the type. 10 | 11 | E.g : 12 | 13 | ````c++ 14 | int some_var; 15 | class dynamic_array; 16 | node* next_ptr; 17 | dynamic_array& lhs = rhs; 18 | const size_t MAX_SIZE = 1024; 19 | ```` 20 | 21 | 22 | 23 | #### Coding conventions 24 | 25 | * placing braces and spaces - opening bracket is on the same line, **everywhere**. The next line is always an empty line. E.g : 26 | 27 | ````C++ 28 | class class_definition { 29 | private: 30 | //... 31 | }; 32 | struct struct_definition { 33 | int x; 34 | double y; 35 | }; 36 | void func() { 37 | 38 | do_something; 39 | } 40 | ```` 41 | 42 | 43 | 44 | After each statement block (if, switch, for, while, do .. etc) there should be a space. E.g : 45 | 46 | ``` c++ 47 | if (cond) { 48 | // do something 49 | } else { 50 | // do something else 51 | } 52 | ``` 53 | 54 | If your block contains only one operation the braces are optional, also it can be done on the same line. E.g : 55 | 56 | ```C++ 57 | if (cond) 58 | long_long_op; 59 | 60 | if (cond) short_op; 61 | ``` 62 | 63 | In conditionals or function calls, there are **no** spaces after the opening brace. 64 | 65 | ````C++ 66 | func_call(param, other_param); 67 | if (a < b && b > c) do_something; 68 | ```` 69 | In loops the rules are: 70 | ````C++ 71 | for (int i = 0; i < n; i++) 72 | do_something; 73 | 74 | for (int i: arr) 75 | do_something; 76 | ```` 77 | 78 | Between binary operations there **are** spaces, but before/after unary operators there are **not**. 79 | 80 | #### General rules 81 | 82 | Indentations are **tabs** and each tab is 4 characters. 83 | Choose names that are meaningful, but not too long. 84 | 85 | Comments : in header files describe what a function/class should do, what parameters does it take, 86 | some theoretical information about complexity, usages etc. In source files write single line comments 87 | with information what logical steps does the algorithm make. Also try to follow Doxygen style. 88 | -------------------------------------------------------------------------------- /algorithms/sorting_and_searching/searching/search_algorithms.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file search_algorithms.h 7 | * @author Ivan Filipov 8 | * @date 12.2019 9 | * @brief Some searching algorithms implemented in plain C. 10 | * 11 | * @see https://en.wikipedia.org/wiki/Search_algorithm 12 | * @note Theory in searching_algorithms.h, implementation details in searching_algorithms.c 13 | */ 14 | 15 | #pragma once 16 | 17 | #include // size_t 18 | 19 | /** 20 | * @brief Linear searching for an element. 21 | * @param[in] arr: array of elements, in which to search 22 | * @param[in] size: size of the array 23 | * @param[in] elem: element to be searched for 24 | * @retval >0 element's index if found 25 | * @retval -1 if there is no such element 26 | * 27 | * Advantages/disadvantages: 28 | * - slow O(n) 29 | * + works always 30 | */ 31 | int linear_search(int arr[], size_t size, int elem); 32 | 33 | 34 | /** 35 | * @brief Searching for an element using binary search algorithm. 36 | * @param[in] arr: array of elements, in which to search, should be sorted 37 | * @param[in] size: size of the array 38 | * @param[in] elem: element to be searched for 39 | * @retval >0 element's index if found 40 | * @retval -1 if there is no such element 41 | * 42 | * Advantages/disadvantages: 43 | * - the array must be sorted 44 | * O(logn) time complexity 45 | */ 46 | int binary_search(int arr[], size_t size, int elem); 47 | 48 | 49 | /** 50 | * @brief Searching for an element using K - step search (Jump search). 51 | * @param[in] arr: array of elements, in which to search, should be sorted 52 | * @param[in] size: size of the array 53 | * @param[in] elem: element to be searched for 54 | * @retval >0 element's index if found 55 | * @retval -1 if there is no such element 56 | * 57 | * @see https://en.wikipedia.org/wiki/Jump_search 58 | * Advantages/disadvantages: 59 | * - only for sorted array 60 | * + time complexity of Jump search is between Linear search and Binary search 61 | * ! best results when step = sqrt(size) 62 | */ 63 | /// K - step search (Jump search) 64 | /// - only for sorted array 65 | /// + time complexity of Jump search is between Linear search and Binary search 66 | /// best results when step = sqrt(size) 67 | int jump_search(int* arr, size_t size, int elem); 68 | -------------------------------------------------------------------------------- /algorithms/sorting_and_searching/easy_sorting/easy_sorting.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file easy_sorting.c 7 | * @author Ivan Filipov 8 | * @date 12.2019 9 | * @brief The most basic sorting algorithms implemented in plain C. 10 | * 11 | * @see https://en.wikipedia.org/wiki/Sorting_algorithm 12 | * @note Theory in easy_sorting.h, implementation details in easy_sorting.c 13 | */ 14 | 15 | #include // C declaration of boolean type 16 | 17 | #include "easy_sorting.h" 18 | 19 | 20 | void swap(int* f, int* s) { 21 | 22 | int t = *f; 23 | *f = *s; 24 | *s = t; 25 | } 26 | 27 | void bubble_sort(int arr[], size_t size) { 28 | // an optimization to make the algorithm a bit faster and adaptive 29 | bool change_made = true; 30 | // iterating over the whole array, until there weren't any changes 31 | for (size_t i = 0; i < size - 1 && change_made; i++) { 32 | change_made = false; // flip the flag 33 | // iterating in the opposite direction, making the lightest element 34 | // "bubble" up to the top of sorted part 35 | for (size_t j = size - 1; j > i; j--) { 36 | // if two consecutive elements are in wrong order, then swap them 37 | if (arr[j - 1] > arr[j]) { 38 | swap(&arr[j - 1], &arr[j]); // the addresses are passed 39 | change_made = true; // raise the flag 40 | } 41 | } 42 | } 43 | } 44 | 45 | void selection_sort(int arr[], size_t size) { 46 | // the index of the current smallest element 47 | size_t index; 48 | // iterate through the whole array 49 | for (size_t i = 0; i < size - 1; i++) { 50 | index = i; // mark the current element as smallest 51 | for (size_t j = i + 1; j < size; j++) // for the elements in the non-sorted part 52 | if (arr[index] > arr[j]) // check if a smaller element than the current smallest is present 53 | index = j; // update the smallest element index 54 | // on each step the smallest element "joins" the sorted part 55 | swap(&arr[i], &arr[index]); 56 | } 57 | } 58 | 59 | void insertion_sort(int arr[], size_t size) { 60 | // shortest sorting algorithm, beat those three lines ;) 61 | for (size_t i = 1; i < size; i++) // for each element after the first 62 | for (size_t j = i ; j > 0 && arr[j] < arr[j-1]; j--) // go back until elements in the sorted part are smaller then the current 63 | swap(&arr[j], &arr[j-1]); // rotate the elements 64 | } 65 | -------------------------------------------------------------------------------- /algorithms/greedy_and_heuristic/lectures.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file lectures.cpp.cpp 7 | * @author Ivan Filipov 8 | * @date 02.2019 9 | * @brief Taking maximum lectures without collisions 10 | * task solution 11 | * as an example of a greedy algorithm. 12 | */ 13 | 14 | #include // std::printf() 15 | #include // std::string 16 | #include // std::vector 17 | #include // std::sort() 18 | 19 | /// begin - end time for each lecture 20 | using time_interval = std::pair; 21 | /// name - time interval for each lecture 22 | using lecture = std::pair; 23 | /// all lectures in a scheduler 24 | using scheduler = std::vector; 25 | /// all lectures to be taken 26 | scheduler sch; 27 | 28 | /// initialize the scheduler with some lectures 29 | void init() { 30 | 31 | sch = { 32 | std::make_pair("Object Oriented Programming", std::make_pair(3, 8)), 33 | std::make_pair("Introduction to Programming", std::make_pair(7, 10)), 34 | std::make_pair("Data Structures and Algorithms", std::make_pair(5, 12)), 35 | std::make_pair("Data bases", std::make_pair(9, 14)), 36 | std::make_pair("Computer Graphics", std::make_pair(13, 15)), 37 | std::make_pair("Internet of things", std::make_pair(15, 19)), 38 | std::make_pair("Network security", std::make_pair(17, 20)) 39 | }; 40 | } 41 | 42 | /// solve the task, by simply sorting all lectures by their end 43 | /// time and take them one by one 44 | void solve() { 45 | // sort by end time 46 | std::sort(sch.begin(), sch.end(), [](const lecture& a,const lecture& b) { 47 | return a.second.second < b.second.second; 48 | }); 49 | // first lecture is selected 50 | printf("%s form %u to %u\n", sch[0].first.c_str(), 51 | sch[0].second.first, sch[0].second.second); 52 | 53 | size_t last = 0, cur = 0; 54 | // take other lectures as so the 55 | // begin of current is after the end of last 56 | while (cur < sch.size() - 1) { 57 | cur++; 58 | if (sch[cur].second.first > sch[last].second.second) { 59 | last = cur; 60 | printf("%s form %u to %u\n", sch[cur].first.c_str(), 61 | sch[cur].second.first, sch[cur].second.second); 62 | } 63 | } 64 | } 65 | 66 | int main() { 67 | 68 | init(); 69 | solve(); 70 | 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /algorithms/sorting_and_searching/non_comparison_sorting/example.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file example.cpp 7 | * @author Ivan Filipov 8 | * @author Nikolay Babulkov 9 | * @date 12.2019 10 | * @brief An example usage of our non-comparison sorting implementations. 11 | */ 12 | 13 | #include // std::printf(), std::putchar() 14 | 15 | #include "non_comparison_sorting.h" 16 | 17 | using std::printf; 18 | using std::putchar; 19 | 20 | /// printing array with contain which can be casted to integer 21 | template 22 | void print_array(const std::vector& arr) { 23 | 24 | for(int i : arr) 25 | printf("%d ", i); 26 | putchar('\n'); 27 | } 28 | 29 | /// test counting sort on fixed array 30 | void test_counting_sort() { 31 | 32 | std::vector arr = { 124 ,5 , 11, 2, 6, 7 , 42 , 13 , 88 , 21 ,9 , 8 }; 33 | printf("\n--------------\n"); 34 | printf("\ngiven array : \n"); 35 | print_array(arr); 36 | 37 | counting_sort(arr); 38 | 39 | printf("\nresult of counting sort : \n"); 40 | print_array(arr); 41 | } 42 | 43 | /// test MSD radix sort on fixed array of strings 44 | void test_msd_radix() { 45 | 46 | std::vector given_names = { "pesho", "gosho", "katrin", "jean", 47 | "claude", "van", "damme" }; 48 | printf("\n--------------\n"); 49 | printf("\ngiven names : \n"); 50 | for (const std::string& name : given_names) 51 | printf("\"%s\" ", name.c_str()); 52 | 53 | msd_strings_radix_sort(given_names, 0, given_names.size(), 0); 54 | 55 | printf("\n\nafter MSD radix sorting : \n"); 56 | for (const std::string& name : given_names) 57 | printf("\"%s\" ", name.c_str()); 58 | putchar('\n'); 59 | } 60 | 61 | /// test LSD radix sort on fixed array 62 | void test_lsd_radix() { 63 | 64 | std::vector vec = { 22211, 555, 444, 1223, 1000, 322, 245, 231, 3, 23 }; 65 | printf("\n--------------\n"); 66 | printf("\ngiven array:\n"); 67 | print_array(vec); 68 | 69 | lsd_radix_sort(vec); 70 | 71 | printf("\nsorted output from LSD radix sorting: \n"); 72 | print_array(vec); 73 | } 74 | 75 | int main() { 76 | 77 | /* run counting sort algorithm */ 78 | test_counting_sort(); 79 | /* run MSD strings radix sort algorithm */ 80 | test_msd_radix(); 81 | /* run LSD integers radix sort algorithm */ 82 | test_lsd_radix(); 83 | 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /algorithms/sorting_and_searching/advanced_sorting/advanced_sorting.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file advanced_sorting.h 7 | * @author Ivan Filipov 8 | * @date 12.2019 9 | * @brief The most basic sorting algorithms implemented in plain C. 10 | * 11 | * @see https://en.wikipedia.org/wiki/Sorting_algorithm 12 | * @note Theory in advanced_sorting.h, implementation details in advanced_sorting.c 13 | */ 14 | 15 | #pragma once 16 | 17 | #include // size_t type definition 18 | 19 | /** 20 | * @brief a simple swap function 21 | * @note in C there are no references, so using pointers 22 | * is the only approach 23 | */ 24 | void swap(int* f, int* s); 25 | 26 | /** 27 | * @brief Sorts an array of numbers using Merge sort. 28 | * @param[in,out] arr: array to be sorted. 29 | * @param[in] size: number of element in the array. 30 | * 31 | * + good sides : STABLE , difficulty in best , worst and average case : ~ O(nlogn) 32 | * does not require random access to data, therefore it is 33 | * applicable to linked lists (only O(logn) additional memory for recursions) 34 | * - bad sides : requires additional memory ~ O(n), not adaptive ( when elements are nearly sorted, 35 | * the difficulty is still O(nlogn) ) 36 | * ! notice : an linear in-place merge exists, but it is both expensive and complex 37 | */ 38 | void merge_sort(int arr[], size_t size); 39 | 40 | /** 41 | * @brief Sorts an array of numbers using Quick sort. 42 | * @param[in,out] arr: array to be sorted. 43 | * @param[in] size: number of element in the array. 44 | * 45 | * + good sides : maybe the fastest one in average case : O(nlogn) 46 | * - bad sides: worst case : O(n^2), NOT STABLE, 47 | * additional memory for recursion calls O(logn) 48 | * pivot point choice could be critical to algorithm's performance 49 | * ! notice : an even faster algorithm based on the same logic exists : it is called 50 | * "Three way quick sort" 51 | */ 52 | void quick_sort(int arr[], size_t size); 53 | 54 | /** 55 | * @brief Sorts an array of numbers using Heap sort. 56 | * @param[in,out] arr: array to be sorted. 57 | * @param[in] size: number of element in the array. 58 | * 59 | * + good sides : O(nlogn) difficulty in every case, 60 | * in-place (no additional memory needed) 61 | * - bad sides : not stable, not really adaptive 62 | */ 63 | void heap_sort(int arr[], size_t size); 64 | -------------------------------------------------------------------------------- /algorithms/sorting_and_searching/non_comparison_sorting/non_comparison_sorting.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file non_comparison_sorting.h 7 | * @author Ivan Filipov 8 | * @author Nikolay Babulkov 9 | * @date 12.2019 10 | * @brief Some sorting algorithms, than use NO comparison operations. 11 | * 12 | * @see https://en.wikipedia.org/wiki/Sorting_algorithm#Non-comparison_sorts 13 | * @note Theory in non_comparison_sorting.h, implementation details in non_comparison_sorting.cpp 14 | */ 15 | 16 | #pragma once 17 | 18 | #include // std::string 19 | #include // std::vector 20 | 21 | /** 22 | * @brief Sorts an array of positive numbers using Counting sort. 23 | * @param[in,out] arr: array to be sorted. 24 | * 25 | * Counting sort : n is number of elements, k is range size ( k = max_elem - min_elem ) 26 | * + good sides : O(n + k) time complexity, easy to implement 27 | * - bad sides : O(k) additional memory, the range should be small 28 | * 29 | * @note could be easily modified to sort natural numbers. 30 | */ 31 | void counting_sort(std::vector& arr); 32 | 33 | /** 34 | * @brief Recursively sorts an array of strings using MSD radix sort. 35 | * @param[in,out] strings: array of strings to be sorted. 36 | * @param[in] low: left index cut into the array. 37 | * @param[in] high: right index cut. 38 | * @param[in] pos: current letter in strings. 39 | * 40 | * MSD Radix sort (variant) : n is number of strings to be sorted 41 | * c is count of all symbols 42 | * k is the length of the longest word 43 | * MSD strings radix sort 44 | * + good sides : O(n.k) time complexity in the worst case, but average is much better ~O(n+k) 45 | * - bad sides : using additional memory O(1), but not the be ignored 46 | * ! better than classical algorithms for strings, because, the compare operation is much expensive 47 | * when comparing strings 48 | */ 49 | void msd_strings_radix_sort(std::vector& strings, int low, int high, int pos); 50 | 51 | /** 52 | * @brief Sorts an array of strings using LSD radix sort. 53 | * @param[in,out] arr: array to be sorted. 54 | * LSD Radix Sort (variant) : n is number of integers to sort, 55 | * w is number of bits per each integer 56 | * LSD unsigned integer radix sort 57 | * + good sides O(n.w) time complexity 58 | * - bad sides O(n + w) space complexity 59 | */ 60 | void lsd_radix_sort(std::vector& arr); 61 | -------------------------------------------------------------------------------- /algorithms/sorting_and_searching/abstract_sorting/example.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file example.cpp 7 | * @author Ivan Filipov 8 | * @date 12.2019 9 | * @brief Example usage of our abstract sorting algorithms. 10 | */ 11 | 12 | #include // std::cout 13 | #include // std::grater, std::less 14 | 15 | #include "abstract_sorting.hpp" // abstract sorting algorithms with STL 16 | #include "../../../utils/student.h" // class for students 17 | #include "../../../data_structures/dynamic_array/dynamic_array.hpp" // our custom vector 18 | 19 | using namespace dsa; // include name 20 | // typedef like modern syntax 21 | using student_vec = dynamic_array; 22 | 23 | /// test all abstract sorting implementations on dsa::dynamic_array 24 | void test_abstract_sorts() { 25 | 26 | std::cout << "abstract sorting algorithms testing" << std::endl 27 | << "------------------" 28 | << std::endl 29 | << std::endl; 30 | 31 | student_vec st_vec = { { "Ivancho", 40000 }, { "Mariika", 25000 }, 32 | { "Gencho", 50001 }, { "Pencho", 25000 }, 33 | { "Genka", 42000 }, { "Penka", 25000 }, 34 | { "Kalin", 40000 }, { "Kalinka", 25000 } }; 35 | 36 | std::cout << "\ngiven elements :\n"; 37 | st_vec.print_elems(std::cout); 38 | 39 | // sorting using operator<, std::grater is a shortcut for writing that function 40 | std::cout << "\n\nsorted by dsa::insertion_sort with operator > for names :\n"; 41 | dsa::insertion_sort(st_vec.begin(), st_vec.end(), std::greater()); 42 | st_vec.print_elems(std::cout); 43 | 44 | std::cout << "\n\nsorted by std::sort (probably a version of quick sort) :\n"; 45 | std::sort(st_vec.begin(), st_vec.end()); 46 | st_vec.print_elems(std::cout); 47 | 48 | std::cout << "\n\nsorted by std::stable_sort (probably a version of merge sort) :\n"; 49 | std::stable_sort(st_vec.begin(), st_vec.end()); 50 | st_vec.print_elems(std::cout); 51 | 52 | std::cout << "\n\nsorted by dsa::merge_sort with operator < for names :\n"; 53 | dsa::merge_sort(st_vec.begin(), st_vec.end(), std::less()); 54 | st_vec.print_elems(std::cout); 55 | 56 | std::cout << "\n\nsorted by dsa::heap_sort with operator > for names :\n"; 57 | dsa::heap_sort(st_vec.begin(), st_vec.end(), std::greater()); 58 | st_vec.print_elems(std::cout); 59 | } 60 | 61 | 62 | 63 | int main() { 64 | 65 | /* testing abstract version of insertion, merge and heap sort */ 66 | test_abstract_sorts(); 67 | 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /data_structures/tree/trie_tree/trie.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file trie.cpp 7 | * @author Vasilena Peycheva 8 | * @date 03.04.2019 9 | * @brief Example implementation of Trie (Prefix tree). 10 | * 11 | * @see https://en.wikipedia.org/wiki/Trie 12 | * @see https://medium.com/basecs/trying-to-understand-tries-3ec6bede0014 13 | */ 14 | 15 | #include "trie.h" 16 | 17 | using dsa::trie; 18 | 19 | trie::trie(): root(new node) { 20 | //... 21 | } 22 | 23 | trie::~trie() { 24 | 25 | clean_rec(root); 26 | } 27 | 28 | bool trie::insert(const std::string& word) { 29 | 30 | if (!is_word(word)) 31 | return false; 32 | 33 | if (!root) // if the trie was cleaned 34 | root = new node(); // create root node (starting point) 35 | 36 | node* current = root; // starting point 37 | size_t index = 0; 38 | for (size_t i = 0; i < word.size(); i++) { 39 | // get the current letter index in node alphabet array 40 | index = indexation(word[i]); 41 | // if the letter is not already created (used with another word) 42 | if (!current->alphabet[index]) 43 | //create new node from the current node alphabet[letter index] 44 | current->alphabet[index] = new node(); 45 | current = current->alphabet[index]; 46 | } 47 | // mark the last node as end of a word 48 | current->is_word = true; 49 | return true; 50 | } 51 | 52 | bool trie::search(const std::string & word) { 53 | 54 | if (!is_word(word) || !root) 55 | return false; 56 | 57 | node* current = root; // starting point 58 | size_t index = 0; 59 | for (size_t i = 0; i < word.size(); i++) { 60 | // get the current letter index in node alphabet array 61 | index = indexation(word[i]); 62 | // if the current letter is not created, then the word is not in the trie 63 | if (!current->alphabet[index]) 64 | return false; 65 | current = current->alphabet[index]; 66 | } 67 | // check if the last node is marked as word 68 | return current->is_word; 69 | } 70 | 71 | void dsa::trie::clean() 72 | { 73 | clean_rec(root); 74 | root = nullptr; 75 | } 76 | 77 | void trie::clean_rec(node* root) { 78 | 79 | if (!root) 80 | return; 81 | 82 | // standard tree deletion 83 | for (size_t i = 0; i < ALPHABET_SIZE; i++) { 84 | if (root->alphabet[i]) 85 | clean_rec(root->alphabet[i]); 86 | } 87 | 88 | delete root; 89 | } 90 | 91 | int trie::indexation(char c) { 92 | 93 | if (islower(c)) 94 | return c - 'a'; 95 | if (isupper(c)) 96 | return c - 'A'; 97 | 98 | return -1; 99 | } 100 | 101 | bool trie::is_word(const std::string& word) { 102 | 103 | for (char c: word) { 104 | if (!isalpha(c)) 105 | return false; 106 | } 107 | 108 | return true; 109 | } 110 | -------------------------------------------------------------------------------- /algorithms/graph_algorithms/shortest_path_problem/floyd-warshall/floyd-warshall_spp.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file floyd-warshall_spp.cpp 7 | * @author Ivan Filipov 8 | * @date 01.2019 9 | * @brief Finding the shortest path from each vertex to all others, using Floyd-Warshall's algorithm. 10 | * 11 | * @see https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm 12 | * @note Written in plain C. 13 | * 14 | * Difference from Dijkstra - edges can also have negative weights, solves all-to-all (SPP). 15 | */ 16 | 17 | #include // printf(), putchar() 18 | 19 | /// graph matrix size 20 | #define SIZE 6 21 | /// "no path" constant, should be maximum INT_MAX / 2 22 | #define MAX 1000 23 | /// helper marco for finding minimum of two numbers 24 | #define min(x, y) ((x) < (y)) ? (x) : (y) 25 | 26 | /// the graph is represented as weighing matrix 27 | int graph[SIZE][SIZE] = { 28 | // vertex "to" : 29 | // A B C D E F // vertex "from" : 30 | { MAX, MAX, 6, 2, MAX, MAX }, // A 31 | { MAX, MAX, MAX, -7, MAX, MAX }, // B 32 | { MAX, 1, MAX, MAX, MAX, MAX }, // C 33 | { MAX, MAX, 11, MAX, MAX, MAX }, // D 34 | { 15, MAX, 10, MAX, MAX, -3 }, // E 35 | { MAX, 5, MAX, MAX, MAX, MAX } // F 36 | }; 37 | 38 | /// simply outputs info about all found paths 39 | void print_paths() { 40 | 41 | printf("\nall paths with their weights:\n"); 42 | for (size_t i = 0; i < SIZE; i++) { 43 | printf("from %c ", 'A' + i); 44 | 45 | for (size_t j = 0; j < SIZE; j++) 46 | if (graph[i][j] != MAX) 47 | printf("[ to %c : %d ]", 'A' + j, graph[i][j]); 48 | 49 | putchar('\n'); 50 | } 51 | } 52 | 53 | /** 54 | * @brief Runs Floyd-Warshall'salgorithm in the given graph. 55 | * 56 | * @note Outputs the path's weights into the same weighting matrix 57 | * @note Time complexity : O(n^3) 58 | */ 59 | void floyd_shortest_paths() { 60 | 61 | for (size_t k = 0; k < SIZE; k++) // for each vertex 62 | for (size_t i = 0; i < SIZE; i++) // between i 63 | for (size_t j = 0; j < SIZE; j++) { // and j 64 | // don't optimize path from vertex to itself 65 | if (i == j) continue; 66 | // if there is no edge from i to k or from k to j skip this step 67 | if (graph[i][k] == MAX || graph[k][j] == MAX) continue; 68 | // remember the old value, debug purpose only 69 | int old = graph[i][j]; 70 | // try to optimize the path 71 | graph[i][j] = min(graph[i][j], graph[i][k] + graph[k][j]); 72 | // debug only 73 | if (old != graph[i][j]) { 74 | printf("optimizing path from %c to %c through %c (%d->%d)\n", 75 | 'A' + i, 'A' + j, 'A' + k, old, graph[i][j]); 76 | } 77 | } 78 | } 79 | 80 | int main() { 81 | 82 | /* run the algorithm */ 83 | floyd_shortest_paths(); 84 | 85 | /* print the info about all paths */ 86 | print_paths(); 87 | 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /algorithms/sorting_and_searching/easy_sorting/easy_sorting.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file easy_sorting.h 7 | * @author Ivan Filipov 8 | * @date 12.2019 9 | * @brief The most basic sorting algorithms implemented in plain C. 10 | * 11 | * @see https://en.wikipedia.org/wiki/Sorting_algorithm 12 | * @note Theory in easy_sorting.h, implementation details in easy_sorting.c 13 | */ 14 | 15 | #pragma once 16 | 17 | #include // size_t 18 | 19 | /** 20 | * @brief a simple swap function 21 | * @note in C there are no references, so using pointers 22 | * is the only approach 23 | */ 24 | void swap(int* f, int* s); 25 | 26 | /* 27 | For each sorting algorithm the following factors are considered : 28 | - time complexity (how much time does the algorithm takes) 29 | - space complexity (how much additional memory is used) 30 | - # of swaps (how many elements are substituted) 31 | - # of comparisons (how many times < or > is used) 32 | - stability (maintaining the relative order between equal elements) 33 | - adaptability (is the algorithm faster when data is nearly (fully) sorted) 34 | - known use cases 35 | */ 36 | 37 | 38 | /** 39 | * @brief Sorts an array of numbers using Bubble sort . 40 | * @param[in,out] arr: array to be sorted. 41 | * @param[in] size: number of element in the array. 42 | * 43 | * + good sides : easy to implement, STABLE, adaptive (fast when data is nearly sorted ~ O(n) 44 | * + no additional memory is needed 45 | * - bad sides : difficulty ~ O(n^2) in worst and average case, too much compare and swap operations 46 | * ! usage : only as academical example 47 | */ 48 | void bubble_sort(int arr[], size_t size); 49 | 50 | 51 | /** 52 | * @brief Sorts an array of numbers using Selection sort . 53 | * @param[in,out] arr: array to be sorted. 54 | * @param[in] size: number of element in the array. 55 | * 56 | * + good sides : the easiest to implement, no additional memory is needed 57 | * + less swaps than bubble and insertion sorts 58 | * - bad sides : NOT STABLE ( in basic implementation on array ) 59 | * - difficulty O(n^2) in best, worst and average case -> non-adaptive 60 | * ! usage : when in hurry, and the elements to be sorted are very few 61 | */ 62 | void selection_sort(int arr[], size_t size); 63 | 64 | /** 65 | * @brief Sorts an array of numbers using Insertion sort . 66 | * @param[in,out] arr: array to be sorted. 67 | * @param[in] size: number of element in the array. 68 | * 69 | * + good sides : easy to implement, STABLE, adaptive - fast when data is nearly sorted ~ O(n), 70 | * + no additional memory is needed, in average case less swaps then bubble, comparisons are fewer, except the worst case 71 | * - bad sides : difficulty ~ O(n^2) in worst ( data is sorted in the opposite way ), average case 72 | * ! usage : as a part of some "fast" sorting algorithms, applicable on linked list 73 | */ 74 | void insertion_sort(int arr[], size_t size); 75 | -------------------------------------------------------------------------------- /algorithms/sorting_and_searching/searching/search_algorithms.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file search_algorithms.c 7 | * @author Ivan Filipov 8 | * @date 12.2019 9 | * @brief Some searching algorithms implemented in plain C. 10 | * 11 | * @see https://en.wikipedia.org/wiki/Search_algorithm 12 | * @note Theory in searching_algorithms.h, implementation details in searching_algorithms.c 13 | */ 14 | 15 | #include // sqrt() 16 | 17 | #include "search_algorithms.h" 18 | 19 | int linear_search(int arr[], size_t size, int elem) { 20 | // simply traverse all elements 21 | for (size_t i = 0; i < size; i++) 22 | if (arr[i] == elem) 23 | return i; 24 | 25 | return -1; 26 | } 27 | 28 | /** 29 | * @brief binary search recursive helper 30 | * @param[in] arr: array in which to search 31 | * @param[in] left: index of the left border 32 | * @param[in] right: index of the right border 33 | * @param[in] elem: element to be searched 34 | * @note in each call only [L; R] interval of the array is used. 35 | */ 36 | static int binary_search_rec(int arr[], int left, int right, int elem) { 37 | //there is no such element 38 | if (left > right) 39 | return -1; 40 | // 41 | // calculate the middle index 42 | // int med = (L + R) / 2; // <- a bug source, because L + R can easily overflow 43 | // solution is 44 | int mid = left + (right - left) / 2; 45 | // or even better int med = L + (((unsigned int)R - (unsigned int)L) >> 1); 46 | // 47 | // is the search element on that index? 48 | if (arr[mid] == elem) 49 | return mid; 50 | // 51 | // grater than the middle element -> search in right 52 | if (arr[mid] > elem) 53 | return binary_search_rec(arr, left, mid - 1, elem); 54 | // 55 | // less than the middle element -> search in the left 56 | if (arr[mid] < elem) 57 | return binary_search_rec(arr, mid + 1, right, elem); 58 | // 59 | // won't reach here 60 | return -1; 61 | } 62 | 63 | int binary_search(int arr[], size_t size, int elem) { 64 | // binary search wrapper 65 | return binary_search_rec(arr, 0, (int)size - 1, elem); 66 | } 67 | 68 | /** 69 | * @brief jump search helper 70 | * @param[in] step: how many elements to "jump" 71 | * @see jump_search() 72 | */ 73 | static int jump_search_it(int arr[], int size, int step, int elem) { 74 | 75 | int ind = 0; 76 | // skip blocks with elements less than the searched 77 | for (;ind < size && arr[ind] < elem; ind += step) 78 | ; 79 | if (arr[ind] == elem) return ind; // are we on this element? 80 | // size of this block 81 | int blk_size = (ind + step > size) ? size - ind : step; 82 | // search from the beginning of the block, for block size elements 83 | int res_ind = linear_search(arr + ind - step, blk_size, elem); 84 | if (res_ind != -1) res_ind += (ind - step); 85 | return res_ind; 86 | } 87 | 88 | int jump_search(int arr[], size_t size, int elem) { 89 | // jump search wrapper 90 | return jump_search_it(arr, size, sqrt(size), elem); 91 | } 92 | -------------------------------------------------------------------------------- /algorithms/sorting_and_searching/list_sorting/example.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file example.cpp 7 | * @author Ivan Filipov 8 | * @date 12.2018 9 | * @brief Example usage and test of merge sort and 10 | * insertion sort on singly linked list. 11 | */ 12 | 13 | #include // std::cout 14 | #include // std::rand(), std::srand() 15 | #include // std::time() 16 | 17 | #include "../../../utils/student.h" 18 | #include "minimal_list.hpp" // minimal slightly linked list 19 | 20 | /// test merge sort on fixed linked list 21 | void test_custom_merge_sort() { 22 | 23 | std::cout << "MERGE sort testing" << std::endl 24 | << "------------------" 25 | << std::endl 26 | << std::endl; 27 | 28 | dsa::minimal_list list; 29 | 30 | for (int i = 0; i < 10; i++) 31 | list.push_back(std::rand() % 100); 32 | 33 | std::cout << "starting list items : \n"; 34 | list.print_elems(std::cout); 35 | list.sort(); 36 | std::cout << "sorted list items : \n"; 37 | list.print_elems(std::cout); 38 | 39 | 40 | dsa::minimal_list st_list = { { "Ivancho", 40000 }, { "Mariika", 25000 }, 41 | { "Gencho", 50001 }, { "Pencho", 25000 }, 42 | { "Genka", 42000 }, { "Penka", 25000 }, 43 | { "Kalin", 40000 }, { "Kalinka", 25000 } }; 44 | std::cout << "\nstarting students list : \n"; 45 | st_list.print_elems(std::cout); 46 | st_list.sort(); 47 | std::cout << "sorted list items by name (using operator<) : \n"; 48 | st_list.print_elems(std::cout); 49 | std::cout << std::endl << std::endl; 50 | } 51 | 52 | /// test insertion sort on fixed linked list 53 | void test_custom_insertion_sort() { 54 | 55 | std::cout << "Insertion sort testing" << std::endl 56 | << "------------------" 57 | << std::endl 58 | << std::endl; 59 | 60 | dsa::minimal_list list; 61 | 62 | for (int i = 0; i < 10; i++) 63 | list.push_back(std::rand() % 100); 64 | 65 | std::cout << "starting list items : \n"; 66 | list.print_elems(std::cout); 67 | list.slow_sort(); 68 | std::cout << "sorted list items : \n"; 69 | list.print_elems(std::cout); 70 | 71 | dsa::minimal_list st_list = { { "Ivancho", 40000 }, { "Mariika", 25000 }, 72 | { "Gencho", 50001 }, { "Pencho", 25000 }, 73 | { "Genka", 42000 }, { "Penka", 25000 }, 74 | { "Kalin", 40000 }, { "Kalinka", 25000 } }; 75 | std::cout << "\nstarting students list : \n"; 76 | st_list.print_elems(std::cout); 77 | st_list.slow_sort(); 78 | std::cout << "sorted list items by name (using operator<) : \n"; 79 | st_list.print_elems(std::cout); 80 | std::cout << std::endl << std::endl; 81 | } 82 | 83 | int main() { 84 | 85 | /* seed the random generator */ 86 | std::srand(std::time(nullptr)); 87 | /* test merge sort implementation */ 88 | test_custom_merge_sort(); 89 | /* test insertion sort implementation */ 90 | test_custom_insertion_sort(); 91 | 92 | return 0; 93 | } 94 | -------------------------------------------------------------------------------- /algorithms/sorting_and_searching/abstract_sorting/abstract_sorting.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file abstract_sorting.hpp 7 | * @author Ivan Filipov 8 | * @date 12.2019 9 | * @brief An example with abstract implementation of some sorting algorithms. 10 | * 11 | * They depend only on iterators and comparators, no matter what the data structure below is. 12 | * 13 | * @note The idea of this implementation is taken from @see https://en.cppreference.com 14 | */ 15 | 16 | #pragma once 17 | 18 | #include // std::swap(), std::rotate(), std::upper_bound() 19 | // std::inplace_merge(), std::make_heap(), std::sort_heap() 20 | 21 | namespace dsa { 22 | /** 23 | * @brief STL like Insertion sort 24 | * @param[in] being: iterator to the beginning of the set 25 | * @param[in] iterator to the end of the set 26 | * @param[in] cmp: comparator to be used 27 | * @tparam Iterator: type of iterator - should provide random access 28 | * @tparam Comparator: type of comparator 29 | * 30 | * for std::rotate @see https://en.cppreference.com/w/cpp/algorithm/rotate 31 | * for std::upper_bound @see https://en.cppreference.com/w/cpp/algorithm/upper_bound 32 | */ 33 | template 34 | void insertion_sort(Iterator begin, Iterator end, Comparator cmp) { 35 | 36 | for (Iterator i = begin; i != end; ++i) { 37 | std::rotate(std::upper_bound(begin, i, *i, cmp), 38 | i, i+1); 39 | } 40 | } 41 | 42 | /** 43 | * @brief STL like Merge sort 44 | * @param[in] being: iterator to the beginning of the set 45 | * @param[in] iterator to the end of the set 46 | * @param[in] cmp: comparator to be used 47 | * @tparam Iterator: type of iterator - should be at least bidirectional 48 | * @tparam Comparator: type of comparator 49 | * 50 | * for std::inplace_merge @see https://en.cppreference.com/w/cpp/algorithm/inplace_merge 51 | */ 52 | template 53 | void merge_sort(Iterator begin, Iterator end, Comparator cmp) { 54 | 55 | if (end - begin > 1) { 56 | Iterator middle = begin + (end - begin) / 2; 57 | merge_sort(begin, middle, cmp); 58 | merge_sort(middle, end, cmp); 59 | std::inplace_merge(begin, middle, end, cmp); 60 | } 61 | } 62 | 63 | /** 64 | * @brief STL like Heap sort 65 | * @param[in] being: iterator to the beginning of the set 66 | * @param[in] iterator to the end of the set 67 | * @param[in] cmp: comparator to be used 68 | * @tparam Iterator: type of iterator - should provide random access 69 | * @tparam Comparator: type of comparator 70 | * 71 | * for std::make_heap @see https://en.cppreference.com/w/cpp/algorithm/make_heap 72 | * for std::sort_heap @see https://en.cppreference.com/w/cpp/algorithm/sort_heap 73 | */ 74 | template 75 | void heap_sort(Iterator begin, Iterator end, Comparator cmp) { 76 | 77 | std::make_heap(begin, end, cmp); 78 | std::sort_heap(begin, end, cmp); 79 | } 80 | } // namespace dsa 81 | -------------------------------------------------------------------------------- /algorithms/graph_algorithms/traversal/advanced/bfs_general.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file bfs_general.cpp 7 | * @author Ivan Filipov 8 | * @date 01.2019 9 | * @brief Simply traversing a graph using breadth first search strategy. 10 | * 11 | * @see https://en.wikipedia.org/wiki/Breadth-first_search 12 | */ 13 | 14 | #include // std::printf(), std::putchar() 15 | #include // std::queue 16 | #include // std::array 17 | #include // std::vector 18 | #include // std::unordered_map 19 | 20 | /// maximum value of each vertex 21 | const unsigned int MAX_VER_VAL = 26; 22 | /// each vertex will be a character 23 | typedef char vertex; 24 | /// using lists of adjacent vertices representation of graph 25 | using graph = std::unordered_map>; 26 | 27 | /** 28 | * @brief Creates a graph with some edges. 29 | * @retval the created graph 30 | */ 31 | graph init_graph() { 32 | 33 | graph G; 34 | G['A'] = { 'B' }; 35 | G['B'] = { 'C', 'D' }; 36 | G['C'] = { 'F', 'H' }; 37 | G['D'] = { 'L' }; 38 | G['F'] = { 'I' }; 39 | G['H'] = { 'I' }; 40 | G['I'] = { 'J' }; 41 | G['J'] = { 'K' }; 42 | G['L'] = { 'H' }; 43 | 44 | return G; 45 | } 46 | 47 | /** 48 | * @brief Runs BFS algorithm in graph. 49 | * @param[in] G: input graph 50 | * @param[in] v_begin: starting from that vertex 51 | * 52 | * @note Time complexity : O((m + n)). 53 | */ 54 | void bfs_traversal(const graph& G, vertex start) { 55 | // which are visited 56 | std::array is_visited = { false, }; 57 | // the wave of the algorithm 58 | std::queue wave; 59 | // adding the start vertex 60 | wave.push(start); 61 | // until the wave is not empty 62 | while (!wave.empty()) { 63 | printf("from %c : ", wave.front()); 64 | // trying to get the adjacent list of this vertex 65 | graph::const_iterator it = G.find(wave.front()); 66 | // not found... 67 | if (it == G.end()) { 68 | std::putchar('\n'); 69 | wave.pop(); 70 | continue; 71 | } 72 | // if found, it is an iterator to a pair { vertex, adj_list } 73 | // for each adjacent of the vertex ... 74 | for (vertex adj: it->second) { 75 | // if not visited 76 | if (!is_visited[adj - 'A']) { 77 | // mark it as visited 78 | printf("%c ", adj); 79 | is_visited[adj - 'A'] = true; 80 | // add it to the wave 81 | wave.push(adj); 82 | } 83 | } 84 | putchar('\n'); 85 | // remove the front of the wave 86 | wave.pop(); 87 | } 88 | // end of algorithm 89 | std::printf("traverse completed!\n"); 90 | } 91 | 92 | 93 | int main() { 94 | 95 | /* fill the graph with some vertices and edges */ 96 | graph G = init_graph(); 97 | 98 | /* from which vertex to start the algo */ 99 | vertex start = 'A'; 100 | std::printf("\nstarting BFS traversal from vertex \'%c\'\n\n", start); 101 | 102 | /* run the algorithm */ 103 | bfs_traversal(G, start); 104 | 105 | return 0; 106 | } 107 | -------------------------------------------------------------------------------- /algorithms/numerical_algorithms/primes.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file primes.c 7 | * @author Ivan Filipov 8 | * @date 09.2018 9 | * @brief Some general algorithms for prime numbers - 10 | * check if a number is a prime, 11 | * find all primes in an interval [a, b]. 12 | * 13 | * @see https://en.wikipedia.org/wiki/Prime_number 14 | */ 15 | 16 | #include // printf(), putchar() 17 | #include // sqrt() 18 | #include // boolean type 19 | 20 | /// simple check if a number is a prime 21 | bool is_prime(int n) { 22 | 23 | if (n < 2) 24 | return false; 25 | 26 | int sq = sqrt(n); 27 | 28 | for (int i = 2; i <= sq; i++) 29 | if (n % i == 0) 30 | return false; 31 | 32 | return true; 33 | } 34 | 35 | /// slower version for finding all prime number in 36 | /// an interval [a, b] 37 | void primes_in_interval_slow(int a, int b) { 38 | 39 | for (int i = a; i < b; i++) 40 | if (is_prime(i)) 41 | printf("%d ", i); 42 | 43 | putchar('\n'); 44 | } 45 | 46 | //@{ 47 | /// constants related with the faster algorithm 48 | const int INTERVAL_SIZE = 3000; //!< below this value the difference between the faster and the slower algo is insignificant 49 | const int INTERVAL_STEP = 30; //!< math's interval 50 | #define N_STEPS 8 //!< number of prime multipliers 51 | int steps[N_STEPS] = { 1, 7, 11, 13, 17, 19, 23, 29 }; //!< prime multipliers 52 | //@} 53 | 54 | /// a bit smarter version of the algorithm for finding all primes in the interval [a, b] 55 | /// by using some math... 56 | /// every single number can be presented as 30q + p , where p is in [0,29] 57 | /// 30 = 2.3.5 , so potentially only 30q + 1; 30q + 7; 30q + 11; 30q + 13 58 | /// 30q + 29 ; 30q + 23 ; 30q + 19; 30q + 17 59 | /// can be primes 60 | void primes_in_interval_fast(int a_in, int b_in) { 61 | 62 | // if the parameters are passed in wrong order 63 | int a = (a_in < b_in) ? a_in : b_in; // min 64 | int b = (a_in > b_in) ? a_in : b_in; // max 65 | 66 | if (b - a < INTERVAL_SIZE) { 67 | primes_in_interval_slow(a, b); 68 | return; 69 | } 70 | 71 | // finding the first "30q" number 72 | int new_down = a; 73 | for (int i = a; i < a + INTERVAL_STEP; i++) { 74 | if (i % INTERVAL_STEP == 0) { 75 | new_down = i; 76 | break; 77 | } 78 | 79 | if (is_prime(i)) printf("%d ", i); 80 | } 81 | // 82 | 83 | // filter all number 30 by 30 84 | int last_checked = 0; 85 | for (int i = new_down; i < b - INTERVAL_STEP; i += INTERVAL_STEP) 86 | for (int j = 0; j < N_STEPS; j++) 87 | if (is_prime(i + steps[j])) { 88 | printf("%d ", i + steps[j]); 89 | last_checked = i + steps[j]; 90 | } 91 | // 92 | 93 | // filter the rest with the slower algorithm 94 | for (int i = last_checked + 1; i < b ; i++) 95 | if (is_prime(i)) 96 | printf("%d ", i); 97 | } 98 | 99 | 100 | int main() { 101 | 102 | /* run the slower version of the algorithm */ 103 | primes_in_interval_slow(50, 10000); 104 | 105 | printf("\n------------------------------------\n\n"); 106 | 107 | /* run the faster version of the algorithm */ 108 | primes_in_interval_fast(50, 10000); 109 | 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /algorithms/divide_and_conquer/hanoi_tower.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file hanoi_tower.cpp 7 | * @author Ivan Filipov 8 | * @date 01.2019 9 | * @brief A solution to Hanoi tower's task. 10 | * Using a recursive "divide and conquer" algorithm. 11 | * @see https://en.wikipedia.org/wiki/Tower_of_Hanoi 12 | */ 13 | 14 | #include // std::cout 15 | #include // std::stack 16 | 17 | /// each tower is a stack of numbers 18 | typedef std::stack tower; 19 | /// the three towers 20 | tower At, Bt, Ct; 21 | /// how many elements in each tower 22 | const int ECOUNT = 4; 23 | 24 | /// make a step, moving the top disk from one tower to another 25 | /// @param[in,out] src: tower from which to remove the disk 26 | /// @param[in,out] des: tower to which to add the disk 27 | void move(tower& src, tower& des) { 28 | 29 | des.push(src.top()); 30 | src.pop(); 31 | } 32 | 33 | /// get the top element of a tower, with fixed size 34 | /// @param[in] level: how much the tower size should be 35 | /// @param[in,out] t: tower from which an element will be taken 36 | /// @retval character of the number from the top of the tower, 37 | /// if the tower doesn't meet the requirement for size 38 | /// whitespace is returned 39 | char char_from_tower(int level, tower& t) { 40 | 41 | char ret; 42 | if (t.size() == level) { 43 | ret = (t.top() + '0'); 44 | t.pop(); 45 | } else { 46 | ret = ' '; 47 | } 48 | 49 | return ret; 50 | } 51 | 52 | /// simply outputs the current towers contain 53 | /// @param[in] A: a copy of the first tower 54 | /// @param[in] B: a copy of the second tower 55 | /// @param[in] C: a copy of the third tower 56 | /// @note copies are passed on purpose. 57 | void print_current(tower A, tower B, tower C) { 58 | 59 | char a, b, c; 60 | std::cout << "current towers : \n"; 61 | for(int i = ECOUNT; i > 0; i--) { 62 | a = char_from_tower(i, A); 63 | b = char_from_tower(i, B); 64 | c = char_from_tower(i, C); 65 | std::cout << "| " << a << "| | " << b << "| | " << c << "|\n" 66 | << "|__| |__| |__|\n"; 67 | } 68 | } 69 | 70 | /// the recursive algorithm for solving the problem 71 | /// @param[in,out] A: a current first tower 72 | /// @param[in,out] B: a current second tower 73 | /// @param[in,out] C: a current third tower 74 | /// @note on different steps of the algorithm the order of towers changes 75 | void solve_Hanoi(tower& A, tower& C, tower& B, int step) { 76 | 77 | if (step == 1) { 78 | move(A, C); 79 | // using the global variables, due to correct order 80 | print_current(At, Bt, Ct); 81 | return; 82 | } 83 | 84 | solve_Hanoi(A, B, C , step - 1); 85 | 86 | move(A, C); 87 | print_current(At, Bt, Ct); 88 | 89 | solve_Hanoi(B, C, A, step - 1); 90 | } 91 | 92 | 93 | int main() { 94 | 95 | /* initialize the first tower */ 96 | for (int i = ECOUNT; i > 0; --i) { 97 | At.push(i); 98 | } 99 | print_current(At, Bt, Ct); 100 | 101 | /* run the algorithm */ 102 | solve_Hanoi(At, Ct, Bt, At.size()); 103 | 104 | /* output the results */ 105 | std::cout << "\n\nresult in C \n"; 106 | for(int i = Ct.size(); i > 0; i--) { 107 | std::cout << "| " << Ct.top() << "|\n" 108 | << "|__|\n"; 109 | Ct.pop(); 110 | } 111 | 112 | return 0; 113 | } 114 | -------------------------------------------------------------------------------- /algorithms/sorting_and_searching/comparator_sorting/c_style_comparator.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file c_style_comparator.c 7 | * @author Ivan Filipov 8 | * @date 12.2019 9 | * @brief An example about usage of comparator function with sorting algorithms. C style. 10 | */ 11 | 12 | #include // printf() 13 | 14 | typedef unsigned int _type_data; //!< type of "data" fields 15 | /** 16 | * @struct elem 17 | * @brief defines elements to be sorted 18 | * 19 | * @note without this typedef we will have to write struct 20 | * keyword everywhere when we use Elem 21 | */ 22 | typedef struct { 23 | int key; //!< the key used for comparing 24 | _type_data data; //!< no matter what data we got here 25 | } elem; 26 | 27 | const int ELMS_COUNT = 6; //!< number of elements to be sorted 28 | 29 | /// given set of elements to be sorted 30 | elem elems[] = { 31 | { 10, 20 }, 32 | { 30, 14 }, 33 | { -5, 14 }, 34 | { 77, 34 }, 35 | { 24, 81 }, 36 | { 11, 15 } 37 | }; 38 | 39 | // this pointer to function typedef saves the day 40 | // without it the syntax is pretty bad 41 | // read as cmp_fptr is a pointer to function taking two elems and returning int 42 | /// pointer to compare function 43 | typedef int(*cmp_fptr) (const elem*, const elem*); 44 | 45 | /** 46 | * @brief Ordinary function for comparing two elements with (<) 47 | * @retval >0 if lhs < rhs 48 | * @retval 0 if equal 49 | * @retval <0 if lhs > rhs 50 | */ 51 | int less(const elem* lhs, const elem* rhs) { 52 | 53 | if (lhs->key < rhs->key) 54 | return 1; 55 | else if (lhs->key == rhs->key) 56 | return 0; 57 | else 58 | return -1; 59 | } 60 | 61 | /** 62 | * @brief Ordinary function for comparing two elements with (>) 63 | * @retval >0 if lhs < rhs 64 | * @retval 0 if equal 65 | * @retval <0 if lhs > rhs 66 | */ 67 | int greater(const elem* lhs, const elem* rhs) { 68 | 69 | if (lhs->key > rhs->key) 70 | return 1; 71 | else if (lhs->key == rhs->key) 72 | return 0; 73 | else 74 | return -1; 75 | } 76 | 77 | /** 78 | * @brief simple swap function 79 | * 80 | * @note valid only for bit copyable structures - 81 | * we got shallow copy here 82 | */ 83 | void swap(elem* lhs, elem* rhs) { 84 | 85 | elem temp = *lhs; 86 | *lhs = *rhs; 87 | *rhs = temp; 88 | } 89 | 90 | /** 91 | * @brief insertion sort 92 | * @param[in] arr: array to be sorted 93 | * @param[in] size: size of the array 94 | * @param[in] cmp: pointer to comparator function, to be used 95 | */ 96 | void insertion_sort(elem* arr, int size, cmp_fptr cmp) { 97 | 98 | for (int i = 1; i < size; i++) 99 | for (int j = i; j > 0; j--) 100 | if (cmp(&arr[j] , &arr[j - 1]) > 0) // the slight difference is here 101 | swap(&arr[j] , &arr[j - 1]); 102 | } 103 | 104 | /// simple output function 105 | void print_elems() { 106 | 107 | for (int i = 0; i < ELMS_COUNT; i++) 108 | printf("{ key : %d , data : %d } ", elems[i].key, elems[i].data); 109 | printf("\n\n"); 110 | } 111 | 112 | int main() { 113 | 114 | printf("given elements :\n"); 115 | print_elems(); 116 | 117 | printf("sorted with \"grater\" :\n"); 118 | insertion_sort(elems, ELMS_COUNT, greater); 119 | print_elems(); 120 | 121 | printf("sorted with \"less\" :\n"); 122 | insertion_sort(elems, ELMS_COUNT, less); 123 | print_elems(); 124 | 125 | return 0; 126 | } 127 | -------------------------------------------------------------------------------- /data_structures/tree/trie_tree/trie.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file trie.h 7 | * @author Vasilena Peycheva 8 | * @date 03.04.2019 9 | * @brief Example implementation of Trie (Prefix tree). 10 | * 11 | * @see https://en.wikipedia.org/wiki/Trie 12 | * @see https://medium.com/basecs/trying-to-understand-tries-3ec6bede0014 13 | */ 14 | 15 | #pragma once 16 | 17 | #include // std::string 18 | 19 | namespace dsa { 20 | /** 21 | * @class trie 22 | * @brief A trie is a tree-like data structure whose nodes store the letters of an alphabet. 23 | * Provide fast string searching. It is used for autocomplete, 24 | * matching algorithms, spell checkers, etc. 25 | * 26 | * Time complexity of searching, inserting and deleting from the tree 27 | * depends on the length of the word "a" : O(a). 28 | */ 29 | class trie { 30 | private: 31 | /// length of English alphabet 32 | static const size_t ALPHABET_SIZE = 26; 33 | 34 | /** 35 | * @struct node 36 | * @brief An inner representation of each trie's "node". 37 | */ 38 | struct node { 39 | node* alphabet[ALPHABET_SIZE]; //!< pointers to next symbols 40 | bool is_word; //!< is end of a word 41 | /** Creates an empty node. */ 42 | node(): is_word(false) { 43 | 44 | for (size_t i = 0; i < ALPHABET_SIZE; i++) 45 | alphabet[i] = nullptr; 46 | } 47 | }; 48 | 49 | /* private data members */ 50 | node* root; //! 0 92 | * 'z' -> 25 93 | */ 94 | static int indexation(char c); 95 | 96 | public: 97 | /** 98 | * @brief Insert word in the trie tree. 99 | * @param[in] word: the word to be inserted. 100 | * @retval true if the word is successfully inserted, false otherwise. 101 | */ 102 | bool insert(const std::string& word); 103 | 104 | /** 105 | * @brief Search for a word into the trie tree. 106 | * @param[in] word: the word to be searched. 107 | * @retval true if word is in the tree, false otherwise. 108 | */ 109 | bool search(const std::string& word); 110 | 111 | /// Clean working tree. 112 | void clean(); 113 | }; 114 | } // namespace dsa 115 | -------------------------------------------------------------------------------- /utils/benchmark.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file benchmarking.hpp 7 | * @author Ivan Filipov 8 | * @brief A basic benchmarking generic function, 9 | * for the students to test their own algorithms. 10 | * @date 10.2018 11 | */ 12 | 13 | #pragma once 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | /// marco to create a string 21 | #define fnc_name_to_str(f) #f 22 | 23 | namespace dsa { 24 | /// time formatting argument 25 | enum class T_FORMAT { 26 | F_MICRO, //!< format the time in microseconds => 1000 microseconds = 1 millisecond 27 | F_MILLI, //!< format the time in milliseconds => 1000 milliseconds = 1 second 28 | F_SEC, //!< format the time in seconds => 60 seconds = 1 minute 29 | F_MIN, //!< format the time in minutes 30 | F_ALL //!< format the time as: m-s-ms-us 31 | }; 32 | 33 | // Functor could be : 34 | // - ordinary function 35 | // - an object from class which implements operator() 36 | // - a lambda function ( we will talk about them throughout the semester ) 37 | 38 | template 39 | /** 40 | * @brief a function which receives Functor as a parameter 41 | * runs it and outputs the formatted time that running took 42 | * @tparam Functor -> function like object 43 | * @param[in] format: time formatting, @see T_FORMAT 44 | * @param[in] name: name of the tested function, to be printed into the output 45 | * @param[in] os: output stream, in which to write 46 | */ 47 | void benchmark_test_fnc(Functor f, T_FORMAT format = T_FORMAT::F_MICRO, 48 | const std::string& name = "", std::ostream& os = std::cout) { 49 | // using some dark c++ 11 magic to calculate the time 50 | // needed from the function in microseconds 51 | // 1 second = 1000* 1 millisecond = 1000 * 1000 * 1 microsecond 52 | // the running time may vary to your machine's current available resources 53 | using namespace std::chrono; 54 | 55 | // starting time 56 | steady_clock::time_point begin = steady_clock::now(); 57 | // 58 | 59 | // run the function 60 | f(); 61 | // 62 | 63 | //end time 64 | steady_clock::time_point end = steady_clock::now(); 65 | // 66 | 67 | // output human readable results 68 | if (!name.empty()) os << '[' << name << "] "; 69 | os << "running took : "; 70 | 71 | switch (format) { 72 | case T_FORMAT::F_MICRO: 73 | os << duration_cast(end - begin).count() 74 | << " microseconds" << std::endl; 75 | break; 76 | 77 | case T_FORMAT::F_MILLI: 78 | os << duration_cast(end - begin).count() 79 | << " milliseconds" << std::endl; 80 | break; 81 | 82 | case T_FORMAT::F_SEC: 83 | os << duration_cast(end - begin).count() 84 | << " seconds" << std::endl; 85 | break; 86 | 87 | case T_FORMAT::F_MIN: 88 | os << duration_cast(end - begin).count() 89 | << " minutes" << std::endl; 90 | break; 91 | 92 | case T_FORMAT::F_ALL: 93 | microseconds dur = duration_cast(end - begin); 94 | 95 | minutes mm = duration_cast(dur % hours(1)); 96 | seconds ss = duration_cast(dur % minutes(1)); 97 | milliseconds ms = duration_cast(dur % seconds(1)); 98 | microseconds us = duration_cast(dur % milliseconds(1)); 99 | 100 | os << std::setfill('0') 101 | << std::setw(2) << mm.count() << "::" 102 | << std::setw(2) << ss.count() << "::" 103 | << std::setw(3) << ms.count() << "::" 104 | << std::setw(3) << us.count() << std::endl; 105 | } 106 | // 107 | } 108 | } // namespace dsa 109 | -------------------------------------------------------------------------------- /data_structures/tree/binary_search_tree/simple_bst/tests.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file tests.h 7 | * @author Ivan Filipov 8 | * @date 02.2019 9 | * @brief Some tests for our custom BST. 10 | * 11 | * @see https://en.wikipedia.org/wiki/Binary_search_tree 12 | */ 13 | 14 | #include // rand(), srand() 15 | #include // time() 16 | #include // std::exception 17 | 18 | #include "tests.h" 19 | #include "bs_tree.h" 20 | 21 | //@{ 22 | /** some constants, change them to customize the tests */ 23 | const unsigned int NELEM = 16; //!< how many elements to insert in our tree 24 | const unsigned int MAX_KEY = 51; //!< the max value a key can have 25 | const unsigned int MAX_DATA = 101; //!< max data value is [-100;100] 26 | const unsigned int NTRY = 4; //!< numbers of tries (search/remove) 27 | //@} 28 | 29 | /** fills a tree with random data by inserting NELEM elements */ 30 | static void init(bs_tree& tree) { 31 | 32 | data_type cur_data; 33 | key_type cur_key; 34 | 35 | for (unsigned int i = 0; i < NELEM; ++i) { 36 | cur_key = rand() % MAX_KEY; 37 | cur_data = rand() % MAX_DATA; 38 | // positive or negative 39 | cur_data = ((rand() % 2) ? cur_data : -cur_data); 40 | printf("\ninserting element with key %d :\n", cur_key); 41 | 42 | try { 43 | tree.insert(cur_key, cur_data); 44 | printf("successfully inserted\n"); 45 | } catch (std::logic_error& e) { 46 | printf("error : %s\n", e.what()); 47 | } 48 | } 49 | } 50 | 51 | /** search some elements in a given tree */ 52 | static void search_test(const bs_tree& tree) { 53 | 54 | data_type value; 55 | key_type cur_key; 56 | 57 | for (unsigned int i = 0; i < NTRY; ++i) { 58 | cur_key = rand() % MAX_KEY; 59 | printf("\nsearching for element with key %d :\n", cur_key); 60 | 61 | try { 62 | value = tree.find(cur_key); 63 | printf("element found! it's value is : %d\n", value); 64 | } catch (std::logic_error& e) { 65 | printf("error : %s\n", e.what()); 66 | } 67 | } 68 | } 69 | 70 | /** remove some elements from a given tree */ 71 | static void remove_test(bs_tree& tree) { 72 | 73 | key_type cur_key; 74 | 75 | for (unsigned int i = 0; i < NTRY; ++i) { 76 | cur_key = rand() % MAX_KEY; 77 | printf("\nremoving element with key %d :\n", cur_key); 78 | 79 | try { 80 | tree.remove(cur_key); 81 | printf("successfully removed ! \n"); 82 | } catch (std::logic_error& e) { 83 | printf("error : %s\n", e.what()); 84 | } 85 | } 86 | } 87 | 88 | void run_basic_tests() { 89 | // create empty tree 90 | bs_tree tree; 91 | // add some elements 92 | tree.insert(11, 1); 93 | tree.insert(9, 1); 94 | tree.insert(15, 1); 95 | tree.insert(12, 1); 96 | tree.insert(16, 1); 97 | tree.insert(10, 1); 98 | tree.insert(5, 1); 99 | tree.insert(3, 1); 100 | tree.insert(6, 1); 101 | tree.insert(13, 1); 102 | tree.print_tree(); 103 | 104 | printf("\n tree's height = %d \n", tree.get_height()); 105 | tree.print_sorted_keys(); 106 | 107 | printf("\nremoving element with key 9\n"); 108 | tree.remove(9); 109 | tree.print_tree(); 110 | 111 | printf("\n tree's height = %d \n", tree.get_height()); 112 | tree.print_sorted_keys(); 113 | } 114 | 115 | void run_advanced_tests() { 116 | 117 | bs_tree tree; 118 | // seed the random generator 119 | srand(time(NULL)); 120 | // create random tree 121 | init(tree); 122 | tree.print_tree(); 123 | printf("\n tree's height = %d \n", tree.get_height()); 124 | tree.print_sorted_keys(); 125 | // search some random elements 126 | search_test(tree); 127 | // remove some random elements 128 | remove_test(tree); 129 | tree.print_tree(); 130 | printf("\n tree's height = %d \n", tree.get_height()); 131 | tree.print_sorted_keys(); 132 | } 133 | -------------------------------------------------------------------------------- /algorithms/graph_algorithms/traversal/advanced/dfs_general.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file dfs_general.cpp 7 | * @author Ivan Filipov 8 | * @date 01.2019 9 | * @brief Traversing a given graph using depth first search (DFS) strategy. 10 | * Searching for a path from a vertex to another. 11 | * 12 | * @see https://en.wikipedia.org/wiki/Depth-first_search 13 | */ 14 | 15 | #include // std::printf(), std::putchar() 16 | #include // std::queue 17 | #include // std::array 18 | #include // std::vector 19 | #include // std::unordered_map 20 | 21 | /// maximum value of each vertex 22 | const unsigned int MAX_VER_VAL = 26; 23 | /// each vertex will be a characte 24 | typedef char vertex; 25 | /// using lists of adjacent vertices representation of graph 26 | using graph = std::unordered_map>; 27 | /// visited array 28 | using visited = std::array; 29 | 30 | /** 31 | * @brief Creates a graph with some edges. 32 | * @retval the created graph 33 | */ 34 | graph init_graph() { 35 | 36 | graph G; 37 | G['A'] = { 'B' }; 38 | G['B'] = { 'C', 'D' }; 39 | G['C'] = { 'F', 'H' }; 40 | G['D'] = { 'L' }; 41 | G['F'] = { 'I' }; 42 | G['H'] = { 'I' }; 43 | G['I'] = { 'J' }; 44 | G['J'] = { 'K' }; 45 | G['L'] = { 'H' }; 46 | 47 | return G; 48 | } 49 | 50 | /** 51 | * @brief Recursively searching for path. 52 | * @param[in] G: the graph 53 | * @param[in] current: current vertex 54 | * @param[in] target: target vertex 55 | * @param[in,out] is_visited: markers for visited vertices 56 | * @retval true if there is a path 57 | */ 58 | bool dfs_find_rec(const graph& G, vertex current, 59 | vertex target, visited& is_visited) { 60 | // entering in this vertex 61 | printf("-> %c", current); 62 | // check if it is the target 63 | if (current == target) 64 | return true; 65 | // mark it as visited 66 | is_visited[current - 'A'] = true; 67 | // get iterator for the adjacent list 68 | graph::const_iterator it = G.find(current); 69 | // no such vertex 70 | if (it == G.end()) { 71 | putchar('\n'); 72 | return false; 73 | } 74 | // if we have a list for this vertex 75 | // for each of it's adjacent vertices 76 | for (vertex adj: it->second) { 77 | // if the adjacent is not visited 78 | if (!is_visited[adj - 'A']) { 79 | // go in the adjacent and run the algorithm from there 80 | if (dfs_find_rec(G, adj, target, is_visited)) 81 | return true; // if in the recursion we have found the target, return true 82 | // output that we are going back from an adjacent 83 | printf("<- %c\n",adj); 84 | } 85 | } 86 | // if we got here there are not path between current and target 87 | return false; 88 | } 89 | 90 | /// a wrapper around the recursive function 91 | bool dfs_find(const graph& G, vertex start, vertex target) { 92 | // create visited array 93 | visited is_visited = { false, }; 94 | // returns the result from the recursive function 95 | return dfs_find_rec(G, start, target, is_visited); 96 | } 97 | 98 | int main() { 99 | 100 | /* fill the graph with some vertices and edges */ 101 | graph G = init_graph(); 102 | 103 | /* which are the start and target vertices */ 104 | vertex start = 'A'; 105 | vertex target = 'P'; 106 | std::printf("\nsearching for vertex %c from %c using DFS\n\n", target, start); 107 | 108 | /* runs the algorithm with start and target */ 109 | if (dfs_find(G, start, target)) 110 | std::printf("\nfound vertex %c\n", target); 111 | else 112 | std::printf("\ncan't find vertex %c\n",target); 113 | 114 | /* change the target */ 115 | target = 'I'; 116 | std::printf("\n\nsearching for vertex %c from %c using DFS\n\n", target, start); 117 | 118 | /* runs the algorithm again */ 119 | if (dfs_find(G, start, target)) 120 | std::printf("\nfound vertex %c\n", target); 121 | else 122 | std::printf("\ncan't find %c\n", target); 123 | 124 | return 0; 125 | } 126 | -------------------------------------------------------------------------------- /data_structures/hash_table/linear_probing/linear_probing_hash.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file linear_probing_hash.h 7 | * @author Ivan Filipov 8 | * @date 01.2019 9 | * @brief Basic implementation of hash table, using 10 | * closed hashing (opened address) strategy - 11 | * linear probing. 12 | * Depends only on our custom dynamic array. 13 | * @see https://en.wikipedia.org/wiki/Hash_table 14 | * @see https://en.wikipedia.org/wiki/Open_addressing 15 | */ 16 | 17 | #pragma once 18 | 19 | /// toggles debug output. Value of 1 means print debug info, 0 - opposite 20 | #define DEBUG_HASH 1 21 | 22 | #include // std::string 23 | 24 | #include "../../dynamic_array/dynamic_array.hpp" // dsa::dynamic_array 25 | 26 | //@{ 27 | /** for each element we have a key and some data */ 28 | typedef std::string key_type; 29 | typedef int data_type; 30 | //@} 31 | 32 | /** 33 | * @class lin_pr_hash_table 34 | * @brief An associative container that holds key-value pairs. 35 | * Provides fast lookups, additions and removals. 36 | */ 37 | class lin_pr_hash_table { 38 | private: 39 | /** 40 | * @struct table_elem 41 | * @brief An inner representation of each table element. 42 | */ 43 | struct table_elem { 44 | key_type key = ""; //!< key 45 | data_type data = -1; //!< data 46 | }; 47 | // typedef for easier writing 48 | // same as typedef dynamic_array hash_table; 49 | using hash_table = dsa::dynamic_array; 50 | 51 | private: 52 | /* private data members */ 53 | hash_table table; //!< the whole table 54 | size_t logic_fill = 0; //!< how many elements are there 55 | static const size_t STEP = 1; //!< step for linear probing. @note gcd(STEP, size) == 1 !!! 56 | static const size_t BASE_SIZE = 4; //!< initial size to be allocated. @note small array only with example purpose 57 | public: 58 | /* object life cycle */ 59 | /** create table with @c BASE_SIZE # of empty objects */ 60 | lin_pr_hash_table(size_t size = BASE_SIZE): table(size, {}) {} 61 | // big 4? 62 | // - > the ones generated by the compiler himself will do the trick 63 | private: 64 | /* helpers */ 65 | // the hash function will be one for all hash tables 66 | /** 67 | * @brief Calculates the hash for a key. 68 | * @param[in] key: compute the hash of that key 69 | * @param[in] size: size of a hash table 70 | * @retval index in the table in which that key should belong to 71 | */ 72 | static size_t hash_func(const key_type& key, size_t size); 73 | 74 | /** helper for re-sizing the hash table */ 75 | void rehash(); 76 | 77 | public: 78 | /* interface */ 79 | /** 80 | * @brief Get an element's data by key. 81 | * @param[in] key: the key to be searched. 82 | * @retval data for that key. 83 | * @throw std::logic_error if there is no such key. 84 | * 85 | * Time complexity in the best case: O(1), 86 | * but linear in elements count in the worst case. 87 | */ 88 | data_type get(const key_type& key) const; 89 | 90 | /** 91 | * @brief Inserts a key-value pair into %lin_pr_hash_table, if the key is not presenting. 92 | * @param[in] key: the key to be inserted. 93 | * @param[in] data: data to be inserted. 94 | * @throw std::logic_error if there is already such key inside the hash table. 95 | * 96 | * Time complexity in the best case: O(1), 97 | * but linear in elements count in the worst case. 98 | */ 99 | void insert(const key_type& key, const data_type& data); 100 | 101 | /** 102 | * @brief Removes a pair by key from %lin_pr_hash_table, if the key is presenting. 103 | * @param[in] key: the key of the pair to be removed. 104 | * @throw std::logic_error if there is not a such key inside the hash table. 105 | * 106 | * Time complexity in the best case: O(1), 107 | * but linear in elements count in the worst case. 108 | */ 109 | void erase(const key_type& key); 110 | 111 | /** Prints the content of the hash table */ 112 | void print() const; 113 | }; 114 | -------------------------------------------------------------------------------- /algorithms/graph_algorithms/traversal/basic/bfs_maze.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file bfs_maze.cpp 7 | * @author Ivan Filipov 8 | * @date 01.2019 9 | * @brief Simply traversing a maze using breadth first search strategy. 10 | * 11 | * @see https://en.wikipedia.org/wiki/Breadth-first_search 12 | */ 13 | 14 | #include // std::printf(), std::putchar 15 | #include // std::exit() 16 | #include // std::time() 17 | #include // std::rand(), std::srand() 18 | #include // std::queue 19 | #include // std::array 20 | 21 | /// marker for unreachable cell 22 | const int UNREACHABLE = -1; 23 | /// marker for reachable cell 24 | const int REACHABLE = 0; 25 | // every other int -> visited 26 | 27 | /// matrix rows count 28 | const size_t MAXN = 8; 29 | /// matrix columns count 30 | const size_t MAXM = 8; 31 | /// the maze itself 32 | int maze[MAXN][MAXM]; 33 | /// each point is represented by two indexes 34 | using point = std::pair; 35 | /// each direction we can have 36 | using directions = std::array; 37 | directions dirs = { 38 | std::make_pair( 0, 1), //!< left 39 | std::make_pair( 0, -1), //!< right 40 | std::make_pair( 1, 0), //!< up 41 | std::make_pair(-1, 0) //!< down 42 | }; 43 | 44 | /// simply prints the maze content 45 | void print_maze() { 46 | 47 | for (size_t i = 0; i < MAXN; i++) { 48 | for (size_t j = 0; j < MAXM; j++) 49 | std::printf("%3d", maze[i][j]); 50 | std::putchar('\n'); 51 | } 52 | std::printf("\n\n\n"); 53 | } 54 | 55 | /// initialize maze's cells 56 | /// with 66% change -> reachable cell 57 | /// with 33% change -> unreachable cell 58 | void init_maze() { 59 | 60 | for (size_t i = 0; i < MAXN; i++) 61 | for (size_t j = 0; j < MAXM; j++) 62 | maze[i][j] = ((rand() % 30 ) < 20) - 1; 63 | } 64 | 65 | /// checks if a point is in the maze or outside 66 | bool is_valid(int x, int y) { 67 | 68 | if (x < 0 || y < 0) 69 | return false; 70 | 71 | if (x > (int)MAXN || y > (int)MAXM) 72 | return false; 73 | 74 | return true; 75 | } 76 | 77 | /** 78 | * @brief Runs BFS into the generated maze. 79 | * @param[in] start: starting vertex 80 | */ 81 | void bfs_traversal(const point& start) { 82 | // check if the starting point is reachable 83 | if (maze[start.first][start.second] == UNREACHABLE) { 84 | std::printf("can't start form this point...\n"); 85 | std::exit(0); 86 | } 87 | // marking the first step 88 | maze[start.first][start.second] = 1; 89 | // the "wave" of the algorithm 90 | std::queue wave; 91 | // adding the first vertex 92 | wave.push(start); 93 | // where is the next step 94 | int new_x, new_y; 95 | // until the wave is not empty 96 | while (!wave.empty()) { 97 | // for each direction 98 | for (const point& dir : dirs) { 99 | // get the step indexes 100 | new_x = wave.front().first + dir.first; 101 | new_y = wave.front().second + dir.second; 102 | // if we can go there 103 | if (is_valid(new_x, new_y) && maze[new_x][new_y] == REACHABLE) { 104 | // mark the step count 105 | maze[new_x][new_y] += 106 | maze[wave.front().first][wave.front().second] + 1; 107 | // add the new vertex into the wave 108 | wave.push(std::make_pair(new_x, new_y)); 109 | } 110 | } 111 | // remove the current vertex 112 | wave.pop(); 113 | } 114 | } 115 | 116 | int main() { 117 | 118 | /* initialize the random generator */ 119 | std::srand(std::time(nullptr)); 120 | 121 | /* create the maze */ 122 | init_maze(); 123 | 124 | /* output the starting maze */ 125 | std::printf("starting maze looks like :\n\n"); 126 | print_maze(); 127 | 128 | /* create a random start point */ 129 | point start(rand() % MAXN, rand() % MAXM); 130 | std::printf("starting from (%u, %u) :\n", start.first, start.second); 131 | 132 | /* run the algorithm */ 133 | bfs_traversal(start); 134 | 135 | /* output the result */ 136 | std::printf("after traversing all possible vertices from the graph\n\n"); 137 | print_maze(); 138 | 139 | return 0; 140 | } 141 | -------------------------------------------------------------------------------- /data_structures/queue/priority_queue/example.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file example.cpp 7 | * @author Gratsiela Gancheva 8 | * @author Ivan Filipov 9 | * @date 10.2018 10 | * @brief Tests and example usage of our custom priority_queue. 11 | */ 12 | 13 | #include // std::cout, std::endl 14 | #include // std::rand, std::srand 15 | #include // std::time 16 | 17 | #include "priority_queue.hpp" // dsa::priority_queue 18 | #include "../../../utils/student.h" // dsa::student 19 | 20 | /// run test on randomly filled queue 21 | void run_basic_tests() { 22 | // Create queue 23 | dsa::priority_queue pqueue; 24 | std::cout << "Successful created priority queue!\n\n"; 25 | // Test whether container is empty 26 | std::cout << "Is empty: " << ((pqueue.empty()) ? "True\n\n" : "False\n\n"); 27 | // Get size 28 | std::cout << "Size: " << pqueue.size() << "\n\n"; 29 | // Get top element 30 | try { 31 | std::cout << "Get top element: "; 32 | std::cout << pqueue.top() << std::endl; 33 | } catch (const std::out_of_range& e) { 34 | std::cout << e.what()< 64 | void feed_with_some_students(PQueue& q) { 65 | 66 | q.push({ "Ivancho", 40000 }); 67 | q.push({ "Mariika", 25000 }); 68 | q.push({ "Gencho", 50001 }); 69 | q.push({ "Pencho", 25000 }); 70 | q.push({ "Genka", 42000 }); 71 | q.push({ "Penka", 25000 }); 72 | q.push({ "Kalin", 40000 }); 73 | q.push({ "Kalinka", 25000 }); 74 | } 75 | 76 | /// run tests with custom comparator 77 | void run_advanced_tests() { 78 | 79 | // Create queue with students and custom comparator 80 | // passed as lambda function 81 | auto cmp_students_fn = [](const dsa::student& lhs, const dsa::student& rhs) { return lhs.fn < rhs.fn; }; 82 | dsa::priority_queue pqueue (cmp_students_fn); 85 | 86 | feed_with_some_students(pqueue); 87 | std::cout << "Students from queue ordered by fn :" << std::endl; 88 | int i = 0; 89 | while (!pqueue.empty()) { 90 | std::cout << "# " << i++ << pqueue.top() << std::endl; 91 | pqueue.pop(); 92 | } 93 | std::cout << std::endl << std::endl; 94 | 95 | dsa::priority_queue pnqueue; 96 | 97 | feed_with_some_students(pnqueue); 98 | std::cout << "Students from queue ordered by name (default operator<) :" << std::endl; 99 | i = 0; 100 | while (!pnqueue.empty()) { 101 | std::cout << "# " << i++ << pnqueue.top() << std::endl; 102 | pnqueue.pop(); 103 | } 104 | std::cout << std::endl << std::endl; 105 | 106 | dsa::priority_queue > squeue; 107 | 108 | feed_with_some_students(squeue); 109 | std::cout << "Students from queue ordered by name (using operator>) :" << std::endl; 110 | i = 0; 111 | while (!squeue.empty()) { 112 | std::cout << "# " << i++ << squeue.top() << std::endl; 113 | squeue.pop(); 114 | } 115 | std::cout << std::endl << std::endl; 116 | } 117 | 118 | int main() { 119 | 120 | /* test basic functionality */ 121 | run_basic_tests(); 122 | 123 | /* test advanced functionality */ 124 | run_advanced_tests(); 125 | 126 | return 0; 127 | } 128 | -------------------------------------------------------------------------------- /algorithms/graph_algorithms/shortest_path_problem/bellman–ford/bellman-ford_spp.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file bellman-ford_spp.cpp 7 | * @author Ivan Filipov 8 | * @date 01.2019 9 | * @brief Finding the shortest path from a starting vertex to all others, using Bellman-Ford's algorithm. 10 | * 11 | * @see https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm 12 | * @note Written in plain C. 13 | * 14 | * Difference from Dijkstra - edges can also have negative weights. 15 | * Also finds out if the graph contains negative cycle. 16 | */ 17 | 18 | #include // printf(), putchar() 19 | #include // bool 20 | 21 | /// graph matrix size 22 | #define SIZE 6 23 | /// "no path" constant, should be maximum INT_MAX / 2 24 | #define MAX 1000 25 | /// helper marco for finding minimum of two numbers 26 | #define min(x, y) ((x) < (y)) ? (x) : (y) 27 | 28 | /// the graph is represented as weighing matrix 29 | int graph[SIZE][SIZE] = { 30 | // vertex "to" : 31 | // A B C D E F // vertex "from" : 32 | { MAX, MAX, 6, 2, MAX, MAX }, // A 33 | { MAX, MAX, MAX, -7, MAX, MAX }, // B 34 | { MAX, 1, MAX, MAX, MAX, MAX }, // C 35 | { MAX, MAX, 11, MAX, MAX, MAX }, // D 36 | { 15, MAX, 10, MAX, MAX, -3 }, // E 37 | { MAX, 5, MAX, MAX, MAX, MAX } // F 38 | }; 39 | 40 | /// simply outputs info about all found paths 41 | void print_paths(int v_begin, int dist_vector[]) { 42 | 43 | printf("\nfrom %c to:\n", 'A' + v_begin); 44 | 45 | for (int i = 0; i < SIZE; i++) { 46 | if (v_begin == i) continue; 47 | 48 | printf("\t%c ", 'A' + i); 49 | 50 | if (dist_vector[i] == MAX) 51 | printf("no such path!\n"); 52 | else 53 | printf(" weight %d\n", dist_vector[i]); 54 | } 55 | } 56 | 57 | /** 58 | * @brief Checks for negative cycle in the output from Bellman-Ford's algo. 59 | * @param[in] dist_vector: distances between vertices, as outputted from the algo. 60 | * @retval true if there is a negative cycle 61 | * 62 | * If there are two edges (i and j) and the distance to i 63 | * is greater from the distance to j plus the edge between them 64 | * we can conclude that the graph contains a negative cycle. 65 | */ 66 | bool find_negative_cycle(int dist_vector[]) { 67 | 68 | for (int i = 0; i < SIZE; i++) 69 | for (int j = i; j < SIZE; j++) { 70 | if (graph[j][i] == MAX || dist_vector[j] == MAX) continue; 71 | if (dist_vector[i] > dist_vector[j] + graph[j][i]) 72 | return true; 73 | } 74 | 75 | return false; 76 | } 77 | 78 | /** 79 | * @brief Runs Bellman-Ford's algorithm in the given graph. 80 | * @param[in] v_begin: staring vertex 81 | * 82 | * @note outputs in an inner scoped array of distances 83 | */ 84 | void ford_shortest_paths(int v_begin) { 85 | // distance to each vertex -> the algorithm's output 86 | int dist_vector[SIZE]; 87 | // initialize distances 88 | for (int i = 0; i < SIZE; i++) 89 | dist_vector[i] = graph[v_begin][i]; 90 | 91 | // for each vertex between i and j 92 | for (size_t k = 0; k <= SIZE / 2; k++) 93 | for (size_t i = 0; i < SIZE; i++) 94 | for (size_t j = 0; j < SIZE; j++) { 95 | // don optimize path from vertex to itself 96 | if (i == j) continue; 97 | // if there is no edge from i to k or from k to j skip this step 98 | if (graph[j][i] == MAX || dist_vector[j] == MAX) continue; 99 | // try to optimize the distance 100 | if (dist_vector[i] > dist_vector[j] + graph[j][i]) 101 | dist_vector[i] = dist_vector[j] + graph[j][i]; 102 | } 103 | 104 | // check the output for negative cycle 105 | if (find_negative_cycle(dist_vector)) 106 | printf("\nthe given graph contains a negative loop!\n"); 107 | else // if there is no such cycle, output the shortest paths 108 | print_paths(v_begin, dist_vector); 109 | } 110 | 111 | int main() { 112 | 113 | /* run the algorithm from 'B' */ 114 | int v_begin = 1; 115 | ford_shortest_paths(v_begin); 116 | 117 | /* run the algorithm from 'E' */ 118 | v_begin = 4; // 'E' 119 | ford_shortest_paths(v_begin); 120 | 121 | /* change the graph a bit, and run the algorithm again */ 122 | printf("\nmodifying the graph to create a negative loop...\n"); 123 | // for better understanding see the graph's visual representation 124 | graph['D' - 'A']['C' - 'A'] = -11; 125 | ford_shortest_paths(v_begin); 126 | 127 | return 0; 128 | } 129 | -------------------------------------------------------------------------------- /algorithms/graph_algorithms/minimal_spanning_tree/kruskal/kruskal_mst.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file kruskal_mst.cpp 7 | * @author Ivan Filipov 8 | * @date 01.2019 9 | * @brief Creating minimal spanning tree from a graph, using Kruskal's algorithm. 10 | * 11 | * @see https://en.wikipedia.org/wiki/Kruskal%27s_algorithm 12 | */ 13 | 14 | #include // std::printf() 15 | #include // std::list 16 | #include // std::tuple 17 | #include // std::array 18 | 19 | using std::printf; 20 | 21 | /// max value for a vertex 22 | const int MAX_VER_VAL = 26; 23 | /// marker for unknown vertex's parent 24 | const char NO_PARENT = '?'; 25 | /// vertices are characters 26 | typedef char vertex; 27 | /// weights are numbers 28 | typedef unsigned int weight; 29 | /// each edge is a tuple - vertex : vertex : weight 30 | using edge = std::tuple; 31 | /// the graph is represented as a list of edges 32 | using graph = std::list; 33 | /// a map for vertex -> its parent 34 | using parent_list = std::array; 35 | 36 | /** 37 | * @brief Creates a graph with some edges. 38 | * @retval the created graph 39 | */ 40 | graph init_graph() { 41 | 42 | return { 43 | std::make_tuple('A', 'B', 1), 44 | std::make_tuple('A', 'D', 2), 45 | std::make_tuple('B', 'C', 3), 46 | std::make_tuple('B', 'E', 13), 47 | std::make_tuple('C', 'D', 4), 48 | std::make_tuple('C', 'F', 3), 49 | std::make_tuple('D', 'F', 16), 50 | std::make_tuple('D', 'H', 14), 51 | std::make_tuple('E', 'F', 12), 52 | std::make_tuple('E', 'I', 13), 53 | std::make_tuple('E', 'J', 1), 54 | std::make_tuple('F', 'H', 1), 55 | std::make_tuple('H', 'I', 1) 56 | }; 57 | } 58 | 59 | /** 60 | * @brief simply output a graphs contain 61 | * @param[in] G: graph to be outputted 62 | */ 63 | void print_graph(const graph& G) { 64 | 65 | for (const edge& e: G) 66 | printf("(%c, %c, %u) ", std::get<0>(e), 67 | std::get<1>(e), std::get<2>(e)); 68 | printf("\n\n"); 69 | } 70 | 71 | /** 72 | * @brief compares two edges by their weight 73 | * @param[in] e1: lhs edge 74 | * @param[in] e2: rhs edge 75 | * @retval true if e1 is heavier than e2 76 | */ 77 | bool edge_cmp(const edge& e1, const edge& e2) { 78 | 79 | return (std::get<2>(e1) < std::get<2>(e2)); 80 | } 81 | 82 | /** 83 | * @brief finds in which subtree an vertex belongs to. 84 | * @param[in] index: the index of vertex 85 | * @param[in] pl: list of parents 86 | * @retval index of the root of the subtree 87 | */ 88 | size_t find_root(size_t index, const parent_list& pl) { 89 | // until the current index is not root 90 | // go to it's father 91 | while (pl[index] != NO_PARENT) 92 | index = pl[index]; 93 | 94 | return index; 95 | } 96 | 97 | /** 98 | * @brief Creates an minimal spanning tree for a graph. 99 | * @param[in] G: input graph 100 | * @retval the MST for that graph 101 | * @note as a side effect the edges in @p G are sorted. 102 | */ 103 | graph build_kruskal_mst(graph& G) { 104 | // the result minimal spanning tree 105 | graph mst; 106 | // list of parents for each vertex 107 | parent_list pl; 108 | // initialize all in different subsets 109 | pl.fill(NO_PARENT); 110 | // we can calculate the weight of the MST 111 | unsigned int mst_w = 0; 112 | // sorting all edges depending on their weights 113 | G.sort(edge_cmp); 114 | // roots indexes 115 | size_t r1, r2; 116 | // for each edge from the sorted list 117 | for (const edge& e: G) { 118 | // the sub-set's root in which vertexes are 119 | r1 = find_root(std::get<0>(e) - 'A', pl); 120 | r2 = find_root(std::get<1>(e) - 'A', pl); 121 | // if they are not in the same subset 122 | if (r1 != r2) { 123 | // add the edge into the result set 124 | mst.push_back(e); 125 | // add the weight of this edge to total 126 | mst_w += std::get<2>(e); 127 | //join the two subsets 128 | pl[r2] = r1; 129 | } 130 | } 131 | 132 | printf("Minimal spanning tree's weight : %u\n", mst_w); 133 | return mst; 134 | } 135 | 136 | int main() { 137 | 138 | /* fill the graph with some vertices and edges */ 139 | graph G = init_graph(); 140 | 141 | printf("\nthe given graph contains:\n"); 142 | print_graph(G); 143 | 144 | /* run the algorithm */ 145 | graph mst = build_kruskal_mst(G); 146 | printf("\nthe result MST contains:\n"); 147 | print_graph(mst); 148 | 149 | return 0; 150 | } 151 | -------------------------------------------------------------------------------- /algorithms/backtracking/knight_tour.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file knight_tour.c 7 | * @author Ivan Filipov 8 | * @date 01.2019 9 | * @brief Solution to "knight's tour" problem, 10 | * as an example of backtracking algorithm. 11 | * 12 | * @see https://en.wikipedia.org/wiki/Knight%27s_tour 13 | */ 14 | 15 | #include // printf(), scanf(), putchar() 16 | #include // bool type 17 | 18 | /* constants */ 19 | #define NON_VISITED 0 //!< a marker for unvisited cell 20 | #define BOARD_MAX_SIZE 10 //!< maximum board size 21 | #define N_MOVERULES 8 //!< count of movement rules 22 | 23 | /* globals */ 24 | /// the chess board represented as matrix 25 | unsigned int board[BOARD_MAX_SIZE][BOARD_MAX_SIZE]; 26 | 27 | //@{ 28 | /** x + diff_x[i], y + diff_y[i] defines the all reachable positions from x, y */ 29 | int const diff_x[N_MOVERULES] = { 1, 1, -1, -1, 2, -2, 2, -2 }; 30 | int const diff_y[N_MOVERULES] = { 2, -2, 2, -2, 1, 1, -1, -1 }; 31 | //@} 32 | 33 | /* function definitions */ 34 | /** 35 | * @brief initializes all cells from the chess 36 | * board as non-visited. 37 | * @param[in] board_size: size of board to be initialized, should be < BOARD_MAX_SIZE 38 | */ 39 | void init(unsigned board_size) { 40 | 41 | for (unsigned i = 0; i < board_size; ++i) 42 | for (unsigned j = 0; j < board_size; ++j) 43 | board[i][j] = NON_VISITED; 44 | } 45 | 46 | /** 47 | * @brief simply output chess board's contain, 48 | * which describes the knight's tour. 49 | * @param[in] board_size: size of board to be printed, should be < BOARD_MAX_SIZE 50 | */ 51 | void print_board(unsigned board_size) { 52 | 53 | printf("\nthe knight's tour looks like :\n"); 54 | 55 | for (unsigned i = 0; i < board_size; ++i) { 56 | for (unsigned j = 0; j < board_size; ++j) 57 | printf("%3u", board[i][j]); 58 | 59 | putchar('\n'); 60 | } 61 | } 62 | 63 | /** 64 | * @brief recursive function that describes 65 | * each step of the backtracking algorithm 66 | * @param[in] x: current x coordinate of the knight 67 | * @param[in] y: current y coordinate of the knight 68 | * @param[in] step: who many steps up to here 69 | * @param[in] board_size: size of the board 70 | * @retval true, when solution is found, false else 71 | * @note when @p step == @p board_size ^ 2, a solution is found 72 | */ 73 | bool knight_next_move(unsigned x, unsigned y, unsigned step, unsigned board_size) { 74 | 75 | // marking the current cell as visited 76 | board[x][y] = step; 77 | 78 | // we have found a solution 79 | if (step == board_size * board_size) { 80 | print_board(board_size); 81 | return true; 82 | // exit(0); alternative approach is to make the function 83 | // void and just to exit the program when we find solution 84 | } 85 | 86 | int new_x, new_y; 87 | // trying each direction 88 | for (unsigned k = 0; k < N_MOVERULES; ++k){ 89 | // calculate new cell 90 | new_x = x + diff_x[k]; 91 | new_y = y + diff_y[k]; 92 | // if we are inside the board and the cell is non-visited 93 | // then we are moving our knight into it 94 | if (new_x >= 0 && new_y >= 0 && 95 | new_x < (int)board_size && new_y < (int)board_size && 96 | board[new_x][new_y] == NON_VISITED) { 97 | // go into that cell 98 | if (knight_next_move(new_x, new_y, step + 1, board_size)) 99 | return true; 100 | } 101 | } 102 | 103 | // marking the current cell as non-visited 104 | // when returning back from an unsuccessful 105 | // recursion call 106 | board[x][y] = NON_VISITED; 107 | return false; 108 | } 109 | 110 | int main() { 111 | 112 | // valid solution from size = 6, x = 0, y = 5 113 | // size = 6, x = 1, y = 5 114 | /* read inputs */ 115 | unsigned board_size, start_x, start_y; 116 | 117 | printf("board size : "); 118 | scanf("%u", &board_size); 119 | 120 | printf("\nknight X position : "); 121 | scanf("%u", &start_x); 122 | 123 | printf("\nknight Y position : "); 124 | scanf("%u", &start_y); 125 | 126 | /* validate the input */ 127 | if (board_size > BOARD_MAX_SIZE || 128 | start_x >= board_size || 129 | start_y >= board_size) { 130 | printf("invalid input!\n"); 131 | return 1; 132 | } 133 | 134 | /* initialize the board */ 135 | init(board_size); 136 | 137 | /* run the algorithm */ 138 | if(!knight_next_move(start_x, start_y, 1, board_size)) 139 | printf("solution not found!\n"); 140 | 141 | return 0; 142 | } 143 | -------------------------------------------------------------------------------- /data_structures/queue/static_queue/static_queue.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file static_queue.hpp 7 | * @author Ivan Filipov 8 | * @date 10.2018 9 | * @brief Implementation of static queue data structure as 10 | * an adapter on "cyclic" array. No dynamic memory allocations. 11 | * @see https://en.wikipedia.org/wiki/Queue_(abstract_data_type) 12 | */ 13 | 14 | #pragma once 15 | 16 | #include // exception types 17 | 18 | namespace dsa { 19 | /** 20 | * @class static_queue 21 | * @brief Stores elements in FIFO style. 22 | * @tparam T: type of elements stored 23 | * @tparam N: size of underlaying array, must be known at compile time. 24 | */ 25 | template 26 | class static_queue { 27 | private: 28 | /* private data members */ 29 | T data[N]; //!< the underlaying array 30 | size_t head; //!< index of the head 31 | size_t tail; //!< index of the tail 32 | bool is_empty; //!< boolean for emptiness 33 | 34 | public: 35 | /* object life cycle */ 36 | // no dynamic memory to handle, so no need for "the big four" - 37 | // we are OK with the compile-generated 38 | /** Creates %static_queue with no elements */ 39 | static_queue(): head(0), tail(0), is_empty(true) {}; 40 | 41 | /** 42 | * @brief %static_queue copy constructor. 43 | * @param[in] rhs: A %static_queue of identical element type, from which to copy. 44 | */ 45 | static_queue(const static_queue&) = default; 46 | 47 | /** 48 | * @brief %static_queue assignment operator. 49 | * @param[in] rhs: A %static_queue of identical element type, from which to copy. 50 | */ 51 | static_queue& operator=(const static_queue&) = default; 52 | 53 | /** Destroys an object */ 54 | ~static_queue() = default; 55 | 56 | public: 57 | /* interface */ 58 | /** 59 | * @brief Push a new element at the back of the queue. 60 | * @param[in] el: Value to be inserted 61 | * @throw std::logic_error if the queue is full 62 | * Time complexity O(1). 63 | * @note STL friendly name, but "enqueue" is preferable. 64 | */ 65 | void push(const T& el); 66 | 67 | /** 68 | * @brief Pop the front element of the queue. 69 | * @retval Copy of the front element. 70 | * @throw std::logic_error if the queue is empty 71 | * Time complexity O(1). 72 | * @note "dequeue" is generally a better name for that method. 73 | */ 74 | T pop(); 75 | 76 | /** 77 | * @brief Checks if the %static_queue is empty. 78 | * @retval boolean: whether the static_queue is empty. 79 | * Time complexity O(1). 80 | */ 81 | bool empty() const { return is_empty; }; 82 | 83 | /** 84 | * @brief Get the current size of the queue. 85 | * @retval Current size. 86 | * Time complexity O(1). 87 | */ 88 | size_t size() const; 89 | 90 | /** Resets all values */ 91 | void clear(); 92 | }; 93 | 94 | template 95 | void static_queue::push(const T& el) { 96 | 97 | // there is no space 98 | if ((tail == head) && !is_empty) 99 | throw std::logic_error("the queue is full"); 100 | // 101 | //adding the new element and move the tail 102 | data[tail++] = el; 103 | // 104 | //makes the queue cyclic 105 | if (tail >= N) 106 | tail = 0; 107 | // 108 | is_empty = false; 109 | } 110 | 111 | template 112 | T static_queue::pop() { 113 | // empty? 114 | if (is_empty) throw std::logic_error("empty queue"); 115 | // 116 | // a copy of the first element 117 | T el = data[head++]; // move the head 118 | // 119 | // make the queue cyclic 120 | if (head >= N) 121 | head = 0; 122 | // 123 | // check if the head has "caught" the tail 124 | if (head == tail) is_empty = true; 125 | // 126 | return el; 127 | } 128 | 129 | template 130 | inline size_t static_queue::size() const { 131 | // empty 132 | if (is_empty) return 0; 133 | // 134 | // full 135 | if (head == tail && !is_empty) return N; 136 | // 137 | // can't determinate so easy, we should 138 | // make a walk around 139 | size_t num_elems = 0; 140 | 141 | if (head > tail) { // we should make a new cycle 142 | num_elems += (N - head); 143 | num_elems += tail; 144 | } else { 145 | num_elems += (tail - head); 146 | } 147 | // 148 | return num_elems; 149 | } 150 | 151 | template 152 | inline 153 | void static_queue::clear() { 154 | 155 | head = 0; 156 | tail = 0; 157 | is_empty = true; 158 | } 159 | } // namespace dsa 160 | -------------------------------------------------------------------------------- /data_structures/hash_table/separate_chaining/separate_chaining_hash.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file separate_chaining_hash.cpp 7 | * @author Ivan Filipov 8 | * @date 01.2019 9 | * @brief Basic implementation of hash table, using 10 | * opened hashing (closed address) strategy - 11 | * separate chaining. 12 | * Depends only on our custom dynamic array and doubly linked list. 13 | * @see https://en.wikipedia.org/wiki/Hash_table 14 | */ 15 | 16 | #include "separate_chaining_hash.h" 17 | // include only if debugging is ON 18 | #if DEBUG_HASH == 1 19 | #include //std::cin, std::cout 20 | #endif // DEBUG_HASH 21 | 22 | // the hash function, it is the most critical zone, 23 | // the implementation below is pretty simple, consider 24 | // using better hash functions in your applications 25 | size_t sp_ch_hash_table::hash_func(const key_type& key, const size_t size) { 26 | // get the size of the string 27 | size_t result = key.size(); 28 | // add the ASCII code for each character 29 | for (unsigned char c : key) 30 | result += c; 31 | 32 | // return result % size; 33 | 34 | // but better in case the SIZE is a power of 2 35 | return result & (size - 1); 36 | } 37 | 38 | void sp_ch_hash_table::rehash() { 39 | // debug print 40 | #if DEBUG_HASH == 1 41 | std::cout << "\n...rehashing ...\n"; 42 | #endif // DEBUG_HASH 43 | // create new table 44 | sp_ch_hash_table new_table(table.size() * 2); 45 | 46 | // for each chain ... 47 | for (dsa::dlinked_list& list: table) 48 | if (!list.empty()) 49 | for(table_elem& el: list) // for each of its elements 50 | new_table.insert(el.key, el.data); // put it in the new table 51 | // which will lead to re-calculating the hash values 52 | 53 | new_table.table.swap_with(table); 54 | #if DEBUG_HASH == 1 55 | std::cout << ".............\n"; 56 | #endif // DEBUG_HASH 57 | } 58 | 59 | sp_ch_hash_table::chain_iter sp_ch_hash_table::find(size_t index, const key_type& key) { 60 | 61 | chain_iter it = table[index].begin(); 62 | while (it != table[index].end() && it->key != key) 63 | ++it; 64 | return it; 65 | } 66 | 67 | void sp_ch_hash_table::insert(const key_type& key, const data_type& data) { 68 | // calculate where to add the new element using the hash function 69 | size_t index = hash_func(key, table.size()); 70 | // check if this key is taken 71 | chain_iter it = find(index, key); 72 | if (it != table[index].end()) 73 | throw std::logic_error("this key is already taken!\n"); 74 | // check if resizing is needed 75 | if (table[index].size() >= MAX_CHAIN_SIZE) { 76 | rehash(); 77 | // calculate the hash again, because the table now have different size 78 | index = hash_func(key, table.size()); 79 | } 80 | // add the new elem as a first in the list for O(1) 81 | table[index].push_front({ key, data }); 82 | #if DEBUG_HASH == 1 83 | std::cout << "stored at hash " << index << std::endl; 84 | #endif 85 | } 86 | 87 | data_type sp_ch_hash_table::get(const key_type& key) { 88 | // calculate where to add the new element using the hash function 89 | size_t index = hash_func(key, table.size()); 90 | // iterate through the elements of the list 91 | // searching for exactly the same key 92 | chain_iter it = find(index, key); 93 | // there is not such element 94 | if (it == table[index].end()) 95 | throw std::logic_error("there isn't element with such key\n"); 96 | 97 | return it->data; 98 | } 99 | 100 | void sp_ch_hash_table::erase(const key_type& key) { 101 | // calculate where to add the new element using the hash function 102 | size_t index = hash_func(key, table.size()); 103 | // find it and get a iterator to it 104 | chain_iter it = find(index, key); 105 | // can't find it 106 | if (it == table[index].end()) 107 | throw std::logic_error("there isn't element with such key\n"); 108 | // remove it for O(1) 109 | table[index].remove(it); 110 | } 111 | 112 | void sp_ch_hash_table::print() { 113 | #if DEBUG_HASH == 1 114 | for (size_t i = 0; i < table.size(); i++) { 115 | std::cout << "hash = " << i << " :"; 116 | if (table[i].empty()) { 117 | std::cout << "{}" << std::endl; 118 | continue; 119 | } 120 | std::cout << "{ "; 121 | size_t j = 0; 122 | for (chain_iter it = table[i].begin(); it != table[i].end(); ++it) { 123 | std::cout << "{ key: " << it->key << ", " 124 | << " data: " << it->data << " }"; 125 | if (j != table[i].size() -1) std::cout << ", "; 126 | j++; 127 | } 128 | std::cout << " }" << std::endl; 129 | 130 | } 131 | #endif // DEBUG_HASH 132 | } 133 | -------------------------------------------------------------------------------- /data_structures/hash_table/template_hash_map/example.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file example.cpp 7 | * @author Ivan Filipov 8 | * @date 01.2019 9 | * @brief Example usage and test of our custom hash map. 10 | */ 11 | 12 | 13 | #include // std::cout 14 | #include // std::string 15 | #include // std::srand(), rand() 16 | #include // std::time() 17 | 18 | #include "hash_map.hpp" // dsa::hash_map 19 | 20 | /// test basic interface 21 | void run_basic_tests() { 22 | 23 | std::cout << "#### running basic tests ####\n"; 24 | 25 | dsa::hash_map s_map; 26 | // inserting some students 27 | s_map[46489] = "Sashko"; 28 | s_map[12345] = "Hacker"; 29 | s_map[46123] = "Clang_master"; 30 | s_map[41234] = "Sbndio"; 31 | s_map[46289] = "Misho"; 32 | s_map[46255] = "Dakomir"; 33 | s_map[47123] = "Kriskchakis"; 34 | s_map[48234] = "Sandio"; 35 | // output the map 36 | using student_info = std::pair; 37 | std::cout << "\ncurrent contain:\n\n"; 38 | std::vector vec = s_map.to_vector(); 39 | for (const student_info& s : vec) { 40 | std::cout << "{ key: " << s.first << ", value: " 41 | << s.second << " }" << std::endl; 42 | } 43 | 44 | std::cout << "\nchanging data for key 12345:" << std::endl;; 45 | s_map[12345] = "Semeshko"; 46 | std::cout << "new data: " << s_map[12345]; 47 | std::cout << std::endl; 48 | 49 | try { 50 | std::cout << "\nsearching for 46230" << std::endl; 51 | std::string res = s_map.find(46230); 52 | std::cout << "found: " << res << std::endl; 53 | } catch(const std::exception& e) { 54 | std::cout << e.what() << std::endl; 55 | } 56 | 57 | try { 58 | std::cout << "\nsearching for 46255" << std::endl; 59 | std::string res = s_map.find(46255); 60 | std::cout << "found: " << res << std::endl; 61 | std::cout << "removing him..."; 62 | s_map.erase(46255); 63 | std::cout << "done!" << std::endl; 64 | } catch(const std::exception& e) { 65 | std::cout << e.what() << std::endl; 66 | } 67 | 68 | try { 69 | std::cout << "\ntrying to remove 51230..." << std::endl; 70 | s_map.erase(51230); 71 | std::cout << "done!" << std::endl; 72 | } catch(const std::exception& e) { 73 | std::cout << e.what() << std::endl; 74 | } 75 | 76 | try { 77 | std::cout << "\ntrying to insert { 47123, Java Lover }..." << std::endl; 78 | s_map.insert(std::make_pair(47123, "Java Lover")); 79 | std::cout << "done!" << std::endl; 80 | } catch(const std::exception& e) { 81 | std::cout << e.what() << std::endl; 82 | std::cout << "Sorry, Java Lover, there is no room for you here!" << std::endl; 83 | } 84 | 85 | std::cout << "\ncurrent contain:\n\n"; 86 | std::vector vec_new = s_map.to_vector(); 87 | for (const student_info& s: vec_new) { 88 | std::cout << "{ key: " << s.first << ", value: " 89 | << s.second << " }" << std::endl; 90 | } 91 | } 92 | 93 | /// test rehashing 94 | void run_advanced_tests() { 95 | 96 | std::cout << "\n\n#### running advanced tests ####\n"; 97 | 98 | dsa::hash_map s_map; 99 | 100 | std::cout << "current chains count: " 101 | << s_map.chain_count() 102 | << std::endl 103 | << "maximum length of each chain: " 104 | << s_map.chain_max_size() 105 | << std::endl; 106 | 107 | std::cout << "\n...let's check Dirichlet's principle...\n"; 108 | size_t max_elems_count = s_map.chain_count() * s_map.chain_max_size() + 200; 109 | 110 | std::cout << "going to insert " 111 | << max_elems_count 112 | << " random {key, data} elements into the map" 113 | << std::endl 114 | << "with total capacity: " << max_elems_count - 200 115 | << std::endl; 116 | 117 | std::srand(std::time(nullptr)); 118 | size_t cnt = 0; 119 | for (size_t i = 0; i < max_elems_count; i++) 120 | try { 121 | s_map.insert(std::make_pair(std::rand() % 100000, std::rand())); 122 | ++cnt; 123 | } catch(...) {} 124 | 125 | std::cout << "\ntotal inserted: " << cnt << std::endl; 126 | size_t new_cap = s_map.chain_count() * s_map.chain_max_size(); 127 | if (max_elems_count - 200 < new_cap) { 128 | std::cout << "\ndefinitely resized!" 129 | << std::endl 130 | << "new capacity: " 131 | << new_cap 132 | << std::endl; 133 | } else { 134 | std::cout << "no resize?!?!?!" << std::endl; 135 | } 136 | } 137 | 138 | int main() { 139 | 140 | /* test basic functionality */ 141 | run_basic_tests(); 142 | 143 | /* test advanced functionality (rehashing) */ 144 | run_advanced_tests(); 145 | 146 | return 0; 147 | } 148 | -------------------------------------------------------------------------------- /schedule: -------------------------------------------------------------------------------- 1 | 1. Introduction to the idea of the course, 2 | connections with other courses, 3 | review of the themes. 4 | 5 | 2. Introduction to algorithms. 6 | 7 | - GCD (Euclidean division algorithm ) 8 | - prime number 9 | - primes less than N (Sieve of Eratosthenes) 10 | - prime numbers in [a,b] 11 | - combinatorics : generation of permutations/variations 12 | -- calculate 13 | (n) 14 | ( ) = ? 15 | (k) 16 | using Pascal's triangle 17 | - generation of all possible sums for a natural number 18 | 19 | 3. Introduction to data structures. Dynamic array. 20 | 21 | - time/space complexity 22 | - big O notation. 23 | - some familiar functions' growth 24 | - template container classes 25 | - vector's realization 26 | - iterator 27 | - search in unsorted/sorted 28 | - time complexity of all operations over vector 29 | 30 | 4. Linked list. 31 | 32 | - all kinds - with one/two links, cyclic 33 | - realization + iterator 34 | - comparison with dynamic array 35 | -stack + static realization 36 | 37 | 5. Stack / Queue. 38 | 39 | - adaptors to vector or linked list 40 | - applications 41 | - stack - static/dynamic (on vector/list) realization 42 | - queue - dynamic/static realization 43 | - deque, priority queue - general idea 44 | 45 | 6. Sort / search algorithms. 46 | 47 | - properties 48 | (stable or not, # of comparisons, # of swaps, adaptive or not) 49 | 50 | - implementation of some basic sorting algorithms: 51 | Bubble sort, Selection sort, Insertion sort 52 | 53 | - searching algorithms: 54 | Linear search, Jump search, Binary search 55 | 56 | 7. Sorting /part two/. 57 | 58 | - advanced sorting algorithms and their implementations: 59 | Quick sort, Merge sort, Heap sort 60 | - algorithms for sorting a linked list 61 | 62 | - sorting algorithms without comparisons: 63 | Counting sort, Bucket sort, Radix sort 64 | 65 | - partial sorting 66 | * after learning heap sort, we can implement 67 | binary heap and priority queue 68 | 69 | 8. Trees. 70 | 71 | - conception, recursive definitions, applications 72 | 73 | - binary search tree + implementation: 74 | insert, remove, search, traverse, 75 | height, leafs counting 76 | 77 | - trees with random count of subtrees 78 | 79 | *9. Trees /part two/. 80 | 81 | - prefix tree (trie) 82 | 83 | - *balanced / ideal balanced trees: 84 | -- definition, examples : 85 | 86 | - Fibonacci's tree, Binary heap 87 | 88 | - 2-3-4 Tree - idea 89 | 90 | - Red Black tree - applications + 91 | implementation of Left-Leaning Red Black tree 92 | 93 | - AVL tree - idea 94 | 95 | - B-tree idea, applications 96 | 97 | 10. Hash functions, hash tables. 98 | 99 | - associative data structures + operations complexity 100 | 101 | - Hash function, examples: 102 | mod(array size), 103 | sum of ASCII codes + string's length 104 | 105 | - collision resolution strategies : 106 | closed - linear step (Linear probing), 107 | square step, 108 | multiple hash functions 109 | 110 | opened - lists for each index (Separate chaining) 111 | 112 | - examples, applications 113 | 114 | - some notable hash functions : 115 | CRC, MD, SHA 116 | 117 | 11. Graphs. 118 | 119 | - representation: 120 | list of edges, adjacency matrix(weight), 121 | adjacency lists 122 | 123 | - differences, applications 124 | 125 | - path searching/traversal: 126 | BFS/DFS 127 | 128 | 12. Graphs /part two/. 129 | 130 | - Minimal spanning tree algorithms: 131 | Prim, Kruskal 132 | 133 | 13. Graphs /part three/. 134 | 135 | - Shortest path problem: 136 | -- unweighted graph: BFS 137 | -- weighted graph: 138 | 139 | Dijkstra -> shortest path from "s" to all others 140 | /without negative weights/ 141 | 142 | Floyd - Warshall -> shortest path between each pair of vertices 143 | 144 | Bellman - Ford -> shortest path from "s" to all others 145 | /slower than Dijkstra, but supports negative weights/ 146 | 147 | *14. Algorithm classes, examples. 148 | 149 | - "Backtracking": 150 | -- knight tour 151 | -- 8 queens problem 152 | -- "branch and bound" method 153 | 154 | - "Divide and Conquer": 155 | -- done: ("Quick", "Merge" sort) 156 | -- fast pow 157 | -- Hanoi towers 158 | 159 | - "Dynamic programming": 160 | -- done: Prim, Kruskal, Dijkstra 161 | -- alan and bob's task 162 | -- linear optimization tasks 163 | 164 | 165 | - "Greedy and heuristic": 166 | -- done: Prim and Kruskal (greedy) 167 | -- random search 168 | -- lectures scheduler 169 | -- modified BFS & DSF: 170 | Depth bound search, Iterative deepening 171 | Best first search, Beam search, Hill climbing search, A* 172 | 173 | - "Cryptographic algorithms" 174 | 175 | - "Compression algorithms": 176 | -- Huffman coding 177 | 178 | 15. Summary/Recap. Questions. Projects discussion. Best practice guides. 179 | -------------------------------------------------------------------------------- /algorithms/graph_algorithms/traversal/basic/dfs_maze.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file dfs_maze.c 7 | * @author Ivan Filipov 8 | * @date 01.2019 9 | * @brief Simply traversing a maze using depth first search strategy. 10 | * 11 | * @see https://en.wikipedia.org/wiki/Depth-first_search 12 | * 13 | * Searching a path between two cells - from (0, 0) to (ROWS - 1, COLS - 1). 14 | * Nicely animated in the console. 15 | * 16 | * @note Written in plain C. 17 | */ 18 | 19 | #include // printf() 20 | #include // rand(), srand() 21 | #include // time() 22 | #include // bool 23 | 24 | //@{ 25 | /// some constants for easier cell recognition 26 | #define LAVA -2 //!< lava 27 | #define UNVGND -1 //!< unvisited ground 28 | #define VGND 4 //!< visited by not in the path 29 | #define REACHED_TARGET 5 //!< marker for reached target 30 | //@} 31 | 32 | //@{ 33 | /// some characters for beautiful output 34 | #define LAVA_SYM '#' 35 | #define FINISH_SYM '$' 36 | #define UNVGND_SYM ' ' 37 | #define VGND_SYM '-' 38 | //@} 39 | 40 | /// maze rows count 41 | #define ROWS 4 42 | /// maze columns count 43 | #define COLS 4 44 | /// the maze itself 45 | int maze[ROWS][COLS]; 46 | 47 | /// number of directions 48 | #define N_DIR 4 49 | //@{ 50 | /// difference by x and y for each direction 51 | int diff_x[] = { 0, 1, 0, -1 }; 52 | int diff_y[] = { 1, 0, -1, 0 }; 53 | char dir_sym[] = { '>', 'v', '<', '^' }; 54 | //@} 55 | 56 | /// initialize maze's cells 57 | /// with 66% chance -> available cell (UNVGND) 58 | /// with 33% chance -> unavailable cell (LAVA) 59 | void init_maze() { 60 | 61 | for (size_t i = 0; i < ROWS; i++) 62 | for (size_t j = 0; j < COLS; j++) 63 | maze[i][j] = ((rand() % 30) < 20) - 2; // results in -2 or -1 64 | // mark the target reachable 65 | maze[ROWS - 1][COLS - 1] = UNVGND; 66 | } 67 | 68 | /// simple delay function in C - style 69 | /// used to set delay between printing each step 70 | void delay(unsigned secs) { 71 | 72 | time_t ret_time = time(NULL) + secs; 73 | 74 | while (time(NULL) < ret_time) 75 | ; 76 | } 77 | 78 | /// simply outputs the maze content 79 | void print_maze() { 80 | 81 | for (size_t i = 0; i < ROWS; i++) { 82 | for (size_t j = 0; j < COLS; j++) { 83 | switch (maze[i][j]) { 84 | case LAVA: 85 | printf("%2c", LAVA_SYM); break; 86 | case REACHED_TARGET: 87 | printf("%2c", FINISH_SYM); break; 88 | case UNVGND: 89 | printf("%2c", UNVGND_SYM); break; 90 | case VGND: 91 | printf("%2c", VGND_SYM); break; 92 | default: printf("%2c", dir_sym[maze[i][j]]); 93 | } 94 | } 95 | putchar('\n'); 96 | } 97 | printf("\n\n\n"); 98 | delay(1); 99 | } 100 | 101 | /// checks if a point is in the maze or outside 102 | bool is_valid(int x, int y) { 103 | 104 | if (x < 0 || y < 0) 105 | return false; 106 | 107 | if (x >= ROWS || y >= COLS) 108 | return false; 109 | 110 | return true; 111 | } 112 | 113 | /** 114 | * @brief Runs recursively DFS into the generated maze. 115 | * @param[in] x: current x coordinate 116 | * @param[in] y: current y coordinate 117 | * @param[in] dir: from which direction we are coming 118 | */ 119 | bool dfs_find(size_t x , size_t y, int dir) { 120 | 121 | // checks if we have reached the target 122 | if ((x == ROWS - 1) && (y == COLS - 1)) { 123 | maze[x][y] = REACHED_TARGET; 124 | print_maze(); 125 | return true; 126 | } 127 | // else marking this cell as visited 128 | // from the direction passed 129 | maze[x][y] = dir; 130 | // current maze look 131 | print_maze(); 132 | // where to make the next step 133 | int new_x, new_y; 134 | // trying all directions 135 | for (int i = 0; i < N_DIR; i++) { 136 | // calc next step 137 | new_x = x + diff_x[i]; 138 | new_y = y + diff_y[i]; 139 | // if we can get there 140 | if (is_valid(new_x, new_y) && 141 | ((maze[new_x][new_y] == UNVGND) || 142 | (maze[new_x][new_y] == REACHED_TARGET))) { 143 | // try to find path from there 144 | if (dfs_find(new_x, new_y, i)) 145 | return true; 146 | else { // if this step wasn't winning cut that path 147 | maze[new_x][new_y] = VGND; 148 | print_maze(); 149 | } 150 | } 151 | } 152 | 153 | return false; 154 | } 155 | 156 | 157 | int main() { 158 | 159 | /* initialize the random generator */ 160 | srand(time(NULL)); 161 | /* create the maze */ 162 | init_maze(); 163 | /* output the starting maze */ 164 | print_maze(); 165 | /* run the algorithm */ 166 | if (dfs_find(0, 0, 0)) 167 | printf("there is a path !\n"); 168 | else 169 | printf("there isn't a path !\n"); 170 | 171 | return 0; 172 | } 173 | -------------------------------------------------------------------------------- /algorithms/sorting_and_searching/comparator_sorting/c++_style_comparator.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file c++_style_comparator.cpp 7 | * @author Ivan Filipov 8 | * @date 12.2019 9 | * @brief An example about usage of comparator function with sorting algorithms. C++ style. 10 | */ 11 | 12 | #include // std::cout 13 | #include // std::swap 14 | #include // std::grater, std::less 15 | 16 | #include "../../../utils/student.h" // class for students 17 | #include "../../../data_structures/dynamic_array/dynamic_array.hpp" // custom vector 18 | 19 | 20 | using dsa::student; // include name 21 | // typedef like modern syntax 22 | using student_vec = dsa::dynamic_array; 23 | 24 | /** 25 | * @brief insertion sort 26 | * @param[in] arr: array to be sorted 27 | * @param[in] cmp: comparator to be used 28 | * @tparam Comparator: type of comparator 29 | */ 30 | template 31 | void insertion_sort(student_vec& arr, Comparator cmp) { 32 | 33 | int size = arr.size(); 34 | for (int i = 1; i < size; i++) 35 | for (int j = i; j > 0; j--) 36 | if (cmp(arr[j], arr[j - 1])) // the slight difference is here 37 | std::swap(arr[j] , arr[j - 1]); 38 | } 39 | 40 | /** 41 | * @brief STL like insertion sort 42 | * @param[in] being: iterator to the beginning of the set 43 | * @param[in] iterator to the end of the set 44 | * @param[in] cmp: comparator to be used 45 | * @tparam Iterator: type of iterator - should provide random access 46 | * @tparam Comparator: type of comparator 47 | * 48 | * @note using iterators a even better abstraction above the raw data is achieved. 49 | */ 50 | template 51 | void insertion_sort_iter(Iterator begin, Iterator end, Comparator cmp) { 52 | 53 | for (Iterator i = begin + 1; i < end; ++i) 54 | for (Iterator j = i; j > begin; --j) 55 | if (cmp(*j, *(j - 1))) // slight difference is here 56 | std::swap(*j , *(j - 1)); 57 | } 58 | 59 | /// simple compare function for two students 60 | bool old_style_cmp(const student& lhs, const student& rhs) { 61 | 62 | return lhs.fn < rhs.fn; 63 | } 64 | 65 | /// test function for @see insertion_sort() 66 | void test_template_comparator() { 67 | 68 | std::cout << "template comparator testing" << std::endl 69 | << "------------------" 70 | << std::endl 71 | << std::endl; 72 | 73 | student_vec st_vec = { { "Ivancho", 40000 }, { "Mariika", 25000 }, 74 | { "Gencho", 50001 }, { "Pencho", 25000 }, 75 | { "Genka", 42000 }, { "Penka", 25000 }, 76 | { "Kalin", 40000 }, { "Kalinka", 25000 } }; 77 | 78 | std::cout << "\ngiven elements :\n"; 79 | st_vec.print_elems(std::cout); 80 | 81 | // sorting using operator<, std::grater is a shortcut for writing that function 82 | std::cout << "\n\nsorted by operator > for names :\n"; 83 | insertion_sort(st_vec, std::greater()); 84 | st_vec.print_elems(std::cout); 85 | 86 | std::cout << "\n\nsorted by operator > for names :\n"; 87 | insertion_sort(st_vec, std::less()); 88 | st_vec.print_elems(std::cout); 89 | 90 | std::cout << "\n\nsorted by old-style compare function for fn :\n"; 91 | insertion_sort(st_vec, old_style_cmp); 92 | st_vec.print_elems(std::cout); 93 | 94 | std::cout << "\n\nsorted by custom lambda function for fn :\n"; 95 | insertion_sort(st_vec, [] (const student& lhs, const student& rhs) -> bool { 96 | return lhs.fn > rhs.fn; 97 | }); 98 | 99 | st_vec.print_elems(std::cout); 100 | std::cout << "------------------" 101 | << std::endl; 102 | } 103 | 104 | /// test function for @see insertion_sort_iter() 105 | void test_template_cmp_iter() { 106 | 107 | std::cout << "template comparator and iterator testing" << std::endl 108 | << "------------------" 109 | << std::endl; 110 | 111 | dsa::dynamic_array arr = { 255, -124, 5, 11, 2, 6, 7 , -42, 13, 88, 21, 9, 8 }; 112 | 113 | std::cout << "\ngiven elements :\n"; 114 | arr.print_elems(std::cout); 115 | 116 | // sorting using operator<, std::grater is a shortcut for writing that function 117 | std::cout << "\n\nsorted by operator > :\n"; 118 | insertion_sort_iter(arr.begin(), arr.end(), std::greater()); 119 | arr.print_elems(std::cout); 120 | // sorting with lambda 121 | std::cout << "\n\nsorted by lambda (<) :\n"; 122 | insertion_sort_iter(arr.begin(), arr.end(), [] (int x, int y) { return x < y; }); 123 | arr.print_elems(std::cout); 124 | } 125 | 126 | int main() { 127 | 128 | /* testing the sorting version with template comparator */ 129 | test_template_comparator(); 130 | /* testing the version with both template comparator and iterators */ 131 | test_template_cmp_iter(); 132 | 133 | return 0; 134 | } 135 | -------------------------------------------------------------------------------- /data_structures/hash_table/separate_chaining/separate_chaining_hash.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file separate_chaining_hash.h 7 | * @author Ivan Filipov 8 | * @date 01.2019 9 | * @brief Basic implementation of hash table, using 10 | * opened hashing (closed address) strategy - 11 | * separate chaining. 12 | * Depends only on our custom dynamic array and doubly linked list. 13 | * @see https://en.wikipedia.org/wiki/Hash_table 14 | */ 15 | 16 | #pragma once 17 | 18 | /// toggles debug output. Value of 1 means print debug info, 0 - opposite 19 | #define DEBUG_HASH 1 20 | 21 | #include // std::string 22 | 23 | #include "../../dynamic_array/dynamic_array.hpp" // dsa::dynamic_array 24 | #include "../../linked_list/doubly_linked_list/dlinked_list.hpp" // dsa::dlinked_list 25 | 26 | //@{ 27 | /** for each element we have a key and some data */ 28 | typedef std::string key_type; 29 | typedef int data_type; 30 | //@} 31 | 32 | /** 33 | * @class sp_ch_hash_table 34 | * @brief An associative container that holds key-value pairs. 35 | * Provides fast lookups, additions and removals. 36 | */ 37 | class sp_ch_hash_table { 38 | private: 39 | /** 40 | * @struct table_elem 41 | * @brief An inner representation of each table element. 42 | */ 43 | struct table_elem { 44 | key_type key; //!< key 45 | data_type data; //!< data 46 | /** Creates a table element by key and data */ 47 | table_elem(const key_type& key = "", const data_type& data = -1) : 48 | key(key), data(data) {} 49 | }; 50 | // typedef for easier writing 51 | // same as typedef dynamic_array> hash_table; 52 | using hash_table = dsa::dynamic_array>; 53 | // same for internal iterators 54 | using chain_iter = dsa::dlinked_list::iterator; 55 | private: 56 | /* private data members */ 57 | hash_table table; //!< the whole table 58 | static const size_t MAX_CHAIN_SIZE = 3; //!< maximum size of each chain. @note short chains with example purpose only 59 | static const size_t BASE_SIZE = 2; //!< initial size to be allocated. @note small array only with example purpose 60 | 61 | public: 62 | /* object life cycle */ 63 | /** create table with @c BASE_SIZE # of empty chains */ 64 | sp_ch_hash_table(size_t size = BASE_SIZE): table(size, {}) {} 65 | // big 4? 66 | // - > the ones generated by the compiler himself will do the trick 67 | private: 68 | /* helpers */ 69 | // the hash function will be one for all hash tables 70 | /** 71 | * @brief Calculates the hash for a key. 72 | * @param[in] key: compute the hash of that key 73 | * @param[in] size: size of a hash table 74 | * @retval index in the table in which that key should belong to 75 | */ 76 | static size_t hash_func(const key_type& key, size_t size); 77 | 78 | /** helper for re-sizing the hash table */ 79 | void rehash(); 80 | /** 81 | * @brief helper for searching in a linked list 82 | * @param[in] index: index of the chain - the list to be searched 83 | * @param[in] key: key to be searched for 84 | * @retval iterator to the element 85 | * @retval iterator to %end(), if an element with that key does not exist. 86 | * @note should be const, but we don't support const_iterator 87 | * and the syntax will be too complicated 88 | */ 89 | chain_iter find(size_t index, const key_type& key); 90 | 91 | public: 92 | /* interface */ 93 | /** 94 | * @brief Get an element's data by key. 95 | * @param[in] key: the key to be searched. 96 | * @retval data for that key. 97 | * @throw std::logic_error if there is no such key. 98 | * 99 | * Time complexity in the best case: O(1), 100 | * but linear in @c MAX_CHAIN_SIZE in the worst case. 101 | */ 102 | data_type get(const key_type& key); 103 | 104 | /** 105 | * @brief Inserts a key-value pair into %sp_ch_hash_table, if the key is not presenting. 106 | * @param[in] key: the key to be inserted. 107 | * @param[in] data: data to be inserted. 108 | * @throw std::logic_error if there is already such key inside the hash table. 109 | * 110 | * Time complexity in the best case: O(1), 111 | * but linear in @c MAX_CHAIN_SIZE in the worst case. 112 | */ 113 | void insert(const key_type& key, const data_type& data); 114 | 115 | /** 116 | * @brief Removes a pair by key from %sp_ch_hash_table, if the key is presenting. 117 | * @param[in] key: the key of the pair to be removed. 118 | * @throw std::logic_error if there is not a such key inside the hash table. 119 | * 120 | * Time complexity in the best case: O(1), 121 | * but linear in elements count in the worst case. 122 | */ 123 | void erase(const key_type& key); 124 | 125 | /** Prints the content of the hash table */ 126 | void print(); 127 | }; 128 | -------------------------------------------------------------------------------- /algorithms/sorting_and_searching/non_comparison_sorting/non_comparison_sorting.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file non_comparison_sorting.cpp 7 | * @author Ivan Filipov 8 | * @author Nikolay Babulkov 9 | * @date 12.2019 10 | * @brief Some sorting algorithms, than use NO comparison operations. 11 | * 12 | * @see https://en.wikipedia.org/wiki/Sorting_algorithm#Non-comparison_sorts 13 | * @note Theory in non_comparison_sorting.h, implementation details in non_comparison_sorting.cpp 14 | */ 15 | 16 | #include // std::queue 17 | #include // std::array 18 | #include // std::swap(), std::max_element() 19 | 20 | #include "non_comparison_sorting.h" 21 | 22 | // unsigned chars have values in [0 , 255] 23 | const size_t MAX_VAL = 256; 24 | void counting_sort(std::vector& arr) { 25 | // here we will count each number's frequency 26 | // int freq[MAX_VAL + 1] = { 0 , }; 27 | std::array freq; 28 | freq.fill(0); 29 | // counting step 30 | for (size_t i = 0; i < arr.size(); i++) 31 | freq[arr[i]]++; 32 | // 33 | // pushing numbers back into the input array 34 | size_t j = 0; 35 | for (size_t i = 0; i < MAX_VAL; i++) 36 | while (freq[i]--) 37 | arr[j++] = i; 38 | // 39 | } 40 | 41 | // English alphabet + '\0' 42 | int const ALPHA_COUNT = 27; 43 | // building kind of radix tree to sort the strings 44 | // going from the most significant 'digit'/char in our case/ (MSD) 45 | // because we have a lexicographic sort 46 | void msd_strings_radix_sort(std::vector& strings, int low, int high, int pos) { 47 | 48 | if (high <= low) 49 | return; 50 | // a queue for each letter and '\0' symbol 51 | std::queue buckets[ALPHA_COUNT]; 52 | // placement index 53 | int bucket_ind; 54 | for (int j = low; j < high; ++j) { 55 | // index of the queue where we should add the current name 56 | bucket_ind = strings[j][pos] ? strings[j][pos] - 'a' + 1 : 0; 57 | buckets[bucket_ind].push(strings[j]); 58 | } 59 | // indexes of the sorted part 60 | int start_ind = low; 61 | int finish_ind = low; 62 | // for each bucket 63 | for (int j = 0; j < ALPHA_COUNT; ++j) { 64 | // put back all strings sorted by first letter 65 | while (!buckets[j].empty()) { 66 | strings[finish_ind++] = buckets[j].front(); 67 | buckets[j].pop(); 68 | } 69 | // sort all from current bucket 70 | // based on second letter 71 | if (finish_ind > low + 1) 72 | msd_strings_radix_sort(strings, start_ind, finish_ind, pos + 1); 73 | // for the next iteration 74 | start_ind = finish_ind; 75 | } 76 | } 77 | 78 | 79 | 80 | // BASE of the numerical system, we use to split the numbers 81 | // Example: BASE = 10: 15 = [1, 5] 82 | // BASE = 2 : 10 = [1, 0, 1, 0] 83 | // BASE = 8 : 85 = [1, 2, 5] 84 | const int BASE = 10; 85 | 86 | /// Counting sort with additional memory, a helper step for LSD radix 87 | ///----------------------------------------------------------------- 88 | /// It sorts the array by the 'i'-th digit, when 10^i == mask 89 | /// + good sides : O(n + k) 90 | /// - bad sides : O(n + k) additional memory 91 | void lsd_counting_sort(const std::vector& arr, std::vector& output, int mask) { 92 | // initialize counting array with 93 | std::array count_arr; 94 | count_arr.fill(0); 95 | int size = arr.size(); 96 | // store count of occurrences in count_arr[] 97 | for (int i = 0; i < size; i++) 98 | count_arr[(arr[i] / mask) % BASE]++; 99 | // change count_arr[i] so that count_arr[i] now contains actual 100 | // position of this digit in output[] 101 | for (int i = 1; i < BASE; i++) 102 | count_arr[i] += count_arr[i - 1]; 103 | // build the output array, instead of writing on top of arr 104 | // it is needed because otherwise we will loose the initial values in arr 105 | for (int i = size - 1; i >= 0; i--) { 106 | // Uncomment for debugging purposes: 107 | // std::cout << "Element: " << arr[i] << " -> " << count_arr[ (arr[i]/mask)%BASE ] - 1 << " index" << std::endl; 108 | output[count_arr[(arr[i] / mask) % BASE] - 1] = arr[i]; 109 | count_arr[(arr[i] / mask) % BASE]--; 110 | } 111 | } 112 | 113 | // LSD unsigned integer radix sort 114 | void lsd_radix_sort(std::vector& arr) { 115 | // find maximum 116 | int max_el = *std::max_element(arr.begin(), arr.end()); 117 | int size = arr.size(); 118 | // result array 119 | std::vector output_arr; 120 | output_arr.resize(size); 121 | // Mask is BASE^i; i <- [0, ..., count of digits in max_el] 122 | // Loops until the mask has the same digit count as the max element 123 | for (int mask = 1; max_el / mask > 0; mask *= BASE) { 124 | // using the stable counting, we sort by each digit, preserving previous order 125 | lsd_counting_sort(arr, output_arr, mask); 126 | std::swap(output_arr, arr); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /data_structures/stack/template_stack/example.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file example.cpp 7 | * @author Ivan Filipov 8 | * @date 11.2018 9 | * @brief Example usage of the template stack idea. 10 | * Some benchmarking - a competition between our data structures vs. STL. 11 | */ 12 | 13 | #include // std::cout 14 | #include // std::vector 15 | #include // std::list 16 | #include // std::stack 17 | #include // std::deque 18 | 19 | #include "../../../utils/benchmark.hpp" // our test benchmark lib 20 | #include "../../dynamic_array/dynamic_array.hpp" // our custom vector 21 | #include "../../linked_list/singly_linked_list/slinked_list.hpp" // our custom singly linked list 22 | #include "../../linked_list/doubly_linked_list/dlinked_list.hpp" // our custom doubly linked list 23 | #include "../dynamic_stack/dynamic_stack.hpp" // our custom dynamic stack 24 | #include "../../deque/deque.hpp" // our custom deque 25 | 26 | #include "template_stack.hpp" // the template for stack interface with variadic down-laying container 27 | 28 | // bring here some names from our lib 29 | using namespace dsa; 30 | 31 | // and also from std 32 | using std::vector; 33 | using std::list; 34 | using std::stack; 35 | 36 | const size_t ECOUNT = 10000; //!< number of test elements 37 | 38 | /** Run a fixed test on a stack with different container */ 39 | template 40 | void test_stack_adaptor() { 41 | // creating a stack on a container 42 | t_stack st; 43 | // adding elements 44 | for (size_t i = 0; i < ECOUNT; i++) 45 | st.push(static_cast(i)); 46 | // 47 | // make a copy 48 | t_stack st2 = st; 49 | st2.push(11); // just to be sure that stack2 is used, 50 | // because the compiler could optimize the code 51 | // 52 | // check is empty? 53 | std::cout << "\nempty " << (st.empty() ? "yes" : "no"); 54 | std::cout << " | size " << st.size(); 55 | // 56 | unsigned long long sum = 0; 57 | // remove elements 58 | for (size_t i = ECOUNT; i > 0; i--) { 59 | sum += st.peek(); 60 | st.pop(); 61 | } 62 | // 63 | std::cout << " | sum = " << sum << std::endl; 64 | //destructor for st and st2 65 | } 66 | 67 | /** Test a plain stack implementation */ 68 | template 69 | void test_stack_plain() { 70 | 71 | Stack st; 72 | // adding elements 73 | for (size_t i = 0; i < ECOUNT; i++) 74 | st.push(static_cast(i)); 75 | // 76 | // make a copy 77 | Stack st2 = st; 78 | st2.push(11); // just to be sure that stack2 is used, 79 | // because the compiler could optimize the code 80 | // 81 | // check is empty? 82 | std::cout << "\nempty " << (st.empty() ? "yes" : "no"); 83 | std::cout << " | size " << st.size(); 84 | // 85 | unsigned long long sum = 0; 86 | // remove elements 87 | for (size_t i = ECOUNT; i > 0; i--) { 88 | sum += st.top(); 89 | st.pop(); 90 | } 91 | // 92 | std::cout << " | sum = " << sum << std::endl; 93 | //destructor for st and st2 94 | } 95 | 96 | int main() { 97 | 98 | /* test operations with std::vector */ 99 | benchmark_test_fnc(test_stack_adaptor>, T_FORMAT::F_MILLI, "implementation on std::vector"); 100 | 101 | /* test operations with our custom vector */ 102 | benchmark_test_fnc(test_stack_adaptor>, T_FORMAT::F_MILLI, "implementation on dsa::dynamic_array"); 103 | 104 | /* test operations with std::list */ 105 | benchmark_test_fnc(test_stack_adaptor>, T_FORMAT::F_MILLI, "implementation on std::list"); 106 | 107 | /* test operations with our custom singly linked list */ 108 | ///!!! our pop_back is O(n), while std::list's one is O(1), because it is doubly linked list, that's way this one is much slower 109 | benchmark_test_fnc(test_stack_adaptor>, T_FORMAT::F_MILLI, "implementation on dsa::slinked_list"); 110 | 111 | /* test operations with our custom doubly linked list */ 112 | benchmark_test_fnc(test_stack_adaptor>, T_FORMAT::F_MILLI, "implementation on dsa::dlinked_list"); 113 | 114 | /* test operations with std::deque */ 115 | benchmark_test_fnc(test_stack_adaptor>, T_FORMAT::F_MILLI, "implementation on std::deque"); 116 | 117 | /* test operations with our custom deque */ 118 | benchmark_test_fnc(test_stack_adaptor>, T_FORMAT::F_MILLI, "implementation on dsa::deque"); 119 | 120 | /* test operations with std::stack */ 121 | benchmark_test_fnc(test_stack_plain>, T_FORMAT::F_MILLI, "implementation on std::stack"); 122 | 123 | /* test operations with our custom dynamic stack */ 124 | benchmark_test_fnc(test_stack_plain>, T_FORMAT::F_MILLI, "implementation on dsa::dynamic_stack"); 125 | } 126 | -------------------------------------------------------------------------------- /data_structures/hash_table/hash_table_tests.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file hash_table_tests.hpp 7 | * @author Ivan Filipov 8 | * @date 01.2019 9 | * @brief Some basic test written as template functions, so they can be ran with both our 10 | * linear probing hash table and separate chaining hash table implementations. 11 | */ 12 | 13 | #pragma once 14 | 15 | #include // std::array 16 | #include // std::vector 17 | #include // std::cout 18 | #include // std::quoted(), may need C++14 19 | #include // std::srand(), std::rand() 20 | #include // std::time() 21 | 22 | /// how many elements 23 | const size_t N_ELEMS = 10; 24 | //@{ 25 | /// some typedefs for easier writing 26 | using key_set = std::array; 27 | using data_set = std::vector; 28 | //@} 29 | 30 | /// tests inserting @c N_ELEMS into a hash table. 31 | template 32 | void put_test(HashTable& table, const key_set& keys, const data_set& data) { 33 | 34 | for (size_t i = 0; i < N_ELEMS; i++) { 35 | std::cout << "putting element with key " 36 | << std::quoted(keys[i]) 37 | << " and data " 38 | << data[i] << " ... "; 39 | try { 40 | table.insert(keys[i], data[i]); 41 | } catch(const std::exception& e){ 42 | std::cerr << "insert error : " << e.what(); 43 | } 44 | } 45 | std::cout << "\ntrying to insert taken key:" << std::endl; 46 | try { 47 | table.insert(keys[0], data[0]); 48 | } catch(const std::exception& e){ 49 | std::cerr << "insert error: " << e.what(); 50 | } 51 | 52 | std::cout << std::endl 53 | << "\nAfter the test the table looks like :" 54 | << std::endl << std::endl; 55 | table.print(); 56 | } 57 | 58 | /// tests getting @c N_ELEMS from a hash table. Also a fixed random search. 59 | template 60 | void get_test(HashTable& table, const key_set& keys) { 61 | 62 | const size_t N_GETS = 3; 63 | 64 | size_t which; 65 | for (size_t i = 0; i < N_GETS; i++) { 66 | which = std::rand() % keys.size(); 67 | 68 | std::cout << "getting element with key " 69 | << std::quoted(keys[which]) 70 | << " ... "; 71 | try { 72 | std::cout << "data is " 73 | << table.get(keys[which]) 74 | << std::endl; 75 | } catch (std::exception& e) { 76 | std::cerr << "get error: " << e.what(); 77 | } 78 | } 79 | 80 | std::string random_test = "Pleven"; 81 | 82 | std::cout << "getting element with key " 83 | << std::quoted(random_test) 84 | << " ... "; 85 | 86 | try { 87 | std::cout << "data is " 88 | << table.get(random_test) 89 | << std::endl; 90 | } catch (std::exception& e) { 91 | std::cerr << "get error : " << e.what() << std::endl; 92 | } 93 | } 94 | 95 | /// tests removing @c N_DELETES elements from a hash table. 96 | template 97 | void erase_test(HashTable& table, const key_set& keys) { 98 | 99 | const size_t N_DELETES = 3; 100 | 101 | size_t which; 102 | for (size_t i = 0; i < N_DELETES; i++) { 103 | which = std::rand() % keys.size(); 104 | 105 | std::cout << "removing element with key " 106 | << std::quoted(keys[which]) 107 | << " ... "; 108 | 109 | try { 110 | table.erase(keys[which]); 111 | std::cout << " removed !\n"; 112 | } catch (std::exception& e) { 113 | std::cerr << "remove error: " << e.what(); 114 | } 115 | } 116 | 117 | std::string random_test = "Pleven"; 118 | std::cout << "erasing element with key " 119 | << std::quoted(random_test) 120 | << " ... "; 121 | try { 122 | table.erase(random_test); 123 | std::cout << " erased !\n"; 124 | } catch (std::exception& e) { 125 | std::cerr << "remove error: " << e.what() << std::endl; 126 | } 127 | 128 | std::cout << std::endl 129 | << "After the test the table looks like :" 130 | << std::endl << std::endl; 131 | table.print(); 132 | } 133 | 134 | /// runs all three tests on a fixed hash table. 135 | template 136 | void run_tests(HashTable& table) { 137 | 138 | std::srand(time(nullptr)); 139 | // some keys 140 | key_set keys = { 141 | "Sofia", 142 | "Montana", 143 | "Stara Zagora", 144 | "Dobrich", 145 | "Sliven", 146 | "Haskovo", 147 | "Varna", 148 | "Burgas", 149 | "Veliko Turnovo", 150 | "Ruse" 151 | }; 152 | // some data 153 | data_set data; 154 | data.reserve(N_ELEMS); 155 | for (size_t i = 0; i < N_ELEMS; i++) 156 | data.push_back(std::rand() % 100); 157 | 158 | table.print(); 159 | 160 | std::cout << "\n\n>>>>>> PUT TESTS <<<<<<<\n\n"; 161 | put_test(table, keys, data); 162 | 163 | std::cout << "\n\n>>>>>> DELETE TESTS <<<<<<<\n\n"; 164 | erase_test(table, keys); 165 | 166 | std::cout << "\n\n>>>>>> GET TESTS <<<<<<<\n\n"; 167 | get_test(table, keys); 168 | } 169 | -------------------------------------------------------------------------------- /data_structures/queue/template_queue/example.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file example.cpp 7 | * @author Ivan Filipov 8 | * @date 11.2019 9 | * @brief Example usage of the template queue idea. 10 | * Some benchmarking - a competition between our data structures vs. STL. 11 | */ 12 | 13 | #include // std::cout 14 | #include // std::vector 15 | #include // std::list 16 | #include // std::queue 17 | #include // std::deque 18 | 19 | #include "../../../utils/benchmark.hpp" // our test benchmark lib 20 | #include "../../dynamic_array/dynamic_array.hpp" // our custom vector 21 | #include "../../linked_list/singly_linked_list/slinked_list.hpp" // our custom singly linked list 22 | #include "../../linked_list/doubly_linked_list/dlinked_list.hpp" // our custom doubly linked list 23 | #include "../dynamic_queue/dynamic_queue.hpp" // our custom dynamic stack 24 | #include "../../deque/deque.hpp" // our custom deque 25 | 26 | #include "template_queue.hpp" // the template for stack interface with variadic down-laying container 27 | 28 | // bring here some names from our lib 29 | using namespace dsa; 30 | 31 | // and also from std 32 | using std::vector; 33 | using std::list; 34 | using std::queue; 35 | 36 | const size_t ECOUNT = 10000; //!< number of test elements 37 | 38 | /** Run a fixed test on a stack with different container */ 39 | template 40 | void test_queue_adaptor() { 41 | // creating a queue on a container 42 | t_queue q; 43 | 44 | // adding elements 45 | for (size_t i = 0; i < ECOUNT; i++) 46 | q.push(static_cast(i)); 47 | // 48 | // make a copy 49 | t_queue q2 =q; 50 | q2.push(11); // just to be sure that stack2 is used, 51 | // because the compiler could optimize the code 52 | // 53 | // check is empty? 54 | std::cout << "\nempty " << (q.empty() ? "yes" : "no"); 55 | std::cout << " | size " << q.size(); 56 | // 57 | unsigned long long sum = 0; 58 | // remove elements 59 | while (!q.empty()) { 60 | sum += q.front(); 61 | q.pop(); 62 | } 63 | // 64 | std::cout << " | sum = " << sum << std::endl; 65 | //destructor for st and st2 66 | } 67 | 68 | /** Test a plain queue implementation */ 69 | template 70 | void test_queue_plain() { 71 | 72 | Queue q; 73 | // adding elements 74 | for (size_t i = 0; i < ECOUNT; i++) 75 | q.push(static_cast(i)); 76 | // 77 | // make a copy 78 | Queue q2 = q; 79 | q2.push(11); // just to be sure that stack2 is used, 80 | // because the compiler could optimize the code 81 | // 82 | // check is empty? 83 | std::cout << "\nempty " << (q.empty() ? "yes" : "no"); 84 | std::cout << " | size " << q.size(); 85 | // 86 | unsigned long long sum = 0; 87 | // remove elements 88 | while (!q.empty()) { 89 | sum += q.front(); 90 | q.pop(); 91 | } 92 | // 93 | std::cout << " | sum = " << sum << std::endl; 94 | //destructor for st and st2 95 | } 96 | 97 | int main() { 98 | 99 | /* test operations with std::vector */ 100 | benchmark_test_fnc(test_queue_adaptor>, T_FORMAT::F_MILLI, "implementation on std::vector"); 101 | 102 | /* test operations with our custom vector */ 103 | benchmark_test_fnc(test_queue_adaptor>, T_FORMAT::F_MILLI, "implementation on dsa::dynamic_array"); 104 | 105 | /* test operations with std::list */ 106 | benchmark_test_fnc(test_queue_adaptor>, T_FORMAT::F_MILLI, "implementation on std::list"); 107 | 108 | /* test operations with our custom singly linked list */ 109 | /// !!! here the situation in the competition between our list and std::list is different, because 110 | /// the queue only needs pop_front & push_back, both O(1) in these implementations, but std::list is doubly linked. 111 | benchmark_test_fnc(test_queue_adaptor>, T_FORMAT::F_MILLI, "implementation on dsa::slinked_list"); 112 | 113 | /* test operations with our custom doubly linked list */ 114 | benchmark_test_fnc(test_queue_adaptor>, T_FORMAT::F_MILLI, "implementation on dsa::dlinked_list"); 115 | 116 | /* test operations with std::deque */ 117 | benchmark_test_fnc(test_queue_adaptor>, T_FORMAT::F_MILLI, "implementation on std::deque"); 118 | 119 | /* test operations with our custom deque */ 120 | benchmark_test_fnc(test_queue_adaptor>, T_FORMAT::F_MILLI, "implementation on dsa::deque"); 121 | 122 | /* test operations with std::queue */ 123 | benchmark_test_fnc(test_queue_plain>, T_FORMAT::F_MILLI, "implementation on std::queue"); 124 | 125 | /* test operations with our custom dynamic queue */ 126 | benchmark_test_fnc(test_queue_plain>, T_FORMAT::F_MILLI, "implementation on dsa::dynamic_queue"); 127 | } 128 | -------------------------------------------------------------------------------- /algorithms/backtracking/queens_problem.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file queens_problem.c 7 | * @author Ivan Filipov 8 | * @date 01.2019 9 | * @brief Solution to "eight queens" problem, 10 | * as an example of backtracking algorithm. 11 | * 12 | * @see https://en.wikipedia.org/wiki/Eight_queens_puzzle 13 | */ 14 | 15 | #include // printf(),putchar() 16 | #include // time() 17 | 18 | /* constants */ 19 | #define FIRST_COLUMN 0 //!< index of the first column 20 | #define FREE_CELL 0 //!< marker for a cell with no queen and hit inside 21 | #define BOARD_SIZE 8 //!< chess board's size 22 | #define QUEEN_VAL 9 //!< marker for a cell taken by queen 23 | #define QUEEN_SYM 'X' //!< drawing symbol for queen 24 | #define FREE_CELL_SYM '0' //!< drawing symbol for cell without queen 25 | #define DELAY_SECONDS 1 //!< delay between each solution's output 26 | 27 | #define REMOVE_HIT -1 //!< value to be added when removing hits 28 | #define ADD_HIT 1 //!< value to be added when adding hits 29 | 30 | /* globals */ 31 | /// the whole chess board 32 | /// value of board[i][j] means 33 | /// 0: the cell isn't hit by any queen 34 | /// 9: there is a queen on this cell 35 | /// 1 to 8: how many queens hit this cell 36 | unsigned board[BOARD_SIZE][BOARD_SIZE]; 37 | 38 | /* helper functions*/ 39 | /** @brief simple delay function in C - style 40 | * used to set delay between printing each solution 41 | * @param[in] secs - how many seconds to delay 42 | */ 43 | void delay(unsigned secs) { 44 | 45 | time_t ret_time = time(0) + secs; 46 | while (time(0) < ret_time) 47 | ; 48 | } 49 | 50 | /// simple helper function to count 51 | /// solutions and output current one's number 52 | void count_solutions() { 53 | // initializes only once 54 | static unsigned counter = 0; 55 | // printing the incremented counter 56 | printf("\nsolution number : %d\n", ++counter); 57 | } 58 | 59 | /// initializes all cells of the board as FREE_CELL 60 | void board_init() { 61 | 62 | for (unsigned i = 0; i < BOARD_SIZE; i++) 63 | for (unsigned j = 0; j < BOARD_SIZE; j++) 64 | board[i][j] = FREE_CELL; 65 | } 66 | 67 | /// outputs how the board looks like in 68 | /// the current solution 69 | void print_board() { 70 | 71 | count_solutions(); 72 | 73 | for (unsigned i = 0; i < BOARD_SIZE; i++) { 74 | for (unsigned j = 0; j < BOARD_SIZE; j++) 75 | if (board[i][j] == QUEEN_VAL) 76 | printf("%2c", QUEEN_SYM); 77 | else 78 | printf("%2c", FREE_CELL_SYM); 79 | 80 | putchar('\n'); 81 | } 82 | 83 | delay(DELAY_SECONDS); 84 | } 85 | 86 | /** 87 | * @brief helper function for marking queen hits on the board 88 | * @param[in] hit_type: the value to be added to all cells (-1) for removing hit 89 | * @param[in] from_row: x position of the queen 90 | * @param[in] from_col: y position of the queen 91 | */ 92 | void mark_hits(short hit_type, unsigned from_row, unsigned from_col) { 93 | // up and down 94 | for (unsigned rows = 0; rows < BOARD_SIZE; rows++) 95 | if(rows != from_row) 96 | board[rows][from_col] += hit_type; 97 | // left and right 98 | for (unsigned cols = 0; cols < BOARD_SIZE; cols++) 99 | if (cols != from_col) 100 | board[from_row][cols] += hit_type; 101 | // diagonals 102 | for (unsigned rows = 0; rows < BOARD_SIZE; rows++) 103 | for (unsigned cols = 0; cols < BOARD_SIZE; cols++) 104 | if ((from_row - from_col) == (rows - cols) || 105 | (from_row + from_col) == (rows + cols)) 106 | board[rows][cols] += hit_type; 107 | // because while iterating the diagonal 108 | // we are changing our queen's cell value 109 | // we should remove the hit. 110 | // the other approach is to add an additional 111 | // condition in our loop !((cols == from_col) && (rows == from_row)) 112 | board[from_row][from_col] -= hit_type; 113 | } 114 | 115 | 116 | /** 117 | * @brief recursive function for placing a queen on the chess board 118 | * @param[in] col: on which column to try to place a queen 119 | * @note successful placement of queen on column BOARD_SIZE, means solution is found 120 | */ 121 | void place_queen(unsigned col) { 122 | 123 | // trying each row 124 | for (int row = 0; row < BOARD_SIZE; row++) { 125 | // if we can place a queen there 126 | if (board[row][col] == FREE_CELL) { 127 | // placing the queen 128 | board[row][col] = QUEEN_VAL; 129 | // check if we have found a solution 130 | if (col == BOARD_SIZE - 1) 131 | print_board(); 132 | else { // not the last queen 133 | // mark all cells that our queen hits 134 | mark_hits(ADD_HIT, row, col); 135 | // proceeding with the next queen 136 | place_queen(col + 1); 137 | // remove the marks 138 | mark_hits(REMOVE_HIT, row, col); 139 | } 140 | // remove the queen which we have placed 141 | board[row][col] = FREE_CELL; 142 | } 143 | } 144 | } 145 | 146 | /// simple wrapper for solving the problem 147 | void solve_queens_problem() { 148 | 149 | place_queen(FIRST_COLUMN); 150 | } 151 | 152 | int main() { 153 | 154 | /* initialize the board */ 155 | board_init(); 156 | 157 | /* run the algorithm */ 158 | solve_queens_problem(); 159 | 160 | return 0; 161 | } 162 | -------------------------------------------------------------------------------- /data_structures/hash_table/linear_probing/linear_probing_hash.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This file is part of the "Data structures and algorithms" course. FMI 2018/19 3 | *******************************************************************************/ 4 | 5 | /** 6 | * @file linear_probing_hash.cpp 7 | * @author Ivan Filipov 8 | * @date 01.2019 9 | * @brief Basic implementation of hash table, using 10 | * closed hashing (opened address) strategy - 11 | * linear probing. 12 | * Depends only on our custom dynamic array. 13 | * @see https://en.wikipedia.org/wiki/Hash_table 14 | * @see https://en.wikipedia.org/wiki/Open_addressing 15 | */ 16 | 17 | #include "linear_probing_hash.h" 18 | 19 | // include only if debugging is ON 20 | #if DEBUG_HASH == 1 21 | #include //std::cin, std::cout 22 | #endif // DEBUG_HASH 23 | 24 | // the hash function, it is the most critical zone, 25 | // the implementation below is pretty simple, consider 26 | // using better hash functions in your applications 27 | size_t lin_pr_hash_table::hash_func(const key_type& key, const size_t size) { 28 | // get the size of the string 29 | size_t result = key.size(); 30 | // add the ASCII code for each character 31 | for (unsigned char c: key) 32 | result += c; 33 | 34 | // return result % size; 35 | // but better in case the SIZE is a power of 2 36 | return result & (size - 1); 37 | } 38 | 39 | void lin_pr_hash_table::rehash() { 40 | // debug print 41 | #if DEBUG_HASH == 1 42 | std::cout << "\n...rehashing ...\n"; 43 | #endif // DEBUG_HASH 44 | 45 | // create a new table with double the size 46 | lin_pr_hash_table new_table(table.size() * 2); 47 | 48 | // for each element form the old table ... 49 | for (const table_elem& el: table) { 50 | if (el.key != "") 51 | new_table.insert(el.key, el.data); // put it in the new table 52 | // which will lead to re-calculating the hash values 53 | } 54 | // for O(1) swaps the tables 55 | new_table.table.swap_with(table); 56 | // now our table has the new elements, the old content, 57 | // will be freed in the destructor of new_table 58 | #if DEBUG_HASH == 1 59 | std::cout << ".............\n"; 60 | #endif // DEBUG_HASH 61 | } 62 | 63 | void lin_pr_hash_table::insert(const key_type& key, const data_type& data) { 64 | // calculate where to add the new element using the hash function 65 | size_t index = hash_func(key, table.size()); 66 | // check if resizing is needed 67 | if (logic_fill * 2 >= table.size()) { 68 | rehash(); 69 | // calculate the hash again, because the table now have different size 70 | index = hash_func(key, table.size()); 71 | } 72 | // searching for the first free position, maximum number of tries is the size 73 | unsigned int trys = table.size(); 74 | // skip all taken cells 75 | while (table[index].key != "" && --trys) { 76 | if (table[index].key == key) // handling same keys 77 | throw std::logic_error("this key is taken"); 78 | index = (index + STEP) % table.size(); // taking by mod(size) means, make cycles 79 | } 80 | //successfully found a free position 81 | if (table[index].key == "") { 82 | table[index].key = key; 83 | table[index].data = data; 84 | logic_fill++; 85 | } 86 | #if DEBUG_HASH == 1 87 | std::cout << "stored at hash " << index << std::endl; 88 | #endif // DEBUG_HASH 89 | } 90 | 91 | data_type lin_pr_hash_table::get(const key_type& key) const { 92 | // calculate from where to search element with "key" 93 | size_t index = hash_func(key, table.size()); 94 | // we need to make some "probes" to find the element with exactly same key 95 | // maximum number of tries is the size 96 | size_t trys = table.size(); 97 | // search until "size" tries, or same key, or empty cell 98 | while (table[index].key != key && table[index].key != "" && --trys) 99 | index = (index + STEP) % table.size(); 100 | // if found return it's data 101 | if (table[index].key == key) 102 | return table[index].data; 103 | // not found 104 | throw std::logic_error("there isn't element with such key"); 105 | } 106 | 107 | void lin_pr_hash_table::erase(const key_type& key) { 108 | // calculate from where to search element with "key" 109 | size_t index = hash_func(key, table.size()); 110 | 111 | // same searching as in "get" 112 | size_t trys = table.size(); 113 | 114 | while (table[index].key != key && table[index].key != "" && --trys) 115 | index = (index + STEP) % table.size(); 116 | // not found 117 | if (table[index].key != key) 118 | throw std::logic_error("there isn't element with such key"); 119 | 120 | // at "index" there is the wanted element, we need to make him 121 | // the last element in the "clustered" elements 122 | 123 | // where the next elem could be 124 | size_t next_ind = (index + STEP) % table.size(); 125 | // moving it down the cluster 126 | while (table[next_ind].key != "") { 127 | // swap the keys and data-s 128 | std::swap(table[next_ind], table[index]); 129 | // go to the next element 130 | next_ind = (next_ind + STEP) % table.size(); 131 | // where the previous is 132 | index = (index + STEP) % table.size(); 133 | } 134 | 135 | // now the wanted element is a the bottom of the cluster 136 | // we can remove it 137 | table[index].key.clear(); // = ""; 138 | table[index].data = -1; // clear for more complex data 139 | --logic_fill; 140 | } 141 | 142 | void lin_pr_hash_table::print() const { 143 | #if DEBUG_HASH == 1 144 | for (size_t i = 0; i < table.size(); i++) { 145 | std::cout << "hash = " << i << " :" 146 | << "{ key: " << table[i].key << ", " 147 | << " data: " << table[i].data << " }" 148 | << std::endl; 149 | } 150 | #endif // DEBUG_HASH 151 | } 152 | --------------------------------------------------------------------------------