├── .github └── FUNDING.yml ├── CITATION.cff ├── LICENSE ├── README.md ├── assets └── big-o-graph.png └── src ├── data_structures ├── arrays │ ├── 2D_array.py │ ├── README.md │ ├── array_manipulation.py │ ├── left_rotation.py │ ├── max_sum_subarray.py │ ├── reverse_array.py │ └── sparse_array.py ├── bloom-filter │ └── README.md ├── dictionaries │ └── README.md ├── disjoint-set │ └── README.md ├── graph │ └── README.md ├── hash-table │ └── README.md ├── linked_list │ ├── README.md │ ├── detecting_loops_linked_list.py │ ├── doubly_linked_list.py │ ├── flattening_nested_linked_list.py │ ├── singly_linked_list.py │ ├── skip_delete.py │ └── swap_node.py ├── priority-queue │ └── README.md ├── queue │ ├── README.md │ ├── queue.ipynb │ ├── queue.py │ ├── queue_using_stack.py │ ├── queues_using_arrays.py │ ├── queues_using_linked_list.py │ └── reverse_queue.py ├── stack │ ├── README.md │ ├── reverse_stack.py │ ├── stack.ipynb │ ├── stack.py │ ├── stack_using_array.py │ └── stack_using_linked_list.py ├── tree │ ├── README.md │ ├── avl-tree │ │ └── README.md │ ├── binary-search-tree │ │ └── README.md │ ├── fenwick-tree │ │ └── README.md │ ├── red-black-tree │ │ └── README.md │ └── segment-tree │ │ └── README.md └── trie │ └── README.md ├── efficiency ├── ArraySorting.png ├── big_o_efficiency.ipynb ├── big_o_efficiency.py └── bigo.svg └── recursion ├── .ipynb_checkpoints ├── factorial_using_recursion-checkpoint.ipynb ├── fibonacci_using_recursion-checkpoint.ipynb └── intro_recursion-checkpoint.ipynb ├── factorial_using_recursion.ipynb ├── fibonacci_using_recursion.ipynb └── intro_recursion.ipynb /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | github: joaomh 3 | 4 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: "If you use this software, please cite it as below." 3 | authors: 4 | - family-names: "Herrera Pinheiro" 5 | given-names: "João Manoel" 6 | orcid: "https://orcid.org/0009-0001-6192-7374" 7 | title: "Python Data Structures and Algorithms" 8 | date-released: 2020-14-05 9 | url: "https://github.com/joaomh/python-data-structures-and-algorithms" 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 João Pinheiro 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python Data Structures and Algorithms 2 | This repository contains Python based examples of many 3 | data structures and algorithms. 4 | 5 | This is the main reference for my YouTube Channel, 2001 Engenharia in PT-BR 6 | 7 | [▶ Estrutura de Dados e Algoritmos em Python](https://www.youtube.com/playlist?list=PLE1UtdMhwaEonmSRDkSzpFV5m5zKiqM94) 8 | 9 | Each algorithm and data structure has its own separate README 10 | with related explanations and links for further reading. 11 | 12 | ## Data Structures 13 | In computer science, a data structure is a data organization, management, and storage format that enables efficient access and modification. More precisely, a data structure is a collection of data values, the relationships among them, and the functions or operations that can be applied to the data. 14 | 15 | Data structures are the building block for Computer Science and Software Engineering. 16 | 17 | `B` - Beginner, `A` - Advanced 18 | 19 | * `B` [Linked List](src/data_structures/linked_list) 20 | * `B` [Doubly Linked List](src/data_structures/linked_list) 21 | * `B` [Array](src/data_structures/arrays) 22 | * `B` [Queue](src/data_structures/queue) 23 | * `B` [Stack](src/data_structures/stack) 24 | * `B` [Hash Table](src/data_structures/hash-table) 25 | * `B` [Priority Queue](src/data_structures/priority-queue) 26 | * `B` [Dictionaries](src/data_structures/dictionaries) 27 | * `A` [Trie](src/data_structures/trie) 28 | * `A` [Tree](src/data_structures/tree) 29 | * `A` [Binary Search Tree](src/data_structures/tree/binary-search-tree) 30 | * `A` [AVL Tree](src/data_structures/tree/avl-tree) 31 | * `A` [Red-Black Tree](src/data_structures/tree/red-black-tree) 32 | * `A` [Segment Tree](src/data_structures/tree/segment-tree) 33 | * `A` [Fenwick Tree](src/data_structures/tree/fenwick-tree) 34 | * `A` [Graph](src/data_structures/graph) 35 | * `A` [Disjoint Set](src/data_structures/disjoint-set) 36 | * `A` [Bloom Filter](src/data_structures/bloom-filter) 37 | 38 | ## Recursion 39 | In computer science Recursion is a technique for solving problems where the solution to a particular problem depends on the solution to a smaller instance of the same problem. 40 | 41 | `B` - Beginner, `A` - Advanced 42 | 43 | * `B` [Introduction to Recursion](src/recursion/intro_recursion.ipynb) 44 | * `B` [Factorial Using Recursion](src/recursion/factorial_using_recursion.ipynb) 45 | * `B` [Fibonacci Using Recursion](src/recursion/fibonacci_using_recursion.ipynb) 46 | 47 | ## Algorithm 48 | In mathematics and computer science, an algorithm is a finite sequence of well-defined, computer-implementable instructions, typically to solve a class of problems or to perform a computation. Algorithms are always unambiguous and are used as specifications for performing calculations, data processing, automated reasoning, and other tasks. 49 | 50 | 51 | ### Big O Notation 52 | 53 | *Big O notation* is used to classify algorithms according to how their running time or space requirements grow as the input size grows. 54 | On the chart below you may find most common orders of growth of algorithms specified in Big O notation. 55 | 56 | ![Big O graphs](./assets/big-o-graph.png) 57 | 58 | Source: [Big O Cheat Sheet](http://bigocheatsheet.com/). 59 | 60 | Below is the list of some of the most used Big O notations and their performance comparisons against different sizes of the input data. 61 | 62 | | Big O Notation | Computations for 10 elements | Computations for 100 elements | Computations for 1000 elements | 63 | | -------------- | ---------------------------- | ----------------------------- | ------------------------------- | 64 | | **O(1)** | 1 | 1 | 1 | 65 | | **O(log n)** | 3 | 6 | 9 | 66 | | **O(n)** | 10 | 100 | 1000 | 67 | | **O(n log n)** | 30 | 600 | 9000 | 68 | | **O(n2 )** | 100 | 10000 | 1000000 | 69 | | **O(2n )** | 1024 | 1.26e+29 | 1.07e+301 | 70 | | **O(n!)** | 3628800 | 9.3e+157 | 4.02e+2567 | 71 | 72 | ### Data Structure Operations Complexity 73 | 74 | | Data Structure | Access | Search | Insertion | Deletion | Comments | 75 | | ----------------------- | :-------: | :-------: | :-------: | :-------: | :-------- | 76 | | **Array** | 1 | n | n | n | | 77 | | **Stack** | n | n | 1 | 1 | | 78 | | **Queue** | n | n | 1 | 1 | | 79 | | **Linked List** | n | n | 1 | n | | 80 | | **Hash Table** | - | n | n | n | In case of perfect hash function costs would be O(1) | 81 | | **Binary Search Tree** | n | n | n | n | In case of balanced tree costs would be O(log(n)) | 82 | | **B-Tree** | log(n) | log(n) | log(n) | log(n) | | 83 | | **Red-Black Tree** | log(n) | log(n) | log(n) | log(n) | | 84 | | **AVL Tree** | log(n) | log(n) | log(n) | log(n) | | 85 | | **Bloom Filter** | - | 1 | 1 | - | False positives are possible while searching | 86 | 87 | ### Array Sorting Algorithms Complexity 88 | 89 | | Name | Best | Average | Worst | Memory | Stable | Comments | 90 | | --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- | 91 | | **Bubble sort** | n | n2 | n2 | 1 | Yes | | 92 | | **Insertion sort** | n | n2 | n2 | 1 | Yes | | 93 | | **Selection sort** | n2 | n2 | n2 | 1 | No | | 94 | | **Heap sort** | n log(n) | n log(n) | n log(n) | 1 | No | | 95 | | **Merge sort** | n log(n) | n log(n) | n log(n) | n | Yes | | 96 | | **Quick sort** | n log(n) | n log(n) | n2 | log(n) | No | Quicksort is usually done in-place with O(log(n)) stack space | 97 | | **Shell sort** | n log(n) | depends on gap sequence | n (log(n))2 | 1 | No | | 98 | | **Counting sort** | n + r | n + r | n + r | n + r | Yes | r - biggest number in array | 99 | | **Radix sort** | n * k | n * k | n * k | n + k | Yes | k - length of longest key | 100 | 101 | ## References 102 | 103 | This project was based on 104 | 105 | - [JavaScript Algorithms and Data Structures](https://github.com/trekhleb/javascript-algorithms) 106 | 107 | 108 | -------------------------------------------------------------------------------- /assets/big-o-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomh/python-data-structures-and-algorithms/3ba7434d28dfd28f4a9aeb49fd3aeb3090e463e2/assets/big-o-graph.png -------------------------------------------------------------------------------- /src/data_structures/arrays/2D_array.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a 6x6 2D Array, arr: 3 | 1 1 1 0 0 0 4 | 0 1 0 0 0 0 5 | 1 1 1 0 0 0 6 | 0 0 0 0 0 0 7 | 0 0 0 0 0 0 8 | 0 0 0 0 0 0 9 | 10 | We define an hourglass 'A' in to be a subset of values with indices 11 | falling in this pattern in 'arr' graphical representation: 12 | a b c 13 | d 14 | e f g 15 | 16 | Function Description 17 | It should return an integer, the maximum hourglass sum in the array. 18 | arr: an array of integers 19 | 20 | Input Format 21 | Each of the 6 lines of inputs 'arr' contains 6 space-separated integers arr[i][j]. 22 | 23 | Constraints 24 | -9 <= arr[i][j] <= 9 25 | 0 <= i,j <= 5 26 | 27 | Output Format 28 | Print the largest (maximum) hourglass sum found in 'arr'. 29 | 30 | Sample Input 31 | 1 1 1 0 0 0 32 | 0 1 0 0 0 0 33 | 1 1 1 0 0 0 34 | 0 0 2 4 4 0 35 | 0 0 0 2 0 0 36 | 0 0 1 2 4 0 37 | 38 | Sample Output 39 | 19 40 | """ 41 | def hourglass_sum(arr): 42 | rows = len(arr) 43 | columns = len(arr[0]) 44 | max_total = True 45 | for i in range(0, rows-2): 46 | for j in range(0, columns-2): 47 | total = arr[i][j] + arr[i][j+1] + arr[i][j+2] + arr[i+1][j+1] \ 48 | + arr[i+2][j] + arr[i+2][j+1] + arr[i+2][j+2] 49 | if max_total == True: 50 | max_total = total 51 | max_total = max(max_total, total) 52 | return max_total 53 | 54 | arr = [[1, 1, 1, 0, 0, 0], 55 | [0, 1, 0, 0, 0, 0], 56 | [1, 1, 1, 0, 0, 0], 57 | [0, 0, 2, 4, 4, 0], 58 | [0, 0, 0, 2, 0, 0], 59 | [0, 0, 1, 2, 4, 0], 60 | ] 61 | print(hourglass_sum(arr)) -------------------------------------------------------------------------------- /src/data_structures/arrays/README.md: -------------------------------------------------------------------------------- 1 | # Array 2 | 3 | In computer science, an array data structure, or simply an array, is a data structure consisting of a collection of elements (values or variables), each identified by at least one array index or key. An array is stored such that the position of each element can be computed from its index tuple by a mathematical formula. The simplest type of data structure is a linear array, also called one-dimensional array. An array is a type of data structure that stores elements of the same type in a contiguous block of memory. 4 | 5 | Because the mathematical concept of a matrix can be represented as a two-dimensional grid, two-dimensional arrays are also sometimes called matrices. In some cases the term "vector" is used in computing to refer to an array, although tuples rather than vectors are the more mathematically correct equivalent. Tables are often implemented in the form of arrays, the word table is sometimes used as a synonym of array. 6 | 7 | Arrays are among the oldest and most important data structures, and are used by almost every program. They are also used to implement many other data structures, such as lists and strings. They effectively exploit the addressing logic of computers. In most modern computers and many external storage devices, the memory is a one-dimensional array of words, whose indices are their addresses. Processors, especially vector processors, are often optimized for array operations. 8 | 9 | Arrays are useful mostly because the element indices can be computed at run time. Among other things, this feature allows a single iterative statement to process arbitrarily many elements of an array. For that reason, the elements of an array data structure are required to have the same size and should use the same data representation. 10 | 11 | The term array is often used to mean array data type, a kind of data type provided by most high-level programming languages that consists of a collection of values or variables that can be selected by one or more indices computed at run-time. Array types are often implemented by array structures; however, in some languages they may be implemented by hash tables, linked lists, search trees, or other data structures. 12 | 13 | The term is also used, especially in the description of algorithms, to mean associative array or "abstract array", a theoretical computer science model intended to capture the essential properties of arrays. 14 | 15 | ![Array](https://media.geeksforgeeks.org/wp-content/uploads/array-2.png) 16 | 17 | ## Complexities 18 | 19 | ### Time Complexity 20 | 21 | | Access | Search | Insertion | Deletion | 22 | | :-------: | :-------: | :-------: | :-------: | 23 | | O(1) | O(n) | O(n) | O(n) | 24 | 25 | ### Space Complexity 26 | 27 | O(n) 28 | 29 | ## Reference 30 | 31 | - [Wikipedia](https://en.wikipedia.org/wiki/Array_data_structure) 32 | - [GeekforGeeks](https://www.geeksforgeeks.org/array-data-structure/) -------------------------------------------------------------------------------- /src/data_structures/arrays/array_manipulation.py: -------------------------------------------------------------------------------- 1 | """ 2 | Starting with a 1-indexed array of zeros and a list of operations, 3 | for each operation add a value to each of the array element between two given indices, inclusive. 4 | Once all operations have been performed, return the maximum value in your array. 5 | 6 | For example, the length of your array of zeros n = 10. 7 | Your list of queries is as follows: 8 | 9 | a b k 10 | 1 5 3 11 | 4 8 7 12 | 6 9 1 13 | 14 | Add the values of 'k' between the indices 'a' and 'b' inclusive: 15 | 16 | index-> 1 2 3 4 5 6 7 8 9 10 17 | [0,0,0, 0, 0,0,0,0,0, 0] 18 | [3,3,3, 3, 3,0,0,0,0, 0] 19 | [3,3,3,10,10,7,7,7,0, 0] 20 | [3,3,3,10,10,8,8,8,1, 0] 21 | 22 | The largest value is 10 after all operations are performed. 23 | 24 | Input Format 25 | The first line contains two space-separated integers 'n' and 'm', 26 | the size of the array and the number of operations. 27 | Each of the next 'm' lines contains three space-separated integers 'a', 'b' and 'k', 28 | the left index, right index and summand. 29 | 30 | Sample Input 31 | 5 3 32 | 1 2 100 33 | 2 5 100 34 | 3 4 100 35 | 36 | Sample Output 37 | 200 38 | """ 39 | 40 | def array_manipulation(n, queries): 41 | """ 42 | array_manipulation has the following parameters: 43 | n - the number of elements in your array 44 | queries - a two dimensional array of queries where 45 | each queries[i] contains three integers, a, b, and k. 46 | """ 47 | arr = [0]*n 48 | for i in queries: 49 | """ 50 | for j in range(i[0], i[1] + 1): 51 | arr[j - 1] += i[2] 52 | return max(arr)""" 53 | arr[i[0] - 1] += i[2] 54 | if i[1] != len(arr): 55 | arr[i[1]] -= i[2] 56 | 57 | # The insight here is that the sum only needs to be calculated for each step or fall 58 | # in the array rather than every individual element. 59 | # This is easier understand if you draw a picture, 60 | # but ultimately it allows us to do a single loop for calculating 61 | # the two steps in the array, and another for accounting for the maximum step height. 62 | # There still remains the edge-case where arr[i[1]] -= i[2] doesn’t work because 63 | # if i[1] == len(arr), adding ‘fall’ to the ‘step’ is erroneous. 64 | # So simply add in a conditional before arr[i[1]] -= i[2] - line 54 65 | max_value = 0 66 | itk = 0 67 | print(arr) 68 | for q in arr: 69 | itk += q 70 | if itk > max_value: 71 | max_value = itk 72 | return max_value 73 | 74 | n = 5 75 | queries = [ [1, 2, 100], 76 | [2, 5, 100], 77 | [3, 4, 100]] 78 | print(array_manipulation(n,queries)) -------------------------------------------------------------------------------- /src/data_structures/arrays/left_rotation.py: -------------------------------------------------------------------------------- 1 | """ 2 | A left rotation operation on an array of size 'n' 3 | shifts each of the array's elements 1 unit to the left. 4 | For example, if 2 left rotations are performed on array [1, 2, 3, 4, 5], 5 | then the array would become [3, 4, 5, 1, 2]. 6 | 7 | Given an array of n integers and a number, 'd', perform 'd' left rotations on the array. 8 | Then print the updated array as a single line of space-separated integers. 9 | """ 10 | def left_rotation(a, d): 11 | return a[d:] + a[:d] 12 | 13 | a = [1, 2, 3, 4, 5] 14 | print(left_rotation(a, 4)) 15 | 16 | -------------------------------------------------------------------------------- /src/data_structures/arrays/max_sum_subarray.py: -------------------------------------------------------------------------------- 1 | """ 2 | You have been given an array containg numbers. 3 | Find and return the largest sum in a contiguous subarray within the input array. 4 | 5 | Example 1 6 | arr= [1, 2, 3, -4, 6] 7 | The largest sum is '8', which is the sum of all elements of the array. 8 | 9 | Example 2 10 | arr = [1, 2, -5, -4, 1, 6] 11 | The largest sum is '7', which is the sum of the last two elements of the array. 12 | """ 13 | 14 | def max_sum_subarray(arr): 15 | max_so_far = 0 16 | max_ending_here = 0 17 | for i in range(0, len(arr)): 18 | max_ending_here += arr[i] 19 | if max_so_far < max_ending_here: 20 | max_so_far = max_ending_here 21 | if max_ending_here < 0: 22 | max_ending_here = 0 23 | return max_so_far 24 | 25 | arr = [1, 2, 3, -4, 6] 26 | print(str(max_sum_subarray(arr))) 27 | -------------------------------------------------------------------------------- /src/data_structures/arrays/reverse_array.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given an array of 'A' of 'N' integers, print each element in reverse order 3 | as a single line of space-separated integers. 4 | 5 | Example: 6 | Input = 1 4 3 2 7 | Output = 2 3 4 1 8 | """ 9 | 10 | # Reverse the array 11 | 12 | # Method 1 13 | # For loop 14 | def reverseArray_1(a): 15 | b = [] 16 | for i in a: 17 | b.insert(0, i) 18 | return b 19 | 20 | # Method 2 21 | # Copies the list prior to reversing it 22 | def reverseArray_2(a): 23 | return a[::-1] 24 | 25 | # Method 3 26 | # The fastest way to reverse a long list 27 | def reverseArray_3(a): 28 | a = a.reverse() 29 | return a 30 | 31 | a = [1, 4, 3, 2] 32 | print (reverseArray_1(a)) 33 | print (reverseArray_2(a)) 34 | 35 | reverseArray_3(a) 36 | print(a) -------------------------------------------------------------------------------- /src/data_structures/arrays/sparse_array.py: -------------------------------------------------------------------------------- 1 | """ 2 | There is a collection of input strings and a collection of query strings. 3 | For each query string, determine how many times it occurs in the list of input strings. 4 | 5 | Example given a input strings = ['ab', 'ab', 'abc'] and 6 | queries = ['ab',' abc', 'bc'] we find 2 instances of 'ab', 7 | 1 of 'abc' and 0 of 'bc'. For each query we add an element to our return array 8 | results = [2, 1, 0] 9 | 10 | Input Format 11 | 12 | The first line contains and integer 'n', the size of strings. 13 | Each of the next 'n' lines contains a string 'strings[i]'. 14 | The next line contains 'q', the size of queries. 15 | Each of the next 'q' lines contains a string 'q[i]'. 16 | """ 17 | 18 | def matching_strings(strings, queries): 19 | results = [] 20 | for i in range(len(queries)): 21 | count = 0 22 | for j in range(len(strings)): 23 | if strings[j] == queries[i]: 24 | count += 1 25 | results.append(count) 26 | return results 27 | 28 | strings = ['ab', 'ab', 'abc'] 29 | queries = ['ab','abc', 'bc'] 30 | 31 | print(matching_strings(strings, queries)) -------------------------------------------------------------------------------- /src/data_structures/bloom-filter/README.md: -------------------------------------------------------------------------------- 1 | # Bloom Filter 2 | 3 | A **bloom filter** is a space-efficient probabilistic 4 | data structure designed to test whether an element 5 | is present in a set. It is designed to be blazingly 6 | fast and use minimal memory at the cost of potential 7 | false positives. False positive matches are possible, 8 | but false negatives are not – in other words, a query 9 | returns either "possibly in set" or "definitely not in set". 10 | 11 | Bloom proposed the technique for applications where the 12 | amount of source data would require an impractically large 13 | amount of memory if "conventional" error-free hashing 14 | techniques were applied. 15 | 16 | ## Algorithm description 17 | 18 | An empty Bloom filter is a bit array of `m` bits, all 19 | set to `0`. There must also be `k` different hash functions 20 | defined, each of which maps or hashes some set element to 21 | one of the `m` array positions, generating a uniform random 22 | distribution. Typically, `k` is a constant, much smaller 23 | than `m`, which is proportional to the number of elements 24 | to be added; the precise choice of `k` and the constant of 25 | proportionality of `m` are determined by the intended 26 | false positive rate of the filter. 27 | 28 | Here is an example of a Bloom filter, representing the 29 | set `{x, y, z}`. The colored arrows show the positions 30 | in the bit array that each set element is mapped to. The 31 | element `w` is not in the set `{x, y, z}`, because it 32 | hashes to one bit-array position containing `0`. For 33 | this figure, `m = 18` and `k = 3`. 34 | 35 | ![Bloom Filter](https://upload.wikimedia.org/wikipedia/commons/a/ac/Bloom_filter.svg) 36 | 37 | ## Operations 38 | 39 | There are two main operations a bloom filter can 40 | perform: _insertion_ and _search_. Search may result in 41 | false positives. Deletion is not possible. 42 | 43 | In other words, the filter can take in items. When 44 | we go to check if an item has previously been 45 | inserted, it can tell us either "no" or "maybe". 46 | 47 | Both insertion and search are `O(1)` operations. 48 | 49 | ## Making the filter 50 | 51 | A bloom filter is created by allotting a certain size. 52 | In our example, we use `100` as a default length. All 53 | locations are initialized to `false`. 54 | 55 | ### Insertion 56 | 57 | During insertion, a number of hash functions, 58 | in our case `3` hash functions, are used to create 59 | hashes of the input. These hash functions output 60 | indexes. At every index received, we simply change 61 | the value in our bloom filter to `true`. 62 | 63 | ### Search 64 | 65 | During a search, the same hash functions are called 66 | and used to hash the input. We then check if the 67 | indexes received _all_ have a value of `true` inside 68 | our bloom filter. If they _all_ have a value of 69 | `true`, we know that the bloom filter may have had 70 | the value previously inserted. 71 | 72 | However, it's not certain, because it's possible 73 | that other values previously inserted flipped the 74 | values to `true`. The values aren't necessarily 75 | `true` due to the item currently being searched for. 76 | Absolute certainty is impossible unless only a single 77 | item has previously been inserted. 78 | 79 | While checking the bloom filter for the indexes 80 | returned by our hash functions, if even one of them 81 | has a value of `false`, we definitively know that the 82 | item was not previously inserted. 83 | 84 | ## False Positives 85 | 86 | The probability of false positives is determined by 87 | three factors: the size of the bloom filter, the 88 | number of hash functions we use, and the number 89 | of items that have been inserted into the filter. 90 | 91 | The formula to calculate probablity of a false positive is: 92 | 93 | ( 1 - e -kn/m ) k 94 | 95 | `k` = number of hash functions 96 | 97 | `m` = filter size 98 | 99 | `n` = number of items inserted 100 | 101 | These variables, `k`, `m`, and `n`, should be picked based 102 | on how acceptable false positives are. If the values 103 | are picked and the resulting probability is too high, 104 | the values should be tweaked and the probability 105 | re-calculated. 106 | 107 | ## Applications 108 | 109 | A bloom filter can be used on a blogging website. If 110 | the goal is to show readers only articles that they 111 | have never seen before, a bloom filter is perfect. 112 | It can store hashed values based on the articles. After 113 | a user reads a few articles, they can be inserted into 114 | the filter. The next time the user visits the site, 115 | those articles can be filtered out of the results. 116 | 117 | Some articles will inevitably be filtered out by mistake, 118 | but the cost is acceptable. It's ok if a user never sees 119 | a few articles as long as they have other, brand new ones 120 | to see every time they visit the site. 121 | 122 | ## References 123 | 124 | - [Wikipedia](https://en.wikipedia.org/wiki/Bloom_filter) 125 | - [GeeksforGeeks](https://www.geeksforgeeks.org/bloom-filters-introduction-and-python-implementation/) 126 | - [Bloom Filters by Example](http://llimllib.github.io/bloomfilter-tutorial/) 127 | - [Calculating False Positive Probability](https://hur.st/bloomfilter/?n=4&p=&m=18&k=3) 128 | - [Bloom Filters on Medium](https://blog.medium.com/what-are-bloom-filters-1ec2a50c68ff) 129 | - [Bloom Filters on YouTube](https://www.youtube.com/watch?v=bEmBh1HtYrw) 130 | -------------------------------------------------------------------------------- /src/data_structures/dictionaries/README.md: -------------------------------------------------------------------------------- 1 | # Python Dictionary 2 | 3 | Python provides another composite data type called a dictionary, which is similar to a list in that it is a collection of objects. 4 | 5 | **Dictionary** in Python is an unordered collection of data values, used to store data values like a map, which unlike other Data Types that hold only single value as an element, Dictionary holds key:value pair. Key value is provided in the dictionary to make it more optimized. 6 | 7 | Note – Keys in a dictionary doesn’t allows Polymorphism. 8 | 9 | ## Creating a Dictionary 10 | 11 | In Python, a Dictionary can be created by placing sequence of elements within curly {} braces, separated by ‘comma’. Dictionary holds a pair of values, one being the Key and the other corresponding pair element being its Key:value. Values in a dictionary can be of any datatype and can be duplicated, whereas keys can’t be repeated and must be immutable. 12 | 13 | Note – Dictionary keys are case sensitive, same name but different cases of Key will be treated distinctly. 14 | 15 | ![Nested Dictionary - GeeksforGeeks](https://media.geeksforgeeks.org/wp-content/uploads/Dictionary-Creation-1.jpg) 16 | 17 | ## References 18 | 19 | - [GeeksforGeeks](https://www.geeksforgeeks.org/python-dictionary/) 20 | 21 | -------------------------------------------------------------------------------- /src/data_structures/disjoint-set/README.md: -------------------------------------------------------------------------------- 1 | # Disjoint Set 2 | 3 | **Disjoint-set** data structure (also called a union–find data structure or merge–find set) is a data 4 | structure that tracks a set of elements partitioned into a number of disjoint (non-overlapping) subsets. 5 | It provides near-constant-time operations (bounded by the inverse Ackermann function) to *add new sets*, 6 | to *merge existing sets*, and to *determine whether elements are in the same set*. 7 | In addition to many other uses (see the Applications section), disjoint-sets play a key role in Kruskal's algorithm for finding the minimum spanning tree of a graph. 8 | 9 | ![disjoint set](https://upload.wikimedia.org/wikipedia/commons/6/67/Dsu_disjoint_sets_init.svg) 10 | 11 | *MakeSet* creates 8 singletons. 12 | 13 | ![disjoint set](https://upload.wikimedia.org/wikipedia/commons/a/ac/Dsu_disjoint_sets_final.svg) 14 | 15 | After some operations of *Union*, some sets are grouped together. 16 | 17 | ## References 18 | 19 | - [Wikipedia](https://en.wikipedia.org/wiki/Disjoint-set_data_structure) 20 | - [GeekforGeeks](https://www.geeksforgeeks.org/disjoint-set-data-structures/) 21 | -------------------------------------------------------------------------------- /src/data_structures/graph/README.md: -------------------------------------------------------------------------------- 1 | # Graph 2 | 3 | In computer science, a **graph** is an abstract data type 4 | that is meant to implement the undirected graph and 5 | directed graph concepts from mathematics, specifically 6 | the field of graph theory 7 | 8 | A graph data structure consists of a finite (and possibly 9 | mutable) set of vertices or nodes or points, together 10 | with a set of unordered pairs of these vertices for an 11 | undirected graph or a set of ordered pairs for a 12 | directed graph. These pairs are known as edges, arcs, 13 | or lines for an undirected graph and as arrows, 14 | directed edges, directed arcs, or directed lines 15 | for a directed graph. The vertices may be part of 16 | the graph structure, or may be external entities 17 | represented by integer indices or references. 18 | 19 | ![Graph](https://www.tutorialspoint.com/data_structures_algorithms/images/graph.jpg) 20 | 21 | ## References 22 | 23 | - [Wikipedia](https://en.wikipedia.org/wiki/Graph_(abstract_data_type)) 24 | - [GeeksforGeeks](https://www.geeksforgeeks.org/graph-data-structure-and-algorithms/) 25 | - [Introduction to Graphs on YouTube](https://www.youtube.com/watch?v=gXgEDyodOJU&index=9&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 26 | - [Graphs representation on YouTube](https://www.youtube.com/watch?v=k1wraWzqtvQ&index=10&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 27 | -------------------------------------------------------------------------------- /src/data_structures/hash-table/README.md: -------------------------------------------------------------------------------- 1 | # Hash Table 2 | 3 | In computing, a **hash table** (hash map) is a data 4 | structure which implements an *associative array* 5 | abstract data type, a structure that can *map keys 6 | to values*. A hash table uses a *hash function* to 7 | compute an index into an array of buckets or slots, 8 | from which the desired value can be found 9 | 10 | Ideally, the hash function will assign each key to a 11 | unique bucket, but most hash table designs employ an 12 | imperfect hash function, which might cause hash 13 | collisions where the hash function generates the same 14 | index for more than one key. Such collisions must be 15 | accommodated in some way. 16 | 17 | ![Hash Table](https://upload.wikimedia.org/wikipedia/commons/7/7d/Hash_table_3_1_1_0_1_0_0_SP.svg) 18 | 19 | Hash collision resolved by separate chaining. 20 | 21 | ![Hash Collision](https://upload.wikimedia.org/wikipedia/commons/d/d0/Hash_table_5_0_1_1_1_1_1_LL.svg) 22 | 23 | ## References 24 | 25 | - [Wikipedia](https://en.wikipedia.org/wiki/Hash_table) 26 | - [GeeksforGeeks](https://www.geeksforgeeks.org/hashing-data-structure/) 27 | - [YouTube](https://www.youtube.com/watch?v=shs0KM3wKv8&index=4&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 28 | -------------------------------------------------------------------------------- /src/data_structures/linked_list/README.md: -------------------------------------------------------------------------------- 1 | # Linked List 2 | 3 | In computer science, a **linked list** is a linear collection 4 | of data elements, in which linear order is not given by 5 | their physical placement in memory. Instead, each 6 | element points to the next. It is a data structure 7 | consisting of a group of nodes which together represent 8 | a sequence. Under the simplest form, each node is 9 | composed of data and a reference (in other words, 10 | a link) to the next node in the sequence. This structure 11 | allows for efficient insertion or removal of elements 12 | from any position in the sequence during iteration. 13 | More complex variants add additional links, allowing 14 | efficient insertion or removal from arbitrary element 15 | references. A drawback of linked lists is that access 16 | time is linear (and difficult to pipeline). Faster 17 | access, such as random access, is not feasible. Arrays 18 | have better cache locality as compared to linked lists. 19 | 20 | ![Linked List](https://upload.wikimedia.org/wikipedia/commons/6/6d/Singly-linked-list.svg) 21 | 22 | ## Pseudocode for Basic Operations 23 | 24 | ### Insert 25 | 26 | ```text 27 | Add(value) 28 | Pre: value is the value to add to the list 29 | Post: value has been placed at the tail of the list 30 | n ← node(value) 31 | if head = ø 32 | head ← n 33 | tail ← n 34 | else 35 | tail.next ← n 36 | tail ← n 37 | end if 38 | end Add 39 | ``` 40 | 41 | ```text 42 | Prepend(value) 43 | Pre: value is the value to add to the list 44 | Post: value has been placed at the head of the list 45 | n ← node(value) 46 | n.next ← head 47 | head ← n 48 | if tail = ø 49 | tail ← n 50 | end 51 | end Prepend 52 | ``` 53 | 54 | ### Search 55 | 56 | ```text 57 | Contains(head, value) 58 | Pre: head is the head node in the list 59 | value is the value to search for 60 | Post: the item is either in the linked list, true; otherwise false 61 | n ← head 62 | while n != ø and n.value != value 63 | n ← n.next 64 | end while 65 | if n = ø 66 | return false 67 | end if 68 | return true 69 | end Contains 70 | ``` 71 | 72 | ### Delete 73 | 74 | ```text 75 | Remove(head, value) 76 | Pre: head is the head node in the list 77 | value is the value to remove from the list 78 | Post: value is removed from the list, true, otherwise false 79 | if head = ø 80 | return false 81 | end if 82 | n ← head 83 | if n.value = value 84 | if head = tail 85 | head ← ø 86 | tail ← ø 87 | else 88 | head ← head.next 89 | end if 90 | return true 91 | end if 92 | while n.next != ø and n.next.value != value 93 | n ← n.next 94 | end while 95 | if n.next != ø 96 | if n.next = tail 97 | tail ← n 98 | end if 99 | n.next ← n.next.next 100 | return true 101 | end if 102 | return false 103 | end Remove 104 | ``` 105 | 106 | ### Traverse 107 | 108 | ```text 109 | Traverse(head) 110 | Pre: head is the head node in the list 111 | Post: the items in the list have been traversed 112 | n ← head 113 | while n != ø 114 | yield n.value 115 | n ← n.next 116 | end while 117 | end Traverse 118 | ``` 119 | 120 | ### Traverse in Reverse 121 | 122 | ```text 123 | ReverseTraversal(head, tail) 124 | Pre: head and tail belong to the same list 125 | Post: the items in the list have been traversed in reverse order 126 | if tail != ø 127 | curr ← tail 128 | while curr != head 129 | prev ← head 130 | while prev.next != curr 131 | prev ← prev.next 132 | end while 133 | yield curr.value 134 | curr ← prev 135 | end while 136 | yield curr.value 137 | end if 138 | end ReverseTraversal 139 | ``` 140 | 141 | ## Complexities 142 | 143 | ### Time Complexity 144 | 145 | | Access | Search | Insertion | Deletion | 146 | | :-------: | :-------: | :-------: | :-------: | 147 | | O(n) | O(n) | O(1) | O(n) | 148 | 149 | ### Space Complexity 150 | 151 | O(n) 152 | 153 | # Doubly Linked List 154 | In computer science, a **doubly linked list** is a linked data structure that 155 | consists of a set of sequentially linked records called nodes. Each node contains 156 | two fields, called links, that are references to the previous and to the next 157 | node in the sequence of nodes. The beginning and ending nodes' previous and next 158 | links, respectively, point to some kind of terminator, typically a sentinel 159 | node or null, to facilitate traversal of the list. If there is only one 160 | sentinel node, then the list is circularly linked via the sentinel node. It can 161 | be conceptualized as two singly linked lists formed from the same data items, 162 | but in opposite sequential orders. 163 | 164 | ![Doubly Linked List](https://upload.wikimedia.org/wikipedia/commons/5/5e/Doubly-linked-list.svg) 165 | 166 | The two node links allow traversal of the list in either direction. While adding 167 | or removing a node in a doubly linked list requires changing more links than the 168 | same operations on a singly linked list, the operations are simpler and 169 | potentially more efficient (for nodes other than first nodes) because there 170 | is no need to keep track of the previous node during traversal or no need 171 | to traverse the list to find the previous node, so that its link can be modified. 172 | 173 | ## Pseudocode for Basic Operations 174 | 175 | ### Insert 176 | 177 | ```text 178 | Add(value) 179 | Pre: value is the value to add to the list 180 | Post: value has been placed at the tail of the list 181 | n ← node(value) 182 | if head = ø 183 | head ← n 184 | tail ← n 185 | else 186 | n.previous ← tail 187 | tail.next ← n 188 | tail ← n 189 | end if 190 | end Add 191 | ``` 192 | 193 | ### Delete 194 | 195 | ```text 196 | Remove(head, value) 197 | Pre: head is the head node in the list 198 | value is the value to remove from the list 199 | Post: value is removed from the list, true; otherwise false 200 | if head = ø 201 | return false 202 | end if 203 | if value = head.value 204 | if head = tail 205 | head ← ø 206 | tail ← ø 207 | else 208 | head ← head.next 209 | head.previous ← ø 210 | end if 211 | return true 212 | end if 213 | n ← head.next 214 | while n = ø and value !== n.value 215 | n ← n.next 216 | end while 217 | if n = tail 218 | tail ← tail.previous 219 | tail.next ← ø 220 | return true 221 | else if n = ø 222 | n.previous.next ← n.next 223 | n.next.previous ← n.previous 224 | return true 225 | end if 226 | return false 227 | end Remove 228 | ``` 229 | 230 | ### Reverse Traversal 231 | 232 | ```text 233 | ReverseTraversal(tail) 234 | Pre: tail is the node of the list to traverse 235 | Post: the list has been traversed in reverse order 236 | n ← tail 237 | while n = ø 238 | yield n.value 239 | n ← n.previous 240 | end while 241 | end Reverse Traversal 242 | ``` 243 | 244 | ## Complexities 245 | 246 | ## Time Complexity 247 | 248 | | Access | Search | Insertion | Deletion | 249 | | :-------: | :-------: | :-------: | :-------: | 250 | | O(n) | O(n) | O(1) | O(n) | 251 | 252 | ### Space Complexity 253 | 254 | O(n) 255 | 256 | ## References 257 | 258 | - [GeeksforGeeks](https://www.geeksforgeeks.org/data-structures/linked-list/) 259 | - [Wikipedia Linked List](https://en.wikipedia.org/wiki/Linked_list) 260 | - [Wikipedia Doubly Linked List](https://en.wikipedia.org/wiki/Doubly_linked_list) 261 | 262 | -------------------------------------------------------------------------------- /src/data_structures/linked_list/detecting_loops_linked_list.py: -------------------------------------------------------------------------------- 1 | """ 2 | Problem Statement 3 | Implement a function that detects if a loop exists in a linked list. 4 | The way we'll do this is by having two pointers, called "runners", 5 | moving through the list at different rates. Typically we have a "slow" runner 6 | which moves at one node per step and a "fast" runner that moves at two nodes per step. 7 | 8 | If a loop exists in the list, 9 | the fast runner will eventually move behind the slow runner 10 | as it moves to the beginning of the loop. 11 | Eventually it will catch up to the slow runner and both runners 12 | will be pointing to the same node at the same time. 13 | If this happens then you know there is a loop in the linked list. 14 | 15 | """ 16 | class Node: 17 | 18 | # Constructor to create a new node 19 | def __init__(self, data): 20 | self.data = data # Pointer to data 21 | self.next = None # Initialize next as null 22 | 23 | # Linked List class contains a Node 24 | class LinkedList: 25 | 26 | # Constructor for empty linked list 27 | def __init__(self): 28 | self.head = None 29 | 30 | # Given a reference to the head, 31 | # appends a new node 32 | def append(self, data): 33 | 34 | # Put the new node in the data 35 | new_node = Node(data) 36 | 37 | # If the Linked List is empty, 38 | # make the new node as head 39 | if self.head == None: 40 | self.head = new_node 41 | return 42 | 43 | current_node = self.head 44 | 45 | # Now traverse till the new node 46 | while current_node.next: 47 | current_node = current_node.next 48 | 49 | # Change the next of the current node 50 | current_node.next = new_node 51 | return 52 | 53 | 54 | list_with_loop = LinkedList() 55 | list_with_loop.append(2) 56 | list_with_loop.append(-1) 57 | list_with_loop.append(7) 58 | list_with_loop.append(0) 59 | list_with_loop.append(3) 60 | 61 | # Creating a loop where the last node points back to the second node 62 | loop_start = list_with_loop.head.next 63 | 64 | node = list_with_loop.head 65 | while node.next: 66 | node = node.next 67 | node.next = loop_start 68 | 69 | def is_circular(linked_list): 70 | """ 71 | Determine wether the Linked List is circular or not 72 | 73 | Args: 74 | linked_list(obj): Linked List to be checked 75 | Returns: 76 | bool: Return True if the linked list is circular, return False otherwise 77 | """ 78 | 79 | if linked_list.head is None: 80 | return False 81 | 82 | slow = linked_list.head 83 | fast = linked_list.head 84 | 85 | while fast and fast.next: 86 | # slow pointer moves one node 87 | slow = slow.next 88 | # fast pointer moves two nodes 89 | fast = fast.next.next 90 | 91 | if slow == fast: 92 | return True 93 | 94 | # If we get to a node where fast doesn't have a next node or doesn't exist itself, 95 | # the list has an end and isn't circular 96 | return False 97 | # Test Cases 98 | 99 | # Creating a small loop 100 | small_loop = LinkedList() 101 | small_loop.append(1) 102 | small_loop.head.next = small_loop.head 103 | 104 | # Linked list without loop 105 | test_loop = LinkedList() 106 | test_loop.append(2) 107 | test_loop.append(-1) 108 | test_loop.append(7) 109 | test_loop.append(0) 110 | test_loop.append(3) 111 | 112 | print ("Pass" if is_circular(list_with_loop) else "Fail") 113 | print ("Pass" if not is_circular(test_loop) else "Fail") 114 | print ("Pass" if is_circular(small_loop) else "Fail") 115 | print ("Pass" if not is_circular(LinkedList()) else "Fail") 116 | -------------------------------------------------------------------------------- /src/data_structures/linked_list/doubly_linked_list.py: -------------------------------------------------------------------------------- 1 | # Doubly LinkedList 2 | ''' 3 | head second third 4 | | | | 5 | | | | 6 | +----+------+ +----+------+ +----+------+ 7 | | 1 | o-------->| 2 | o-------->| 3 | null | 8 | | | o<--------| | o<--------| | | 9 | +----+------+ +----+------+ +----+------+ 10 | ''' 11 | # Node class of a linked list 12 | class DoubleNode: 13 | # Constructor to create a new node 14 | def __init__(self, data): 15 | self.data = data 16 | self.next = None # Reference to next node 17 | self.previous = None # Reference to the previous node 18 | 19 | class DoublyLinkedList: 20 | # Constructor for empty linked list 21 | def __init__(self): 22 | self.head = None 23 | self.tail = None 24 | 25 | # Given a reference to the head 26 | # appends a new node at the end 27 | def append(self, data): 28 | # Allocates node and put in the data 29 | new_node = DoubleNode(data) 30 | 31 | # This new node is going to be the last node 32 | new_node.next = None 33 | 34 | # If the Linked List is empty, 35 | # make the new node as head 36 | if self.head == None: 37 | new_node.previous = None 38 | self.head = new_node 39 | return 40 | 41 | last_node = self.head 42 | while last_node.next: 43 | last_node = last_node.next 44 | 45 | last_node.next = new_node 46 | 47 | new_node.previous = last_node 48 | return 49 | 50 | # Returns the length of the linked list. 51 | def length(self): 52 | if self.head == None: 53 | return 0 54 | current_node = self.head 55 | total = 0 # Init count 56 | # Loop while end of linked list is not reached 57 | while current_node: 58 | total += 1 59 | current_node = current_node.next 60 | return total 61 | 62 | # Converts a linked list back into a Python list 63 | def to_list(self): 64 | 65 | # Init as null 66 | node_data = [] 67 | current_node = self.head 68 | 69 | while current_node: 70 | node_data.append(current_node.data) 71 | current_node = current_node.next 72 | return node_data 73 | 74 | def reverse_linked_list(self): 75 | if self.head is None: 76 | print("The list has no element to reverse") 77 | return 0 78 | # The next reference of the start node 79 | # should be set none because the first node will 80 | # become the last node in the reversed list. 81 | # The previous reference of the last node should be set to None 82 | # since the last node will become the previous node 83 | # The next references of the nodes (except the first and last node) in the original list 84 | # Should be swapped with the previous references. 85 | current_node = self.head 86 | new_node = current_node.next 87 | current_node.next = None 88 | current_node.previous = new_node 89 | while new_node != None: 90 | new_node .previous = new_node .next 91 | new_node .next = current_node 92 | current_node = new_node 93 | new_node = new_node .previous 94 | self.head = current_node 95 | 96 | def display(self): 97 | contents = self.head 98 | # If the list is null 99 | if contents is None: 100 | print("List has no element") 101 | while contents: 102 | print(contents.data) 103 | contents = contents.next 104 | print("----------") # to see better the lists 105 | 106 | # This insert a node at the start 107 | def insert_at_start(self, data): 108 | if self.head == None: 109 | new_node = DoubleNode(data) 110 | self.head = new_node 111 | print("Node inserted") 112 | return 113 | new_node = DoubleNode(data) 114 | new_node.next = self.head 115 | self.head.previous = new_node 116 | self.head = new_node 117 | 118 | # This insert a node at the end 119 | def insert_at_end(self, data): 120 | if self.head == None: 121 | new_node = DoubleNode(data) 122 | self.head = new_node 123 | return 124 | current_node = self.head 125 | while current_node.next != None: 126 | current_node = current_node.next 127 | new_node = DoubleNode(data) 128 | current_node.next = new_node 129 | new_node.previous = current_node 130 | 131 | # Deleting Elements from the Start 132 | def remove_at_start(self): 133 | if self.head == None: 134 | print("The list has no element to delete") 135 | return 136 | if self.head.next == None: 137 | self.head = None 138 | return 139 | self.head = self.head.next 140 | self.start_prev = None 141 | 142 | # Deleting Elements from the end 143 | def remove_at_end(self): 144 | if self.head == None: 145 | print("The list has no element to delete") 146 | return 147 | if self.head.next == None: 148 | self.head = None 149 | return 150 | current_node = self.head 151 | while current_node.next != None: 152 | current_node = current_node.next 153 | current_node.previous.next = None 154 | 155 | # This remove a node with the specified value 156 | def remove_element_by_value(self, value): 157 | if self.head == None: 158 | print("The list has no element to delete") 159 | return 160 | if self.head.next == None: 161 | if self.head.item == value: 162 | self.head = None 163 | else: 164 | print("Item not found") 165 | return 166 | 167 | if self.head.data == value: 168 | self.head = self.head.next 169 | self.head.previous = None 170 | return 171 | 172 | current_node = self.head 173 | while current_node.next != None: 174 | if current_node.data == value: 175 | break 176 | current_node = current_node.next 177 | if current_node.next != None: 178 | current_node.previous.next = current_node.next 179 | current_node.next.previous = current_node.previous 180 | else: 181 | if current_node.data == value: 182 | current_node.previous.next = None 183 | else: 184 | print("Element not found") 185 | 186 | 187 | 188 | # Test 189 | my_list = DoublyLinkedList() 190 | my_list.display() 191 | # Add the elements 192 | my_list.append(3) 193 | my_list.append(2) 194 | my_list.append(7) 195 | my_list.append(1) 196 | 197 | my_list.display() 198 | 199 | print("The total number of elements are: " + str(my_list.length())) 200 | print(my_list.to_list()) # Python list 201 | print("---------") 202 | my_list.reverse_linked_list() # Reverse linked list 203 | my_list.display() 204 | 205 | my_list.remove_at_start() 206 | my_list.display() 207 | 208 | my_list.remove_at_end() 209 | my_list.display() 210 | 211 | my_list.insert_at_start(1) 212 | my_list.display() 213 | 214 | my_list.insert_at_end(3) 215 | my_list.display() 216 | 217 | my_list.remove_element_by_value(7) 218 | my_list.display() 219 | -------------------------------------------------------------------------------- /src/data_structures/linked_list/flattening_nested_linked_list.py: -------------------------------------------------------------------------------- 1 | """ 2 | Suppose you have a linked list where the value of 3 | each node is a sorted linked list (i.e., it is a _nested_ list). 4 | Your task is to _flatten_ this nested list—that is, to combine all 5 | nested lists into a single (sorted) linked list. 6 | """ 7 | # Use this class as the nodes in your linked list 8 | class Node: 9 | def __init__(self, value): 10 | self.value = value 11 | self.next = None 12 | 13 | def __repr__(self): 14 | return str(self.value) 15 | 16 | 17 | class LinkedList: 18 | def __init__(self, head): 19 | self.head = head 20 | 21 | def append(self, value): 22 | if self.head is None: 23 | self.head = Node(value) 24 | return 25 | node = self.head 26 | while node.next is not None: 27 | node = node.next 28 | node.next = Node(value) 29 | 30 | def flatten_deep(self): 31 | value_list = [] 32 | node = self.head 33 | 34 | while node.next is not None: 35 | value_list.append(node.value) 36 | node = node.next 37 | value_list.append(node.value) 38 | return value_list 39 | 40 | def merge(list1, list2): 41 | merged = LinkedList(None) 42 | if list1 is None: 43 | return list2 44 | if list2 is None: 45 | return list1 46 | list1_elt = list1.head 47 | list2_elt = list2.head 48 | while list1_elt is not None or list2_elt is not None: 49 | if list1_elt is None: 50 | merged.append(list2_elt) 51 | list2_elt = list2_elt.next 52 | elif list2_elt is None: 53 | merged.append(list1_elt) 54 | list1_elt = list1_elt.next 55 | elif list1_elt.value <= list2_elt.value: 56 | merged.append(list1_elt) 57 | list1_elt = list1_elt.next 58 | else: 59 | merged.append(list2_elt) 60 | list2_elt = list2_elt.next 61 | return merged 62 | 63 | 64 | class NestedLinkedList(LinkedList): 65 | def flatten(self): 66 | values = [] 67 | for element in self.flatten_deep(): 68 | values.append(element.flatten_deep()) 69 | values = [item for sublist in values for item in sublist] 70 | values.sort() 71 | return values 72 | 73 | linked_list = LinkedList(Node(1)) 74 | linked_list.append(3) 75 | linked_list.append(5) 76 | 77 | second_linked_list = LinkedList(Node(2)) 78 | second_linked_list.append(4) 79 | 80 | nested_linked_list = NestedLinkedList(Node(linked_list)) 81 | nested_linked_list.append(second_linked_list) 82 | 83 | """ 84 | Structure 'nested_linked_list' should now have 2 nodes. 85 | The head node is a linked list containing '1, 3, 5'. 86 | The second node is a linked list containing '2, 4'. 87 | 88 | Calling 'flatten' should return a linked list containing '1, 2, 3, 4, 5'. 89 | """ 90 | solution = nested_linked_list.flatten() 91 | print(solution == [1,2,3,4,5]) -------------------------------------------------------------------------------- /src/data_structures/linked_list/singly_linked_list.py: -------------------------------------------------------------------------------- 1 | # Singly LinkedList 2 | ''' 3 | head second third 4 | | | | 5 | | | | 6 | +----+------+ +----+------+ +----+------+ 7 | | 1 | o-------->| 2 | o-------->| 3 | null | 8 | +----+------+ +----+------+ +----+------+ 9 | ''' 10 | # Node class of a linked list 11 | class Node: 12 | 13 | # Constructor to create a new node 14 | def __init__(self, data): 15 | self.data = data # Pointer to data 16 | self.next = None # Initialize next as null 17 | 18 | # Linked List class contains a Node 19 | class LinkedList: 20 | 21 | # Constructor for empty linked list 22 | def __init__(self): 23 | self.head = None 24 | 25 | # Given a reference to the head, 26 | # appends a new node 27 | def append(self, data): 28 | 29 | # Put the new node in the data 30 | new_node = Node(data) 31 | 32 | # If the Linked List is empty, 33 | # make the new node as head 34 | if self.head == None: 35 | self.head = new_node 36 | return 37 | 38 | current_node = self.head 39 | 40 | # Now traverse till the new node 41 | while current_node.next: 42 | current_node = current_node.next 43 | 44 | # Change the next of the current node 45 | current_node.next = new_node 46 | return 47 | 48 | # Returns the length of the linked list. 49 | def length(self): 50 | if self.head == None: 51 | return 0 52 | current_node = self.head 53 | total = 0 # Init count 54 | # Loop while end of linked list is not reached 55 | while current_node: 56 | total += 1 57 | current_node = current_node.next 58 | return total 59 | 60 | # Converts a linked list back into a Python list 61 | def to_list(self): 62 | 63 | # Init as null 64 | node_data = [] 65 | current_node = self.head 66 | 67 | while current_node: 68 | node_data.append(current_node.data) 69 | current_node = current_node.next 70 | return node_data 71 | # Returns the value of the node at 'index'. 72 | def get(self, index): 73 | if index >= self.length() or index < 0: 74 | print("ERROR: 'Get' Index out of range!") 75 | return None 76 | current_idx = 0 77 | current_node = self.head 78 | while current_node != None: 79 | if current_idx == index: 80 | return current_node.data 81 | current_node = current_node.next 82 | current_idx += 1 83 | # reverse a linked list 84 | def reverse_linkedlist(self): 85 | previous_node = None 86 | current_node = self.head 87 | while current_node != None: 88 | next = current_node.next 89 | current_node.next = previous_node 90 | previous_node = current_node 91 | current_node = next 92 | self.head = previous_node 93 | 94 | # Searching for an element is quite similar to counting or traversing a linked list 95 | def search_item(self, value): 96 | if self.head == None: 97 | print("List has no elements") 98 | return 99 | current_node = self.head 100 | while current_node != None: 101 | if current_node.data == value: 102 | print("Item found") 103 | return True 104 | current_node = current_node.next 105 | print("Item not found") 106 | return False 107 | 108 | 109 | # This function prints contents of linked list 110 | # starting from the head of the linked list 111 | # traverse function is as follows 112 | def display(self): 113 | contents = self.head 114 | # If the list is null 115 | if contents is None: 116 | print("List has no element") 117 | while contents: 118 | print(contents.data) 119 | contents = contents.next 120 | print("----------") # to see better the lists 121 | 122 | # Deleting an element or item from the start 123 | def remove_at_start(self): 124 | if self.head == None: 125 | print("The list has no element to delete") 126 | return 127 | self.head = self.head.next 128 | 129 | # Deleting an element or item at the end 130 | def remove_at_end(self): 131 | if self.head is None: 132 | print("The list has no element to delete") 133 | return 134 | current_node = self.head 135 | while current_node.next.next != None: 136 | current_node = current_node.next 137 | current_node.next = None 138 | 139 | # This remove a node with the specified value 140 | def remove_element_by_value(self, value): 141 | # Store head node 142 | current_node = self.head 143 | 144 | # If head node itself holds the value to be deleted 145 | if current_node != None: 146 | if current_node.data == value: 147 | self.head = current_node.next 148 | current_node = None 149 | return 150 | 151 | # Search for the value to be deleted, keep track of the 152 | # previous node as we need to change 'prev.next' 153 | while current_node != None: 154 | if current_node.data == value: 155 | break 156 | prev = current_node 157 | current_node = current_node.next 158 | 159 | # if value was not present in linked list 160 | if current_node == None: 161 | return 162 | 163 | # Unlink the node from linked list 164 | prev.next = current_node.next 165 | current_node = None 166 | 167 | # Insert an item in a single linked list 168 | # add an item at the start of the list 169 | def insert_at_start(self, data): 170 | new_node = Node(data) 171 | new_node.next = self.head 172 | self.head = new_node 173 | 174 | # Insert an item in a single linked list 175 | # add an item at the end of the list 176 | def insert_at_end(self, data): 177 | new_node = Node(data) 178 | if self.head is None: 179 | self.head = new_node 180 | return 181 | current_node = self.head 182 | while current_node.next is not None: 183 | current_node = current_node.next 184 | current_node.next = new_node 185 | 186 | # Insert an item in a single linked list 187 | # add an item at any index of the list 188 | def insert_at_index (self, index, data): 189 | if index == 1: 190 | new_node = Node(data) 191 | new_node.next = self.head 192 | self.head = new_node 193 | i = 1 194 | current_node = self.head 195 | while i < index-1 and current_node is not None: 196 | current_node = current_node.next 197 | i = i + 1 198 | if current_node is None: 199 | print("ERROR: Index out of range!") 200 | else: 201 | new_node = Node(data) 202 | new_node.next = current_node.next 203 | current_node.next = new_node 204 | 205 | # Test 206 | my_list = LinkedList() 207 | my_list.display() 208 | # Add the elements 209 | my_list.append(3) 210 | my_list.append(2) 211 | my_list.append(7) 212 | my_list.append(1) 213 | 214 | my_list.display() 215 | 216 | print("The total number of elements are: " + str(my_list.length())) 217 | print(my_list.to_list()) # Python list 218 | print("---------") 219 | my_list.reverse_linkedlist() # Reverse linked list 220 | my_list.display() 221 | 222 | my_list.search_item(7) 223 | my_list.search_item(77) 224 | 225 | my_list.remove_at_start() 226 | my_list.display() 227 | 228 | my_list.remove_at_end() 229 | my_list.display() 230 | 231 | my_list.insert_at_start(1) 232 | my_list.display() 233 | 234 | my_list.insert_at_end(3) 235 | my_list.display() 236 | 237 | my_list.remove_element_by_value(3) 238 | my_list.display() 239 | 240 | my_list.insert_at_index(2, 88) 241 | my_list.display() -------------------------------------------------------------------------------- /src/data_structures/linked_list/skip_delete.py: -------------------------------------------------------------------------------- 1 | """ 2 | You are given the head of a linked list and two integers, 'i' and 'j'. 3 | You have to retain the first 'i' nodes and then delete the next 'j' nodes. 4 | Continue doing so until the end of the linked list. 5 | 6 | Example: 7 | 'linked-list = 1 2 3 4 5 6 7 8 9 10 11 12' 8 | 'i = 2' 9 | 'j = 3' 10 | 'Output = 1 2 6 7 11 12' 11 | """ 12 | # LinkedList Node class for your reference 13 | class Node: 14 | def __init__(self, data): 15 | self.data = data 16 | self.next = None 17 | 18 | def skip_i_delete_j(head, i, j): 19 | """ 20 | :param: head - head of linked list 21 | :param: i - first `i` nodes that are to be skipped 22 | :param: j - next `j` nodes that are to be deleted 23 | return - return the updated head of the linked list 24 | """ 25 | 26 | input_node = head 27 | trim_link_list = None 28 | 29 | position_counter = 1 30 | i_mode = True 31 | last_node = False 32 | 33 | while not last_node: 34 | if i_mode: # Additive mode 35 | if trim_link_list is None: 36 | trim_link_list = Node(input_node.data) 37 | else: 38 | position_tail = trim_link_list 39 | while position_tail.next: # Moving to the end of the list 40 | position_tail = position_tail.next 41 | position_tail.next = Node(input_node.data) 42 | 43 | if position_counter == i: 44 | i_mode = False 45 | position_counter = 0 46 | 47 | else: # Non-additive mode 48 | if position_counter == j: 49 | i_mode = True 50 | position_counter = 0 51 | 52 | position_counter += 1 53 | last_node = input_node.next is None 54 | input_node = input_node.next 55 | 56 | return trim_link_list 57 | 58 | # helper functions for testing purpose 59 | def create_linked_list(arr): 60 | if len(arr)==0: 61 | return None 62 | head = Node(arr[0]) 63 | tail = head 64 | for data in arr[1:]: 65 | tail.next = Node(data) 66 | tail = tail.next 67 | return head 68 | 69 | def print_linked_list(head): 70 | while head: 71 | print(head.data, end=' ') 72 | head = head.next 73 | print() 74 | 75 | def test_function(test_case): 76 | head = test_case[0] 77 | i = test_case[1] 78 | j = test_case[2] 79 | solution = test_case[3] 80 | 81 | temp = skip_i_delete_j(head, i, j) 82 | index = 0 83 | try: 84 | while temp is not None: 85 | if temp.data != solution[index]: 86 | print("Fail") 87 | return 88 | index += 1 89 | temp = temp.next 90 | print("Pass") 91 | except Exception: 92 | print("Fail") 93 | 94 | arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] 95 | i = 2 96 | j = 2 97 | head = create_linked_list(arr) 98 | solution = [1, 2, 5, 6, 9, 10] 99 | test_case = [head, i, j, solution] 100 | test_function(test_case) -------------------------------------------------------------------------------- /src/data_structures/linked_list/swap_node.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a linked list, swap the two nodes present at position 'i' and 'j'. 3 | The positions are based on 0-based indexing. 4 | Note: You have to swap the nodes and not just the values. 5 | 6 | Example: 7 | linked_list = 3 4 5 2 6 1 9 8 | positions = 3 4 9 | output = 3 4 5 6 2 1 9 10 | 11 | Explanation: 12 | The node at position 3 has the value '2' 13 | The node at position 4 has the value '6' 14 | Swapping these nodes will result in a final order of nodes of '3 4 5 6 2 1 9' 15 | """ 16 | 17 | class Node: 18 | """LinkedListNode class to be used for this problem""" 19 | def __init__(self, data): 20 | self.data = data 21 | self.next = None 22 | 23 | def swap_nodes(head, left_index, right_index): 24 | 25 | # if both the indices are same 26 | if left_index == right_index: 27 | return head 28 | 29 | 30 | left_previous = None 31 | left_current = None 32 | 33 | right_previous = None 34 | right_current = None 35 | 36 | count = 0 37 | temp = head 38 | new_head = None 39 | 40 | # find out previous and current node at both the indices 41 | while temp is not None: 42 | if count == left_index: 43 | left_current = temp 44 | elif count == right_index: 45 | right_current = temp 46 | break 47 | 48 | if left_current is None: 49 | left_previous = temp 50 | right_previous = temp 51 | temp = temp.next 52 | count += 1 53 | 54 | right_previous.next = left_current 55 | temp = left_current.next 56 | left_current.next = right_current.next 57 | 58 | # if both the indices are next to each other 59 | if left_index != right_index: 60 | right_current.next = temp 61 | 62 | # if the node at first index is head of the original linked list 63 | if left_previous is None: 64 | new_head = right_current 65 | else: 66 | left_previous.next = right_current 67 | new_head = head 68 | 69 | return new_head 70 | 71 | def test_function(test_case): 72 | head = test_case[0] 73 | left_index = test_case[1] 74 | right_index = test_case[2] 75 | 76 | left_node = None 77 | right_node = None 78 | 79 | temp = head 80 | index = 0 81 | try: 82 | while temp is not None: 83 | if index == left_index: 84 | left_node = temp 85 | if index == right_index: 86 | right_node = temp 87 | break 88 | index += 1 89 | temp = temp.next 90 | 91 | updated_head = swap_nodes(head, left_index, right_index) 92 | 93 | temp = updated_head 94 | index = 0 95 | pass_status = [False, False] 96 | 97 | while temp is not None: 98 | if index == left_index: 99 | pass_status[0] = temp is right_node 100 | if index == right_index: 101 | pass_status[1] = temp is left_node 102 | 103 | index += 1 104 | temp = temp.next 105 | 106 | if pass_status[0] and pass_status[1]: 107 | print("Pass") 108 | else: 109 | print("Fail") 110 | return updated_head 111 | except Exception as e: 112 | print("Fail") 113 | 114 | def create_linked_list(arr): 115 | if len(arr)==0: 116 | return None 117 | head = Node(arr[0]) 118 | tail = head 119 | for data in arr[1:]: 120 | tail.next = Node(data) 121 | tail = tail.next 122 | return head 123 | 124 | def print_linked_list(head): 125 | while head: 126 | print(head.data, end=" ") 127 | head = head.next 128 | print() 129 | 130 | arr = [3, 4, 5, 2, 6, 1, 9] 131 | head = create_linked_list(arr) 132 | left_index = 3 133 | right_index = 4 134 | 135 | test_case = [head, left_index, right_index] 136 | updated_head = test_function(test_case) -------------------------------------------------------------------------------- /src/data_structures/priority-queue/README.md: -------------------------------------------------------------------------------- 1 | # Priority Queue 2 | 3 | In computer science, a **priority queue** is an abstract data type 4 | which is like a regular queue or stack data structure, but where 5 | additionally each element has a "priority" associated with it. 6 | In a priority queue, an element with high priority is served before 7 | an element with low priority. If two elements have the same 8 | priority, they are served according to their order in the queue. 9 | 10 | While priority queues are often implemented with heaps, they are 11 | conceptually distinct from heaps. A priority queue is an abstract 12 | concept like "a list" or "a map"; just as a list can be implemented 13 | with a linked list or an array, a priority queue can be implemented 14 | with a heap or a variety of other methods such as an unordered 15 | array. 16 | 17 | ## References 18 | 19 | - [Wikipedia](https://en.wikipedia.org/wiki/Priority_queue) 20 | - [GeeksforGeeks](https://www.geeksforgeeks.org/priority-queue-set-1-introduction/) 21 | - [YouTube](https://www.youtube.com/watch?v=wptevk0bshY&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=6) 22 | -------------------------------------------------------------------------------- /src/data_structures/queue/README.md: -------------------------------------------------------------------------------- 1 | # Queue 2 | 3 | In computer science, a **queue** is a particular kind of abstract data 4 | type or collection in which the entities in the collection are 5 | kept in order and the principle (or only) operations on the 6 | collection are the addition of entities to the rear terminal 7 | position, known as enqueue, and removal of entities from the 8 | front terminal position, known as dequeue. This makes the queue 9 | a First-In-First-Out (FIFO) data structure. In a FIFO data 10 | structure, the first element added to the queue will be the 11 | first one to be removed. This is equivalent to the requirement 12 | that once a new element is added, all elements that were added 13 | before have to be removed before the new element can be removed. 14 | Often a peek or front operation is also entered, returning the 15 | value of the front element without dequeuing it. A queue is an 16 | example of a linear data structure, or more abstractly a 17 | sequential collection. 18 | 19 | Representation of a FIFO (first in, first out) queue 20 | 21 | ![Queue](https://upload.wikimedia.org/wikipedia/commons/5/52/Data_Queue.svg) 22 | 23 | ## References 24 | 25 | - [Wikipedia](https://en.wikipedia.org/wiki/Queue_(abstract_data_type)) 26 | - [GeeksforGeeks](https://www.geeksforgeeks.org/queue-data-structure/) 27 | - [YouTube](https://www.youtube.com/watch?v=wjI1WNcIntg&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=3&) 28 | -------------------------------------------------------------------------------- /src/data_structures/queue/queue.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Queue\n", 8 | "Operations associated with queue are:\n", 9 | "\n", 10 | "* Enqueue: Adds an item to the queue. If the queue is full, then it is said to be an Overflow condition – Time Complexity : $\\mathcal{O}(1)$\n", 11 | "* Dequeue: Removes an item from the queue. The items are popped in the same order in which they are pushed. If the queue is empty, then it is said to be an Underflow condition – Time Complexity : $\\mathcal{O}(1)$\n", 12 | "* Front: Get the front item from queue – Time Complexity : $\\mathcal{O}(1)$\n", 13 | "* Rear: Get the last item from queue – Time Complexity : $\\mathcal{O}(1)$\n" 14 | ] 15 | }, 16 | { 17 | "cell_type": "markdown", 18 | "metadata": {}, 19 | "source": [ 20 | "## Implementation using list\n", 21 | "\n", 22 | "List is a Python’s built-in data structure that can be used as a queue. Instead of `enqueue()` and `dequeue()`, `append()` and `pop()` function is used. However, lists are quite slow for this purpose because inserting or deleting an element at the beginning requires shifting all of the other elements by one, requiring $\\mathcal{O}(n)$ time." 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": 1, 28 | "metadata": {}, 29 | "outputs": [], 30 | "source": [ 31 | "# Python program to \n", 32 | "# demonstrate queue implementation \n", 33 | "# using list \n", 34 | " \n", 35 | "# Initializing a queue \n", 36 | "queue = [] " 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": 2, 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "# Adding elements to the queue \n", 46 | "queue.insert(0,'a') \n", 47 | "queue.insert(0,'b') \n", 48 | "queue.insert(0,'c') " 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": 3, 54 | "metadata": {}, 55 | "outputs": [ 56 | { 57 | "data": { 58 | "text/plain": [ 59 | "['c', 'b', 'a']" 60 | ] 61 | }, 62 | "execution_count": 3, 63 | "metadata": {}, 64 | "output_type": "execute_result" 65 | } 66 | ], 67 | "source": [ 68 | "queue" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": 4, 74 | "metadata": {}, 75 | "outputs": [ 76 | { 77 | "data": { 78 | "text/plain": [ 79 | "'a'" 80 | ] 81 | }, 82 | "execution_count": 4, 83 | "metadata": {}, 84 | "output_type": "execute_result" 85 | } 86 | ], 87 | "source": [ 88 | "queue.pop()" 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "metadata": {}, 94 | "source": [ 95 | "## Implementation using collections.deque\n", 96 | "Queue in Python can be implemented using deque class from the collections module. Deque is preferred over list in the cases where we need quicker append and pop operations from both the ends of container, as deque provides an$ \\mathcal{O}(1)$ time complexity for append and pop operations as compared to list which provides $\\mathcal{O}(n)$ time complexity. Instead of enqueue and deque, `append()` and `popleft()` functions are used." 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": 5, 102 | "metadata": {}, 103 | "outputs": [], 104 | "source": [ 105 | "# Python program to \n", 106 | "# demonstrate queue implementation \n", 107 | "# using collections.dequeue \n", 108 | "from collections import deque \n", 109 | " \n", 110 | "# Initializing a queue \n", 111 | "q = deque() " 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": 6, 117 | "metadata": {}, 118 | "outputs": [], 119 | "source": [ 120 | "# Adding elements to a queue \n", 121 | "q.append('a') \n", 122 | "q.append('b') \n", 123 | "q.append('c') " 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": 7, 129 | "metadata": {}, 130 | "outputs": [ 131 | { 132 | "data": { 133 | "text/plain": [ 134 | "deque(['a', 'b', 'c'])" 135 | ] 136 | }, 137 | "execution_count": 7, 138 | "metadata": {}, 139 | "output_type": "execute_result" 140 | } 141 | ], 142 | "source": [ 143 | "q" 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": 8, 149 | "metadata": {}, 150 | "outputs": [ 151 | { 152 | "data": { 153 | "text/plain": [ 154 | "'a'" 155 | ] 156 | }, 157 | "execution_count": 8, 158 | "metadata": {}, 159 | "output_type": "execute_result" 160 | } 161 | ], 162 | "source": [ 163 | "q.popleft()" 164 | ] 165 | }, 166 | { 167 | "cell_type": "markdown", 168 | "metadata": {}, 169 | "source": [ 170 | "## Implementation using queue.Queue\n", 171 | "Queue is built-in module of Python which is used to implement a `queue.queue.Queue(maxsize)` initializes a variable to a maximum size of maxsize. A maxsize of zero ‘0’ means a infinite queue. This Queue follows FIFO rule.\n", 172 | "There are various functions available in this module:\n", 173 | "\n", 174 | "* maxsize – Number of items allowed in the queue.\n", 175 | "* empty() – Return True if the queue is empty, False otherwise.\n", 176 | "* full() – Return True if there are maxsize items in the queue. If the queue was initialized with maxsize=0 (the default), then full() never returns True.\n", 177 | "* get() – Remove and return an item from the queue. If queue is empty, wait until an item is available.\n", 178 | "* get_nowait() – Return an item if one is immediately available, else raise QueueEmpty.\n", 179 | "* put(item) – Put an item into the queue. If the queue is full, wait until a free slot is available before adding the item.\n", 180 | "* put_nowait(item) – Put an item into the queue without blocking.\n", 181 | "* qsize() – Return the number of items in the queue. If no free slot is immediately available, raise QueueFull." 182 | ] 183 | }, 184 | { 185 | "cell_type": "code", 186 | "execution_count": 9, 187 | "metadata": {}, 188 | "outputs": [], 189 | "source": [ 190 | "# Python program to \n", 191 | "# demonstrate implementation of \n", 192 | "# queue using queue module \n", 193 | "from queue import Queue \n", 194 | " \n", 195 | "# Initializing a queue \n", 196 | "q = Queue(maxsize = 3) " 197 | ] 198 | }, 199 | { 200 | "cell_type": "code", 201 | "execution_count": 10, 202 | "metadata": {}, 203 | "outputs": [ 204 | { 205 | "data": { 206 | "text/plain": [ 207 | "0" 208 | ] 209 | }, 210 | "execution_count": 10, 211 | "metadata": {}, 212 | "output_type": "execute_result" 213 | } 214 | ], 215 | "source": [ 216 | "# qsize() give the maxsize of the queue\n", 217 | "q.qsize()" 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "execution_count": 11, 223 | "metadata": {}, 224 | "outputs": [], 225 | "source": [ 226 | "# Adding of element to queue \n", 227 | "q.put('a') \n", 228 | "q.put('b') \n", 229 | "q.put('c') " 230 | ] 231 | }, 232 | { 233 | "cell_type": "code", 234 | "execution_count": 12, 235 | "metadata": {}, 236 | "outputs": [ 237 | { 238 | "data": { 239 | "text/plain": [ 240 | "'a'" 241 | ] 242 | }, 243 | "execution_count": 12, 244 | "metadata": {}, 245 | "output_type": "execute_result" 246 | } 247 | ], 248 | "source": [ 249 | "# Removing element from queue \n", 250 | "q.get()" 251 | ] 252 | }, 253 | { 254 | "cell_type": "code", 255 | "execution_count": 13, 256 | "metadata": {}, 257 | "outputs": [ 258 | { 259 | "data": { 260 | "text/plain": [ 261 | "False" 262 | ] 263 | }, 264 | "execution_count": 13, 265 | "metadata": {}, 266 | "output_type": "execute_result" 267 | } 268 | ], 269 | "source": [ 270 | "# Return Boolean for Empty Queue \n", 271 | "q.empty()" 272 | ] 273 | } 274 | ], 275 | "metadata": { 276 | "kernelspec": { 277 | "display_name": "Python 3", 278 | "language": "python", 279 | "name": "python3" 280 | }, 281 | "language_info": { 282 | "codemirror_mode": { 283 | "name": "ipython", 284 | "version": 3 285 | }, 286 | "file_extension": ".py", 287 | "mimetype": "text/x-python", 288 | "name": "python", 289 | "nbconvert_exporter": "python", 290 | "pygments_lexer": "ipython3", 291 | "version": "3.8.2" 292 | } 293 | }, 294 | "nbformat": 4, 295 | "nbformat_minor": 4 296 | } 297 | -------------------------------------------------------------------------------- /src/data_structures/queue/queue.py: -------------------------------------------------------------------------------- 1 | # Queue 2 | 3 | class Queue: 4 | def __init__(self): 5 | self.storage = [] 6 | 7 | def size(self): 8 | return len(self.storage) 9 | 10 | def enqueue(self, item): 11 | self.storage.append(item) 12 | 13 | def dequeue(self): 14 | return self.storage.pop(0) 15 | 16 | 17 | # Setup 18 | q = Queue() 19 | q.enqueue(1) 20 | q.enqueue(2) 21 | q.enqueue(3) 22 | 23 | # Test size 24 | print ("Pass" if (q.size() == 3) else "Fail") 25 | 26 | # Test dequeue 27 | print ("Pass" if (q.dequeue() == 1) else "Fail") 28 | 29 | # Test enqueue 30 | q.enqueue(4) 31 | print ("Pass" if (q.dequeue() == 2) else "Fail") 32 | print ("Pass" if (q.dequeue() == 3) else "Fail") 33 | print ("Pass" if (q.dequeue() == 4) else "Fail") 34 | q.enqueue(5) 35 | print ("Pass" if (q.size() == 1) else "Fail") 36 | -------------------------------------------------------------------------------- /src/data_structures/queue/queue_using_stack.py: -------------------------------------------------------------------------------- 1 | # Here is our Stack Class 2 | 3 | class Stack: 4 | def __init__(self): 5 | self.items = [] 6 | 7 | def size(self): 8 | return len(self.items) 9 | 10 | def push(self, item): 11 | self.items.append(item) 12 | 13 | def pop(self): 14 | if self.size()==0: 15 | return None 16 | else: 17 | return self.items.pop() 18 | class Queue: 19 | def __init__(self): 20 | self.instorage = Stack() 21 | self.outstorage = Stack() 22 | 23 | def size(self): 24 | return self.outstorage.size() + self.instorage.size() 25 | 26 | def enqueue(self,item): 27 | self.instorage.push(item) 28 | 29 | def dequeue(self): 30 | if not self.outstorage.items: 31 | while self.instorage.items: 32 | self.outstorage.push(self.instorage.pop()) 33 | return self.outstorage.pop() 34 | 35 | 36 | q = [] 37 | q.insert(0,130) 38 | q.insert(0,132) 39 | q.insert(0,137) 40 | print(q) 41 | # Setup 42 | q = Queue() 43 | q.enqueue(1) 44 | q.enqueue(2) 45 | q.enqueue(3) 46 | 47 | # Test size 48 | print ("Pass" if (q.size() == 3) else "Fail") 49 | 50 | # Test dequeue 51 | print ("Pass" if (q.dequeue() == 1) else "Fail") 52 | 53 | # Test enqueue 54 | q.enqueue(4) 55 | print ("Pass" if (q.dequeue() == 2) else "Fail") 56 | print ("Pass" if (q.dequeue() == 3) else "Fail") 57 | print ("Pass" if (q.dequeue() == 4) else "Fail") 58 | q.enqueue(5) 59 | print ("Pass" if (q.size() == 1) else "Fail") -------------------------------------------------------------------------------- /src/data_structures/queue/queues_using_arrays.py: -------------------------------------------------------------------------------- 1 | class Queue: 2 | 3 | def __init__(self, initial_size=10): 4 | self.arr = [0 for _ in range(initial_size)] 5 | self.next_index = 0 6 | self.front_index = -1 7 | self.queue_size = 0 8 | 9 | def enqueue(self, value): 10 | # if queue is already full --> increase capacity 11 | if self.queue_size == len(self.arr): 12 | self._handle_queue_capacity_full() 13 | 14 | # enqueue new element 15 | self.arr[self.next_index] = value 16 | self.queue_size += 1 17 | self.next_index = (self.next_index + 1) % len(self.arr) 18 | if self.front_index == -1: 19 | self.front_index = 0 20 | 21 | def dequeue(self): 22 | # check if queue is empty 23 | if self.is_empty(): 24 | self.front_index = -1 # resetting pointers 25 | self.next_index = 0 26 | return None 27 | 28 | # dequeue front element 29 | value = self.arr[self.front_index] 30 | self.front_index = (self.front_index + 1) % len(self.arr) 31 | self.queue_size -= 1 32 | return value 33 | 34 | def size(self): 35 | return self.queue_size 36 | 37 | def is_empty(self): 38 | return self.size() == 0 39 | 40 | def front(self): 41 | # check if queue is empty 42 | if self.is_empty(): 43 | return None 44 | return self.arr[self.front_index] 45 | 46 | def _handle_queue_capacity_full(self): 47 | old_arr = self.arr 48 | self.arr = [0 for _ in range(2 * len(old_arr))] 49 | 50 | index = 0 51 | 52 | # copy all elements from front of queue (front-index) until end 53 | for i in range(self.front_index, len(old_arr)): 54 | self.arr[index] = old_arr[i] 55 | index += 1 56 | 57 | # case: when front-index is ahead of next index 58 | for i in range(0, self.front_index): 59 | self.arr[index] = old_arr[i] 60 | index += 1 61 | 62 | # reset pointers 63 | self.front_index = 0 64 | self.next_index = index -------------------------------------------------------------------------------- /src/data_structures/queue/queues_using_linked_list.py: -------------------------------------------------------------------------------- 1 | class Queue: 2 | 3 | def __init__(self): 4 | self.head = None 5 | self.tail = None 6 | self.num_elements = 0 7 | 8 | def enqueue(self, value): 9 | new_node = Node(value) 10 | if self.head is None: 11 | self.head = new_node 12 | self.tail = self.head 13 | else: 14 | self.tail.next = new_node # add data to the next attribute of the tail (i.e. the end of the queue) 15 | self.tail = self.tail.next # shift the tail (i.e., the back of the queue) 16 | self.num_elements += 1 17 | 18 | def dequeue(self): 19 | if self.is_empty(): 20 | return None 21 | value = self.head.value # copy the value to a local variable 22 | self.head = self.head.next # shift the head (i.e., the front of the queue) 23 | self.num_elements -= 1 24 | return value 25 | 26 | def size(self): 27 | return self.num_elements 28 | 29 | def is_empty(self): 30 | return self.num_elements == 0 31 | -------------------------------------------------------------------------------- /src/data_structures/queue/reverse_queue.py: -------------------------------------------------------------------------------- 1 | def reverse_queue(queue): 2 | stack = Stack() 3 | while not queue.is_empty(): 4 | stack.push(queue.dequeue()) 5 | 6 | while not stack.is_empty(): 7 | queue.enqueue(stack.pop()) 8 | -------------------------------------------------------------------------------- /src/data_structures/stack/README.md: -------------------------------------------------------------------------------- 1 | # Stack 2 | 3 | In computer science, a **stack** is an abstract data type that serves 4 | as a collection of elements, with two principal operations: 5 | 6 | * **push**, which adds an element to the collection, and 7 | * **pop**, which removes the most recently added element that was not yet removed. 8 | 9 | The order in which elements come off a stack gives rise to its 10 | alternative name, LIFO (last in, first out). Additionally, a 11 | peek operation may give access to the top without modifying 12 | the stack. The name "stack" for this type of structure comes 13 | from the analogy to a set of physical items stacked on top of 14 | each other, which makes it easy to take an item off the top 15 | of the stack, while getting to an item deeper in the stack 16 | may require taking off multiple other items first. 17 | 18 | Simple representation of a stack runtime with push and pop operations. 19 | 20 | ![Stack](https://upload.wikimedia.org/wikipedia/commons/b/b4/Lifo_stack.png) 21 | 22 | ## References 23 | 24 | - [Wikipedia](https://en.wikipedia.org/wiki/Stack_(abstract_data_type)) 25 | - [GeeksforGeeks](https://www.geeksforgeeks.org/stack-data-structure/) 26 | - [YouTube](https://www.youtube.com/watch?v=wjI1WNcIntg&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=3&) 27 | -------------------------------------------------------------------------------- /src/data_structures/stack/reverse_stack.py: -------------------------------------------------------------------------------- 1 | class Stack: 2 | def __init__(self): 3 | self.items = [] 4 | 5 | def push(self, item): 6 | self.items.append(item) 7 | 8 | def size(self): 9 | return len(self.items) 10 | 11 | def pop(self): 12 | if self.size()==0: 13 | return None 14 | else: 15 | return self.items.pop() 16 | 17 | def reverse_stack(stack): 18 | holder_stack = Stack() 19 | while not stack.is_empty(): 20 | popped_element = stack.pop() 21 | holder_stack.push(popped_element) 22 | _reverse_stack_recursion(stack, holder_stack) 23 | 24 | 25 | def _reverse_stack_recursion(stack, holder_stack): 26 | if holder_stack.is_empty(): 27 | return 28 | popped_element = holder_stack.pop() 29 | _reverse_stack_recursion(stack, holder_stack) 30 | stack.push(popped_element) -------------------------------------------------------------------------------- /src/data_structures/stack/stack.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Stack\n", 8 | "The functions associated with stack are:\n", 9 | "\n", 10 | "* empty() – Returns whether the stack is empty – Time Complexity : $\\mathcal{O}(1)$\n", 11 | "* size() – Returns the size of the stack – Time Complexity : $\\mathcal{O}(1)$\n", 12 | "* top() – Returns a reference to the top most element of the stack – Time Complexity : $\\mathcal{O}(1)$\n", 13 | "* push(g) – Adds the element ‘g’ at the top of the stack – Time Complexity : $\\mathcal{O}(1)$\n", 14 | "* pop() – Deletes the top most element of the stack – Time Complexity : $\\mathcal{O}(1)$\n" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "## Implementation using list\n", 22 | "Python’s buil-in data structure list can be used as a stack. Instead of `push()`, `append()` is used to add elements to the top of stack while `pop()` removes the element in LIFO order.\n", 23 | "\n", 24 | "Unfortunately, list has a few shortcomings. The biggest issue is that it can run into speed issue as it grows. The items in list are stored next to each other in memory, if the stack grows bigger than the block of memory that currently hold it, then Python needs to do some memory allocations. This can lead to some `append()` calls taking much longer than other ones." 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": 1, 30 | "metadata": {}, 31 | "outputs": [], 32 | "source": [ 33 | "stack = []" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": 2, 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "# append() function to push \n", 43 | "# element in the stack \n", 44 | "stack.append('a') \n", 45 | "stack.append('b') \n", 46 | "stack.append('c') " 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": 3, 52 | "metadata": {}, 53 | "outputs": [ 54 | { 55 | "data": { 56 | "text/plain": [ 57 | "['a', 'b', 'c']" 58 | ] 59 | }, 60 | "execution_count": 3, 61 | "metadata": {}, 62 | "output_type": "execute_result" 63 | } 64 | ], 65 | "source": [ 66 | "stack" 67 | ] 68 | }, 69 | { 70 | "cell_type": "markdown", 71 | "metadata": {}, 72 | "source": [ 73 | "## Implementation using collections.deque\n", 74 | "Python stack can be implemented using deque class from collections module. Deque is preferred over list in the cases where we need quicker append and pop operations from both the ends of the container, as deque provides an $\\mathcal{O}(1)$ time complexity for append and pop operations as compared to list which provides $\\mathcal{O}(n)$ time complexity.\n", 75 | "Same methods on deque as seen in list are used, `append()` and `pop()`." 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": 4, 81 | "metadata": {}, 82 | "outputs": [], 83 | "source": [ 84 | "# Python program to \n", 85 | "# demonstrate stack implementation \n", 86 | "# using collections.deque \n", 87 | "from collections import deque" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 5, 93 | "metadata": {}, 94 | "outputs": [], 95 | "source": [ 96 | "stack = deque()" 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": 6, 102 | "metadata": {}, 103 | "outputs": [], 104 | "source": [ 105 | "# append() function to push \n", 106 | "# element in the stack \n", 107 | "stack.append('a')\n", 108 | "stack.append('b')\n", 109 | "stack.append('c')" 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": 7, 115 | "metadata": {}, 116 | "outputs": [ 117 | { 118 | "data": { 119 | "text/plain": [ 120 | "deque(['a', 'b', 'c'])" 121 | ] 122 | }, 123 | "execution_count": 7, 124 | "metadata": {}, 125 | "output_type": "execute_result" 126 | } 127 | ], 128 | "source": [ 129 | "stack" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": 8, 135 | "metadata": {}, 136 | "outputs": [ 137 | { 138 | "data": { 139 | "text/plain": [ 140 | "'c'" 141 | ] 142 | }, 143 | "execution_count": 8, 144 | "metadata": {}, 145 | "output_type": "execute_result" 146 | } 147 | ], 148 | "source": [ 149 | "# pop() fucntion to pop \n", 150 | "# element from stack in \n", 151 | "# LIFO order \n", 152 | "stack.pop()" 153 | ] 154 | }, 155 | { 156 | "cell_type": "markdown", 157 | "metadata": {}, 158 | "source": [ 159 | "# Implemenation using queue module\n", 160 | "Queue module also has a LIFO Queue, which is basically a Stack. Data is inserted into Queue using `put()` function and `get()` takes data out from the Queue.\n", 161 | "\n", 162 | "There are various functions available in this module:\n", 163 | "* maxsize – Number of items allowed in the queue.\n", 164 | "* empty() – Return True if the queue is empty, False otherwise.\n", 165 | "* full() – Return True if there are maxsize items in the queue. If the queue was initialized with maxsize=0 (the default), then full() never returns True.\n", 166 | "* get() – Remove and return an item from the queue. If queue is empty, wait until an item is available.\n", 167 | "* get_nowait() – Return an item if one is immediately available, else raise QueueEmpty.\n", 168 | "* put(item) – Put an item into the queue. If the queue is full, wait until a free slot is available before adding the item.\n", 169 | "* put_nowait(item) – Put an item into the queue without blocking.\n", 170 | "* qsize() – Return the number of items in the queue. If no free slot is immediately available, raise QueueFull.\n" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": 9, 176 | "metadata": {}, 177 | "outputs": [], 178 | "source": [ 179 | "# Python program to \n", 180 | "# demonstrate stack implementation \n", 181 | "# using queue module \n", 182 | "from queue import LifoQueue \n", 183 | " \n", 184 | "# Initializing a stack \n", 185 | "stack = LifoQueue(maxsize = 3) " 186 | ] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "execution_count": 10, 191 | "metadata": {}, 192 | "outputs": [], 193 | "source": [ 194 | "# put() function to push \n", 195 | "# element in the stack \n", 196 | "stack.put('a') \n", 197 | "stack.put('b') \n", 198 | "stack.put('c') " 199 | ] 200 | }, 201 | { 202 | "cell_type": "code", 203 | "execution_count": 11, 204 | "metadata": {}, 205 | "outputs": [ 206 | { 207 | "name": "stdout", 208 | "output_type": "stream", 209 | "text": [ 210 | "Full: True\n", 211 | "Size: 3\n" 212 | ] 213 | } 214 | ], 215 | "source": [ 216 | "print(\"Full: \", stack.full()) \n", 217 | "print(\"Size: \", stack.qsize()) " 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "execution_count": 12, 223 | "metadata": {}, 224 | "outputs": [ 225 | { 226 | "data": { 227 | "text/plain": [ 228 | "'c'" 229 | ] 230 | }, 231 | "execution_count": 12, 232 | "metadata": {}, 233 | "output_type": "execute_result" 234 | } 235 | ], 236 | "source": [ 237 | "# get() fucntion to pop \n", 238 | "# element from stack in \n", 239 | "# LIFO order \n", 240 | "stack.get()" 241 | ] 242 | } 243 | ], 244 | "metadata": { 245 | "kernelspec": { 246 | "display_name": "Python 3", 247 | "language": "python", 248 | "name": "python3" 249 | }, 250 | "language_info": { 251 | "codemirror_mode": { 252 | "name": "ipython", 253 | "version": 3 254 | }, 255 | "file_extension": ".py", 256 | "mimetype": "text/x-python", 257 | "name": "python", 258 | "nbconvert_exporter": "python", 259 | "pygments_lexer": "ipython3", 260 | "version": "3.8.2" 261 | } 262 | }, 263 | "nbformat": 4, 264 | "nbformat_minor": 4 265 | } 266 | -------------------------------------------------------------------------------- /src/data_structures/stack/stack.py: -------------------------------------------------------------------------------- 1 | # Implement a stack 2 | """ 3 | push - adds an item to the top of the stack 4 | pop - removes an item from the top of the stack (and returns the value of that item) 5 | size - returns the size of the stack 6 | 7 | """ 8 | 9 | class Stack: 10 | def __init__(self): 11 | self.items = [] 12 | 13 | def push(self, item): 14 | self.items.append(item) 15 | 16 | def size(self): 17 | return len(self.items) 18 | 19 | def peek(self): 20 | # return the top element 21 | if self.size() == 0: 22 | return self.items[0] 23 | 24 | 25 | def pop(self): 26 | if self.size()==0: 27 | return None 28 | else: 29 | return self.items.pop() 30 | 31 | MyStack = Stack() 32 | 33 | MyStack.push("Web Page 1") 34 | MyStack.push("Web Page 2") 35 | MyStack.push("Web Page 3") 36 | 37 | print (MyStack.items) 38 | 39 | MyStack.pop() 40 | MyStack.pop() 41 | 42 | print ("Pass" if (MyStack.items[0] == 'Web Page 1') else "Fail") 43 | 44 | MyStack.pop() 45 | 46 | print ("Pass" if (MyStack.pop() == None) else "Fail") -------------------------------------------------------------------------------- /src/data_structures/stack/stack_using_array.py: -------------------------------------------------------------------------------- 1 | class Stack: 2 | 3 | def __init__(self, initial_size = 10): 4 | self.arr = [0 for _ in range(initial_size)] 5 | self.next_index = 0 6 | self.num_elements = 0 7 | 8 | def push(self, data): 9 | if self.next_index == len(self.arr): 10 | print("Out of space! Increasing array capacity ...") 11 | self._handle_stack_capacity_full() 12 | 13 | self.arr[self.next_index] = data 14 | self.next_index += 1 15 | self.num_elements += 1 16 | 17 | def pop(self): 18 | if self.is_empty(): 19 | self.next_index = 0 20 | return None 21 | self.next_index -= 1 22 | self.num_elements -= 1 23 | return self.arr[self.next_index] 24 | 25 | def size(self): 26 | return self.num_elements 27 | 28 | def is_empty(self): 29 | return self.num_elements == 0 30 | 31 | def _handle_stack_capacity_full(self): 32 | old_arr = self.arr 33 | 34 | self.arr = [0 for _ in range( 2* len(old_arr))] 35 | for index, element in enumerate(old_arr): 36 | self.arr[index] = element 37 | 38 | foo = Stack() 39 | print(foo.arr) 40 | print("Pass" if foo.arr == [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] else "Fail") -------------------------------------------------------------------------------- /src/data_structures/stack/stack_using_linked_list.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | 3 | # Constructor to create a new node 4 | def __init__(self, data): 5 | self.data = data # Pointer to data 6 | self.next = None # Initialize next as null 7 | class Stack: 8 | 9 | def __init__(self): 10 | self.head = None 11 | self.num_elements = 0 12 | 13 | def push(self, value): 14 | new_node = Node(value) 15 | # if stack is empty 16 | if self.head is None: 17 | self.head = new_node 18 | else: 19 | new_node.next = self.head # place the new node at the head (top) of the linked list 20 | self.head = new_node 21 | 22 | self.num_elements += 1 23 | 24 | def pop(self): 25 | if self.is_empty(): 26 | return 27 | 28 | value = self.head.value # copy data to a local variable 29 | self.head = self.head.next # move head pointer to point to next node (top is removed by doing so) 30 | self.num_elements -= 1 31 | return value 32 | 33 | def size(self): 34 | return self.num_elements 35 | 36 | def is_empty(self): 37 | return self.num_elements == 0 -------------------------------------------------------------------------------- /src/data_structures/tree/README.md: -------------------------------------------------------------------------------- 1 | # Tree 2 | 3 | * [Binary Search Tree](binary-search-tree) 4 | * [AVL Tree](avl-tree) 5 | * [Red-Black Tree](red-black-tree) 6 | * [Segment Tree](segment-tree) - with min/max/sum range queries examples 7 | * [Fenwick Tree](fenwick-tree) (Binary Indexed Tree) 8 | 9 | In computer science, a **tree** is a widely used abstract data 10 | type (ADT) — or data structure implementing this ADT—that 11 | simulates a hierarchical tree structure, with a root value 12 | and subtrees of children with a parent node, represented as 13 | a set of linked nodes. 14 | 15 | A tree data structure can be defined recursively (locally) 16 | as a collection of nodes (starting at a root node), where 17 | each node is a data structure consisting of a value, 18 | together with a list of references to nodes (the "children"), 19 | with the constraints that no reference is duplicated, and none 20 | points to the root. 21 | 22 | A simple unordered tree; in this diagram, the node labeled 7 has 23 | two children, labeled 2 and 6, and one parent, labeled 2. The 24 | root node, at the top, has no parent. 25 | 26 | ![Tree](https://upload.wikimedia.org/wikipedia/commons/f/f7/Binary_tree.svg) 27 | 28 | ## References 29 | 30 | - [Wikipedia](https://en.wikipedia.org/wiki/Tree_(data_structure)) 31 | - [GeeksforGeeks](https://www.geeksforgeeks.org/binary-tree-data-structure/) 32 | - [YouTube](https://www.youtube.com/watch?v=oSWTXtMglKE&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=8) 33 | -------------------------------------------------------------------------------- /src/data_structures/tree/avl-tree/README.md: -------------------------------------------------------------------------------- 1 | # AVL Tree 2 | 3 | In computer science, an **AVL tree** (named after inventors 4 | Adelson-Velsky and Landis) is a self-balancing binary search 5 | tree. It was the first such data structure to be invented. 6 | In an AVL tree, the heights of the two child subtrees of any 7 | node differ by at most one; if at any time they differ by 8 | more than one, rebalancing is done to restore this property. 9 | Lookup, insertion, and deletion all take `O(log n)` time in 10 | both the average and worst cases, where n is the number of 11 | nodes in the tree prior to the operation. Insertions and 12 | deletions may require the tree to be rebalanced by one or 13 | more tree rotations. 14 | 15 | Animation showing the insertion of several elements into an AVL 16 | tree. It includes left, right, left-right and right-left rotations. 17 | 18 | ![AVL Tree](https://upload.wikimedia.org/wikipedia/commons/f/fd/AVL_Tree_Example.gif) 19 | 20 | AVL tree with balance factors (green) 21 | 22 | ![AVL Tree](https://upload.wikimedia.org/wikipedia/commons/a/ad/AVL-tree-wBalance_K.svg) 23 | 24 | ### AVL Tree Rotations 25 | 26 | **Left-Left Rotation** 27 | 28 | ![Left-Left Rotation](http://btechsmartclass.com/data_structures/ds_images/LL%20Rotation.png) 29 | 30 | **Right-Right Rotation** 31 | 32 | ![Right-Right Rotation](http://btechsmartclass.com/data_structures/ds_images/RR%20Rotation.png) 33 | 34 | **Left-Right Rotation** 35 | 36 | ![Left-Right Rotation](http://btechsmartclass.com/data_structures/ds_images/LR%20Rotation.png) 37 | 38 | **Right-Left Rotation** 39 | 40 | ![Right-Right Rotation](http://btechsmartclass.com/data_structures/ds_images/RL%20Rotation.png) 41 | 42 | ## References 43 | 44 | - [Wikipedia](https://en.wikipedia.org/wiki/AVL_tree) 45 | - [GeeksforGeeks](https://www.geeksforgeeks.org/avl-tree-set-1-insertion/) 46 | - [Tutorials Point](https://www.tutorialspoint.com/data_structures_algorithms/avl_tree_algorithm.htm) 47 | - [BTech](http://btechsmartclass.com/data_structures/avl-trees.html) 48 | - [AVL Tree Insertion on YouTube](https://www.youtube.com/watch?v=rbg7Qf8GkQ4&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=12&) 49 | - [AVL Tree Interactive Visualisations](https://www.cs.usfca.edu/~galles/visualization/AVLtree.html) 50 | -------------------------------------------------------------------------------- /src/data_structures/tree/binary-search-tree/README.md: -------------------------------------------------------------------------------- 1 | # Binary Search Tree 2 | 3 | In computer science, **binary search trees** (BST), sometimes called 4 | ordered or sorted binary trees, are a particular type of container: 5 | data structures that store "items" (such as numbers, names etc.) 6 | in memory. They allow fast lookup, addition and removal of 7 | items, and can be used to implement either dynamic sets of 8 | items, or lookup tables that allow finding an item by its key 9 | (e.g., finding the phone number of a person by name). 10 | 11 | Binary search trees keep their keys in sorted order, so that lookup 12 | and other operations can use the principle of binary search: 13 | when looking for a key in a tree (or a place to insert a new key), 14 | they traverse the tree from root to leaf, making comparisons to 15 | keys stored in the nodes of the tree and deciding, on the basis 16 | of the comparison, to continue searching in the left or right 17 | subtrees. On average, this means that each comparison allows 18 | the operations to skip about half of the tree, so that each 19 | lookup, insertion or deletion takes time proportional to the 20 | logarithm of the number of items stored in the tree. This is 21 | much better than the linear time required to find items by key 22 | in an (unsorted) array, but slower than the corresponding 23 | operations on hash tables. 24 | 25 | A binary search tree of size 9 and depth 3, with 8 at the root. 26 | The leaves are not drawn. 27 | 28 | ![Binary Search Tree](https://upload.wikimedia.org/wikipedia/commons/d/da/Binary_search_tree.svg) 29 | 30 | ## Pseudocode for Basic Operations 31 | 32 | ### Insertion 33 | 34 | ```text 35 | insert(value) 36 | Pre: value has passed custom type checks for type T 37 | Post: value has been placed in the correct location in the tree 38 | if root = ø 39 | root ← node(value) 40 | else 41 | insertNode(root, value) 42 | end if 43 | end insert 44 | ``` 45 | 46 | ```text 47 | insertNode(current, value) 48 | Pre: current is the node to start from 49 | Post: value has been placed in the correct location in the tree 50 | if value < current.value 51 | if current.left = ø 52 | current.left ← node(value) 53 | else 54 | InsertNode(current.left, value) 55 | end if 56 | else 57 | if current.right = ø 58 | current.right ← node(value) 59 | else 60 | InsertNode(current.right, value) 61 | end if 62 | end if 63 | end insertNode 64 | ``` 65 | 66 | ### Searching 67 | 68 | ```text 69 | contains(root, value) 70 | Pre: root is the root node of the tree, value is what we would like to locate 71 | Post: value is either located or not 72 | if root = ø 73 | return false 74 | end if 75 | if root.value = value 76 | return true 77 | else if value < root.value 78 | return contains(root.left, value) 79 | else 80 | return contains(root.right, value) 81 | end if 82 | end contains 83 | ``` 84 | 85 | 86 | ### Deletion 87 | 88 | ```text 89 | remove(value) 90 | Pre: value is the value of the node to remove, root is the node of the BST 91 | count is the number of items in the BST 92 | Post: node with value is removed if found in which case yields true, otherwise false 93 | nodeToRemove ← findNode(value) 94 | if nodeToRemove = ø 95 | return false 96 | end if 97 | parent ← findParent(value) 98 | if count = 1 99 | root ← ø 100 | else if nodeToRemove.left = ø and nodeToRemove.right = ø 101 | if nodeToRemove.value < parent.value 102 | parent.left ← nodeToRemove.right 103 | else 104 | parent.right ← nodeToRemove.right 105 | end if 106 | else if nodeToRemove.left != ø and nodeToRemove.right != ø 107 | next ← nodeToRemove.right 108 | while next.left != ø 109 | next ← next.left 110 | end while 111 | if next != nodeToRemove.right 112 | remove(next.value) 113 | nodeToRemove.value ← next.value 114 | else 115 | nodeToRemove.value ← next.value 116 | nodeToRemove.right ← nodeToRemove.right.right 117 | end if 118 | else 119 | if nodeToRemove.left = ø 120 | next ← nodeToRemove.right 121 | else 122 | next ← nodeToRemove.left 123 | end if 124 | if root = nodeToRemove 125 | root = next 126 | else if parent.left = nodeToRemove 127 | parent.left = next 128 | else if parent.right = nodeToRemove 129 | parent.right = next 130 | end if 131 | end if 132 | count ← count - 1 133 | return true 134 | end remove 135 | ``` 136 | 137 | ### Find Parent of Node 138 | 139 | ```text 140 | findParent(value, root) 141 | Pre: value is the value of the node we want to find the parent of 142 | root is the root node of the BST and is != ø 143 | Post: a reference to the prent node of value if found; otherwise ø 144 | if value = root.value 145 | return ø 146 | end if 147 | if value < root.value 148 | if root.left = ø 149 | return ø 150 | else if root.left.value = value 151 | return root 152 | else 153 | return findParent(value, root.left) 154 | end if 155 | else 156 | if root.right = ø 157 | return ø 158 | else if root.right.value = value 159 | return root 160 | else 161 | return findParent(value, root.right) 162 | end if 163 | end if 164 | end findParent 165 | ``` 166 | 167 | ### Find Node 168 | 169 | ```text 170 | findNode(root, value) 171 | Pre: value is the value of the node we want to find the parent of 172 | root is the root node of the BST 173 | Post: a reference to the node of value if found; otherwise ø 174 | if root = ø 175 | return ø 176 | end if 177 | if root.value = value 178 | return root 179 | else if value < root.value 180 | return findNode(root.left, value) 181 | else 182 | return findNode(root.right, value) 183 | end if 184 | end findNode 185 | ``` 186 | 187 | ### Find Minimum 188 | 189 | ```text 190 | findMin(root) 191 | Pre: root is the root node of the BST 192 | root = ø 193 | Post: the smallest value in the BST is located 194 | if root.left = ø 195 | return root.value 196 | end if 197 | findMin(root.left) 198 | end findMin 199 | ``` 200 | 201 | ### Find Maximum 202 | 203 | ```text 204 | findMax(root) 205 | Pre: root is the root node of the BST 206 | root = ø 207 | Post: the largest value in the BST is located 208 | if root.right = ø 209 | return root.value 210 | end if 211 | findMax(root.right) 212 | end findMax 213 | ``` 214 | 215 | ### Traversal 216 | 217 | #### InOrder Traversal 218 | 219 | ```text 220 | inorder(root) 221 | Pre: root is the root node of the BST 222 | Post: the nodes in the BST have been visited in inorder 223 | if root = ø 224 | inorder(root.left) 225 | yield root.value 226 | inorder(root.right) 227 | end if 228 | end inorder 229 | ``` 230 | 231 | #### PreOrder Traversal 232 | 233 | ```text 234 | preorder(root) 235 | Pre: root is the root node of the BST 236 | Post: the nodes in the BST have been visited in preorder 237 | if root = ø 238 | yield root.value 239 | preorder(root.left) 240 | preorder(root.right) 241 | end if 242 | end preorder 243 | ``` 244 | 245 | #### PostOrder Traversal 246 | 247 | ```text 248 | postorder(root) 249 | Pre: root is the root node of the BST 250 | Post: the nodes in the BST have been visited in postorder 251 | if root = ø 252 | postorder(root.left) 253 | postorder(root.right) 254 | yield root.value 255 | end if 256 | end postorder 257 | ``` 258 | 259 | ## Complexities 260 | 261 | ### Time Complexity 262 | 263 | | Access | Search | Insertion | Deletion | 264 | | :-------: | :-------: | :-------: | :-------: | 265 | | O(log(n)) | O(log(n)) | O(log(n)) | O(log(n)) | 266 | 267 | ### Space Complexity 268 | 269 | O(n) 270 | 271 | ## References 272 | 273 | - [Wikipedia](https://en.wikipedia.org/wiki/Binary_search_tree) 274 | - [GeeksforGeeks](https://www.geeksforgeeks.org/binary-search-tree-data-structure/) 275 | - [Inserting to BST on YouTube](https://www.youtube.com/watch?v=wcIRPqTR3Kc&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=9&t=0s) 276 | - [BST Interactive Visualisations](https://www.cs.usfca.edu/~galles/visualization/BST.html) 277 | -------------------------------------------------------------------------------- /src/data_structures/tree/fenwick-tree/README.md: -------------------------------------------------------------------------------- 1 | # Fenwick Tree / Binary Indexed Tree 2 | 3 | A **Fenwick tree** or **binary indexed tree** is a data 4 | structure that can efficiently update elements and 5 | calculate prefix sums in a table of numbers. 6 | 7 | When compared with a flat array of numbers, the Fenwick tree achieves a 8 | much better balance between two operations: element update and prefix sum 9 | calculation. In a flat array of `n` numbers, you can either store the elements, 10 | or the prefix sums. In the first case, computing prefix sums requires linear 11 | time; in the second case, updating the array elements requires linear time 12 | (in both cases, the other operation can be performed in constant time). 13 | Fenwick trees allow both operations to be performed in `O(log n)` time. 14 | This is achieved by representing the numbers as a tree, where the value of 15 | each node is the sum of the numbers in that subtree. The tree structure allows 16 | operations to be performed using only `O(log n)` node accesses. 17 | 18 | ## Implementation Notes 19 | 20 | Binary Indexed Tree is represented as an array. Each node of Binary Indexed Tree 21 | stores sum of some elements of given array. Size of Binary Indexed Tree is equal 22 | to `n` where `n` is size of input array. In current implementation we have used 23 | size as `n+1` for ease of implementation. All the indexes are 1-based. 24 | 25 | ![Binary Indexed Tree](https://www.geeksforgeeks.org/wp-content/uploads/BITSum.png) 26 | 27 | On the picture below you may see animated example of 28 | creation of binary indexed tree for the 29 | array `[1, 2, 3, 4, 5]` by inserting one by one. 30 | 31 | ![Fenwick Tree](https://upload.wikimedia.org/wikipedia/commons/d/dc/BITDemo.gif) 32 | 33 | ## References 34 | 35 | - [Wikipedia](https://en.wikipedia.org/wiki/Fenwick_tree) 36 | - [GeeksForGeeks](https://www.geeksforgeeks.org/binary-indexed-tree-or-fenwick-tree-2/) 37 | - [YouTube](https://www.youtube.com/watch?v=CWDQJGaN1gY&index=18&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 38 | -------------------------------------------------------------------------------- /src/data_structures/tree/red-black-tree/README.md: -------------------------------------------------------------------------------- 1 | # Red–Black Tree 2 | 3 | A **red–black tree** is a kind of self-balancing binary search 4 | tree in computer science. Each node of the binary tree has 5 | an extra bit, and that bit is often interpreted as the 6 | color (red or black) of the node. These color bits are used 7 | to ensure the tree remains approximately balanced during 8 | insertions and deletions. 9 | 10 | Balance is preserved by painting each node of the tree with 11 | one of two colors in a way that satisfies certain properties, 12 | which collectively constrain how unbalanced the tree can 13 | become in the worst case. When the tree is modified, the 14 | new tree is subsequently rearranged and repainted to 15 | restore the coloring properties. The properties are 16 | designed in such a way that this rearranging and recoloring 17 | can be performed efficiently. 18 | 19 | The balancing of the tree is not perfect, but it is good 20 | enough to allow it to guarantee searching in `O(log n)` time, 21 | where `n` is the total number of elements in the tree. 22 | The insertion and deletion operations, along with the tree 23 | rearrangement and recoloring, are also performed 24 | in `O(log n)` time. 25 | 26 | An example of a red–black tree: 27 | 28 | ![red-black tree](https://upload.wikimedia.org/wikipedia/commons/6/66/Red-black_tree_example.svg) 29 | 30 | ## Properties 31 | 32 | In addition to the requirements imposed on a binary search 33 | tree the following must be satisfied by a red–black tree: 34 | 35 | - Each node is either red or black. 36 | - The root is black. This rule is sometimes omitted. 37 | Since the root can always be changed from red to black, 38 | but not necessarily vice versa, this rule has little 39 | effect on analysis. 40 | - All leaves (NIL) are black. 41 | - If a node is red, then both its children are black. 42 | - Every path from a given node to any of its descendant 43 | NIL nodes contains the same number of black nodes. 44 | 45 | Some definitions: the number of black nodes from the root 46 | to a node is the node's **black depth**; the uniform 47 | number of black nodes in all paths from root to the leaves 48 | is called the **black-height** of the red–black tree. 49 | 50 | These constraints enforce a critical property of red–black 51 | trees: _the path from the root to the farthest leaf is no more than twice as long as the path from the root to the nearest leaf_. 52 | The result is that the tree is roughly height-balanced. 53 | Since operations such as inserting, deleting, and finding 54 | values require worst-case time proportional to the height 55 | of the tree, this theoretical upper bound on the height 56 | allows red–black trees to be efficient in the worst case, 57 | unlike ordinary binary search trees. 58 | 59 | ## Balancing during insertion 60 | 61 | ### If uncle is RED 62 | ![Red Black Tree Balancing](https://www.geeksforgeeks.org/wp-content/uploads/redBlackCase2.png) 63 | 64 | ### If uncle is BLACK 65 | 66 | - Left Left Case (`p` is left child of `g` and `x` is left child of `p`) 67 | - Left Right Case (`p` is left child of `g` and `x` is right child of `p`) 68 | - Right Right Case (`p` is right child of `g` and `x` is right child of `p`) 69 | - Right Left Case (`p` is right child of `g` and `x` is left child of `p`) 70 | 71 | #### Left Left Case (See g, p and x) 72 | 73 | ![Red Black Tree Balancing](https://www.geeksforgeeks.org/wp-content/uploads/redBlackCase3a1.png) 74 | 75 | #### Left Right Case (See g, p and x) 76 | 77 | ![Red Black Tree Balancing](https://www.geeksforgeeks.org/wp-content/uploads/redBlackCase3b.png) 78 | 79 | #### Right Right Case (See g, p and x) 80 | 81 | ![Red Black Tree Balancing](https://www.geeksforgeeks.org/wp-content/uploads/redBlackCase3c.png) 82 | 83 | #### Right Left Case (See g, p and x) 84 | 85 | ![Red Black Tree Balancing](https://www.geeksforgeeks.org/wp-content/uploads/redBlackCase3d.png) 86 | 87 | ## References 88 | 89 | - [Wikipedia](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree) 90 | - [GeeksforGeeks](https://www.geeksforgeeks.org/red-black-tree-set-1-introduction-2/) 91 | - [Red Black Tree Insertion by Tushar Roy (YouTube)](https://www.youtube.com/watch?v=UaLIHuR1t8Q&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=63) 92 | - [Red Black Tree Deletion by Tushar Roy (YouTube)](https://www.youtube.com/watch?v=CTvfzU_uNKE&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=64) 93 | - [Red Black Tree Insertion on GeeksForGeeks](https://www.geeksforgeeks.org/red-black-tree-set-2-insert/) 94 | - [Red Black Tree Interactive Visualisations](https://www.cs.usfca.edu/~galles/visualization/RedBlack.html) 95 | -------------------------------------------------------------------------------- /src/data_structures/tree/segment-tree/README.md: -------------------------------------------------------------------------------- 1 | # Segment Tree 2 | 3 | In computer science, a **segment tree** also known as a statistic tree 4 | is a tree data structure used for storing information about intervals, 5 | or segments. It allows querying which of the stored segments contain 6 | a given point. It is, in principle, a static structure; that is, 7 | it's a structure that cannot be modified once it's built. A similar 8 | data structure is the interval tree. 9 | 10 | A segment tree is a binary tree. The root of the tree represents the 11 | whole array. The two children of the root represent the 12 | first and second halves of the array. Similarly, the 13 | children of each node corresponds to the two halves of 14 | the array corresponding to the node. 15 | 16 | We build the tree bottom up, with the value of each node 17 | being the "minimum" (or any other function) of its children's values. This will 18 | take `O(n log n)` time. The number 19 | of operations done is the height of the tree, which 20 | is `O(log n)`. To do range queries, each node splits the 21 | query into two parts, one sub-query for each child. 22 | If a query contains the whole subarray of a node, we 23 | can use the precomputed value at the node. Using this 24 | optimisation, we can prove that only `O(log n)` minimum 25 | operations are done. 26 | 27 | ![Min Segment Tree](https://www.geeksforgeeks.org/wp-content/uploads/RangeMinimumQuery.png) 28 | 29 | ![Sum Segment Tree](https://www.geeksforgeeks.org/wp-content/uploads/segment-tree1.png) 30 | 31 | ## Application 32 | 33 | A segment tree is a data structure designed to perform 34 | certain array operations efficiently - especially those 35 | involving range queries. 36 | 37 | Applications of the segment tree are in the areas of computational geometry, 38 | and geographic information systems. 39 | 40 | Current implementation of Segment Tree implies that you may 41 | pass any binary (with two input params) function to it and 42 | thus you're able to do range query for variety of functions. 43 | In tests you may find examples of doing `min`, `max` and `sam` range 44 | queries on SegmentTree. 45 | 46 | ## References 47 | 48 | - [Wikipedia](https://en.wikipedia.org/wiki/Segment_tree) 49 | - [GeeksforGeeks](https://www.geeksforgeeks.org/segment-tree-set-1-sum-of-given-range/) 50 | - [YouTube](https://www.youtube.com/watch?v=ZBHKZF5w4YU&index=65&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 51 | 52 | -------------------------------------------------------------------------------- /src/data_structures/trie/README.md: -------------------------------------------------------------------------------- 1 | # Trie 2 | 3 | In computer science, a **trie**, also called digital tree and sometimes 4 | radix tree or prefix tree (as they can be searched by prefixes), 5 | is a kind of search tree—an ordered tree data structure that is 6 | used to store a dynamic set or associative array where the keys 7 | are usually strings. Unlike a binary search tree, no node in the 8 | tree stores the key associated with that node; instead, its 9 | position in the tree defines the key with which it is associated. 10 | All the descendants of a node have a common prefix of the string 11 | associated with that node, and the root is associated with the 12 | empty string. Values are not necessarily associated with every 13 | node. Rather, values tend only to be associated with leaves, 14 | and with some inner nodes that correspond to keys of interest. 15 | For the space-optimized presentation of prefix tree, see compact 16 | prefix tree. 17 | 18 | ![Trie](https://upload.wikimedia.org/wikipedia/commons/b/be/Trie_example.svg) 19 | 20 | ## References 21 | 22 | - [Wikipedia](https://en.wikipedia.org/wiki/Trie) 23 | - [GeeksforGeeks](https://www.geeksforgeeks.org/types-of-tries/) 24 | - [YouTube](https://www.youtube.com/watch?v=zIjfhVPRZCg&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=7&t=0s) 25 | -------------------------------------------------------------------------------- /src/efficiency/ArraySorting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomh/python-data-structures-and-algorithms/3ba7434d28dfd28f4a9aeb49fd3aeb3090e463e2/src/efficiency/ArraySorting.png -------------------------------------------------------------------------------- /src/efficiency/big_o_efficiency.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Big O Notation" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "The goal of this lesson is to develop your ability to look at some code and indentify its time complexity, using Big O notation." 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 52, 27 | "metadata": {}, 28 | "outputs": [ 29 | { 30 | "data": { 31 | "image/png": "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\n", 32 | "text/plain": [ 33 | "
" 34 | ] 35 | }, 36 | "metadata": { 37 | "needs_background": "light" 38 | }, 39 | "output_type": "display_data" 40 | } 41 | ], 42 | "source": [ 43 | "# Comparison computacional complexity\n", 44 | "import matplotlib.pyplot as plt\n", 45 | "from scipy.special import gamma\n", 46 | "import math\n", 47 | "import numpy as np\n", 48 | "n = np.linspace(1,101,100)\n", 49 | "O1 = gamma(n)\n", 50 | "O2 = 2**n\n", 51 | "O3 = n**2\n", 52 | "O4 = n*np.log(n) / np.log(2)\n", 53 | "O5 = n\n", 54 | "O6 = np.sqrt(n)\n", 55 | "O7 = np.log(n) / np.log(2)\n", 56 | "plt.plot(n, O1, '--k', label='n!') \n", 57 | "plt.plot(n, O2, '--r', label='2^n') \n", 58 | "plt.plot(n, O3, '--g', label='n^2') \n", 59 | "plt.plot(n, O4, 'y', label='nlog(n)') \n", 60 | "plt.plot(n, O5, 'c', label='n') \n", 61 | "plt.plot(n, O6, '--m', label='sqrt(n)') \n", 62 | "plt.plot(n, O7, 'b', label='log(n)') \n", 63 | "axes = plt.gca()\n", 64 | "axes.set(xlim=(0, 100), ylim=(0, 100))\n", 65 | "leg = axes.legend()\n", 66 | "plt.show()" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": 1, 72 | "metadata": {}, 73 | "outputs": [ 74 | { 75 | "name": "stdout", 76 | "output_type": "stream", 77 | "text": [ 78 | "CPU times: user 1 µs, sys: 0 ns, total: 1 µs\n", 79 | "Wall time: 4.05 µs\n", 80 | "[1, 2]\n", 81 | "[2, 1]\n" 82 | ] 83 | } 84 | ], 85 | "source": [ 86 | "# O(N!)\n", 87 | "# This is the Heap's algorithm, which is used for generating all possible permutation of n objects\n", 88 | "# Another example could be the Travelling Salesman Problem\n", 89 | "%time\n", 90 | "def Permutation(data, n):\n", 91 | " if n == 1:\n", 92 | " print(data)\n", 93 | " return\n", 94 | " for i in range(n):\n", 95 | " Permutation(data, n - 1)\n", 96 | " if n % 2 == 0:\n", 97 | " data[i], data[n-1] = data[n-1], data[i]\n", 98 | " else:\n", 99 | " data[0], data[n-1] = data[n-1], data[0]\n", 100 | "data = [1, 2]\n", 101 | "Permutation(data,len(data))\n" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": 9, 107 | "metadata": {}, 108 | "outputs": [ 109 | { 110 | "name": "stdout", 111 | "output_type": "stream", 112 | "text": [ 113 | "CPU times: user 8 µs, sys: 0 ns, total: 8 µs\n", 114 | "Wall time: 14.3 µs\n", 115 | "832040\n" 116 | ] 117 | } 118 | ], 119 | "source": [ 120 | "# O(2^n)\n", 121 | "# Recursive calculation of Fibonacci numbers\n", 122 | "%time\n", 123 | "def fibonacci(n):\n", 124 | " if n <= 1:\n", 125 | " return n\n", 126 | " return fibonacci(n-1) + fibonacci(n-2)\n", 127 | "print(fibonacci(30))\n" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": null, 133 | "metadata": {}, 134 | "outputs": [], 135 | "source": [ 136 | "# O(N^2)\n", 137 | "# Print pair of numbers in the data\n", 138 | "%time\n", 139 | "def Print_Pair(some_list):\n", 140 | " for i in some_list:\n", 141 | " for j in some_list:\n", 142 | "\n", 143 | " print(\"Items: {}, {}\".format(i,j))\n", 144 | "Print_Pair([1, 2, 3, 4]) " 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": 78, 150 | "metadata": {}, 151 | "outputs": [ 152 | { 153 | "name": "stdout", 154 | "output_type": "stream", 155 | "text": [ 156 | "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n", 157 | "CPU times: user 3 µs, sys: 0 ns, total: 3 µs\n", 158 | "Wall time: 5.96 µs\n" 159 | ] 160 | } 161 | ], 162 | "source": [ 163 | "# O(nlog(n))\n", 164 | "# Mergesort algorithm\n", 165 | "%time\n", 166 | "def Merge_Sort(data):\n", 167 | " if len(data) <= 1:\n", 168 | " return\n", 169 | " \n", 170 | " mid = len(data) // 2\n", 171 | " left_data = data[:mid]\n", 172 | " right_data = data[mid:]\n", 173 | " \n", 174 | " Merge_Sort(left_data)\n", 175 | " Merge_Sort(right_data)\n", 176 | " \n", 177 | " left_index = 0\n", 178 | " right_index = 0\n", 179 | " data_index = 0\n", 180 | " \n", 181 | " while left_index < len(left_data) and right_index < len(right_data):\n", 182 | " if left_data[left_index] < right_data[right_index]:\n", 183 | " data[data_index] = left_data[left_index]\n", 184 | " left_index += 1\n", 185 | " else:\n", 186 | " data[data_index] = right_data[right_index]\n", 187 | " right_index += 1\n", 188 | " data_index += 1\n", 189 | " \n", 190 | " if left_index < len(left_data):\n", 191 | " del data[data_index:]\n", 192 | " data += left_data[left_index:]\n", 193 | " elif right_index < len(right_data):\n", 194 | " del data[data_index:]\n", 195 | " data += right_data[right_index:]\n", 196 | " \n", 197 | "data = [9, 0, 8, 6, 2, 5, 7, 3, 4, 1]\n", 198 | "Merge_Sort(data)\n", 199 | "print(data)" 200 | ] 201 | }, 202 | { 203 | "cell_type": "code", 204 | "execution_count": 83, 205 | "metadata": {}, 206 | "outputs": [ 207 | { 208 | "name": "stdout", 209 | "output_type": "stream", 210 | "text": [ 211 | "1\n", 212 | "2\n", 213 | "3\n", 214 | "4\n", 215 | "CPU times: user 3 µs, sys: 0 ns, total: 3 µs\n", 216 | "Wall time: 5.48 µs\n", 217 | "5\n", 218 | "CPU times: user 3 µs, sys: 0 ns, total: 3 µs\n", 219 | "Wall time: 5.25 µs\n" 220 | ] 221 | } 222 | ], 223 | "source": [ 224 | "# O(n)\n", 225 | "# Just print some itens\n", 226 | "%time\n", 227 | "def Print_Item(data):\n", 228 | " for i in data:\n", 229 | " print(i)\n", 230 | "\n", 231 | "Print_Item([1, 2, 3, 4])\n", 232 | "\n", 233 | "\n", 234 | "# Linear search\n", 235 | "%time\n", 236 | "def Linear_Search(data, value):\n", 237 | " for index in range(len(data)):\n", 238 | " if value == data[index]:\n", 239 | " return index\n", 240 | " raise ValueError('Value not found in the list')\n", 241 | "data = [1, 3, 7, 4, 5, 9, 0, 11]\n", 242 | "print(Linear_Search(data,9))" 243 | ] 244 | }, 245 | { 246 | "cell_type": "code", 247 | "execution_count": 81, 248 | "metadata": {}, 249 | "outputs": [ 250 | { 251 | "name": "stdout", 252 | "output_type": "stream", 253 | "text": [ 254 | "0\n", 255 | "3\n", 256 | "6\n", 257 | "9\n", 258 | "CPU times: user 3 µs, sys: 0 ns, total: 3 µs\n", 259 | "Wall time: 5.72 µs\n", 260 | "7\n" 261 | ] 262 | } 263 | ], 264 | "source": [ 265 | "# O(log(n))\n", 266 | "# This algorithms with logarithmic time complexity are commonly found on binary trees\n", 267 | "%time\n", 268 | "for idx in range(0, len(data), 3):\n", 269 | " print(data[idx])\n", 270 | "\n", 271 | "# Binary search\n", 272 | "def binary_search(data, value):\n", 273 | " n = len(data)\n", 274 | " left = 0\n", 275 | " right = n - 1\n", 276 | " while left <= right:\n", 277 | " middle = (left + right) // 2\n", 278 | " if value < data[middle]:\n", 279 | " right = middle - 1\n", 280 | " elif value > data[middle]:\n", 281 | " left = middle + 1\n", 282 | " else:\n", 283 | " return middle\n", 284 | " raise ValueError('Value is not in the list')\n", 285 | "\n", 286 | "data = [1, 2, 3, 4, 5, 6, 7, 8, 9]\n", 287 | "print(binary_search(data, 8))" 288 | ] 289 | }, 290 | { 291 | "cell_type": "code", 292 | "execution_count": 82, 293 | "metadata": {}, 294 | "outputs": [ 295 | { 296 | "name": "stdout", 297 | "output_type": "stream", 298 | "text": [ 299 | "1\n", 300 | "CPU times: user 3 µs, sys: 0 ns, total: 3 µs\n", 301 | "Wall time: 5.72 µs\n" 302 | ] 303 | } 304 | ], 305 | "source": [ 306 | "# O(0n + 1)\n", 307 | "%time\n", 308 | "def First_Idx(data):\n", 309 | " return data[0]\n", 310 | " \n", 311 | "data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n", 312 | "print(First_Idx(data))" 313 | ] 314 | } 315 | ], 316 | "metadata": { 317 | "kernelspec": { 318 | "display_name": "Python 3", 319 | "language": "python", 320 | "name": "python3" 321 | }, 322 | "language_info": { 323 | "codemirror_mode": { 324 | "name": "ipython", 325 | "version": 3 326 | }, 327 | "file_extension": ".py", 328 | "mimetype": "text/x-python", 329 | "name": "python", 330 | "nbconvert_exporter": "python", 331 | "pygments_lexer": "ipython3", 332 | "version": "3.7.6" 333 | } 334 | }, 335 | "nbformat": 4, 336 | "nbformat_minor": 4 337 | } 338 | -------------------------------------------------------------------------------- /src/efficiency/big_o_efficiency.py: -------------------------------------------------------------------------------- 1 | # # Big O Notation 2 | 3 | # The goal of this lesson is to develop your ability to look at some code and indentify its time complexity, using Big O notation. 4 | 5 | # Comparison computacional complexity 6 | import matplotlib.pyplot as plt 7 | from scipy.special import gamma 8 | import math 9 | import numpy as np 10 | n = np.linspace(1,101,100) 11 | O1 = gamma(n) 12 | O2 = 2**n 13 | O3 = n**2 14 | O4 = n*np.log(n) / np.log(2) 15 | O5 = n 16 | O6 = np.sqrt(n) 17 | O7 = np.log(n) / np.log(2) 18 | plt.plot(n, O1, '--k', label='n!') 19 | plt.plot(n, O2, '--r', label='2^n') 20 | plt.plot(n, O3, '--g', label='n^2') 21 | plt.plot(n, O4, 'y', label='nlog(n)') 22 | plt.plot(n, O5, 'c', label='n') 23 | plt.plot(n, O6, '--m', label='sqrt(n)') 24 | plt.plot(n, O7, 'b', label='log(n)') 25 | axes = plt.gca() 26 | axes.set(xlim=(0, 100), ylim=(0, 100)) 27 | leg = axes.legend() 28 | plt.show() 29 | 30 | # O(N!) 31 | # This is the Heap's algorithm, which is used for generating all possible permutation of n objects 32 | # Another example could be the Travelling Salesman Problem 33 | def Permutation(data, n): 34 | if n == 1: 35 | print(data) 36 | return 37 | for i in range(n): 38 | Permutation(data, n - 1) 39 | if n % 2 == 0: 40 | data[i], data[n-1] = data[n-1], data[i] 41 | else: 42 | data[0], data[n-1] = data[n-1], data[0] 43 | data = [1, 2] 44 | Permutation(data,len(data)) 45 | get_ipython().run_line_magic('time', '') 46 | 47 | # O(2^n) 48 | # Recursive calculation of Fibonacci numbers 49 | def fibonacci(n): 50 | if n <= 1: 51 | return n 52 | return fibonacci(n-1) + fibonacci(n-2) 53 | print(fibonacci(20)) 54 | get_ipython().run_line_magic('time', '') 55 | 56 | # O(N^2) 57 | # Print pair of numbers in the data 58 | 59 | def Print_Pair(some_list): 60 | for i in some_list: 61 | for j in some_list: 62 | 63 | print("Items: {}, {}".format(i,j)) 64 | Print_Pair([1, 2, 3, 4]) 65 | get_ipython().run_line_magic('time', '') 66 | 67 | # O(nlog(n)) 68 | # Mergesort algorithm 69 | def Merge_Sort(data): 70 | if len(data) <= 1: 71 | return 72 | 73 | mid = len(data) // 2 74 | left_data = data[:mid] 75 | right_data = data[mid:] 76 | 77 | Merge_Sort(left_data) 78 | Merge_Sort(right_data) 79 | 80 | left_index = 0 81 | right_index = 0 82 | data_index = 0 83 | 84 | while left_index < len(left_data) and right_index < len(right_data): 85 | if left_data[left_index] < right_data[right_index]: 86 | data[data_index] = left_data[left_index] 87 | left_index += 1 88 | else: 89 | data[data_index] = right_data[right_index] 90 | right_index += 1 91 | data_index += 1 92 | 93 | if left_index < len(left_data): 94 | del data[data_index:] 95 | data += left_data[left_index:] 96 | elif right_index < len(right_data): 97 | del data[data_index:] 98 | data += right_data[right_index:] 99 | 100 | data = [9, 0, 8, 6, 2, 5, 7, 3, 4, 1] 101 | Merge_Sort(data) 102 | print(data) 103 | get_ipython().run_line_magic('time', '') 104 | 105 | # O(n) 106 | # Just print some itens 107 | 108 | def Print_Item(data): 109 | for i in data: 110 | print(i) 111 | 112 | Print_Item([1, 2, 3, 4]) 113 | get_ipython().run_line_magic('time', '') 114 | 115 | # Linear search 116 | def Linear_Search(data, value): 117 | for index in range(len(data)): 118 | if value == data[index]: 119 | return index 120 | raise ValueError('Value not found in the list') 121 | data = [1, 3, 7, 4, 5, 9, 0, 11] 122 | print(Linear_Search(data,9)) 123 | get_ipython().run_line_magic('time', '') 124 | 125 | # O(log(n)) 126 | # This algorithms with logarithmic time complexity are commonly found on binary trees 127 | for idx in range(0, len(data), 3): 128 | print(data[idx]) 129 | get_ipython().run_line_magic('time', '') 130 | 131 | # Binary search 132 | def binary_search(data, value): 133 | n = len(data) 134 | left = 0 135 | right = n - 1 136 | while left <= right: 137 | middle = (left + right) // 2 138 | if value < data[middle]: 139 | right = middle - 1 140 | elif value > data[middle]: 141 | left = middle + 1 142 | else: 143 | return middle 144 | raise ValueError('Value is not in the list') 145 | 146 | data = [1, 2, 3, 4, 5, 6, 7, 8, 9] 147 | print(binary_search(data, 8)) 148 | 149 | # O(0n + 1) 150 | 151 | def First_Idx(data): 152 | return data[0] 153 | 154 | data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 155 | print(First_Idx(data)) 156 | get_ipython().run_line_magic('time', '') 157 | 158 | -------------------------------------------------------------------------------- /src/efficiency/bigo.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | Comparison computational complexity 5 | Graphs number of operations, N vs number of input items, n for algorithms of common complexities, assumming a constant of 1, by CMG Lee. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 1 24 | 25 | log₂n 26 | 27 | n 28 | 29 | n 30 | 31 | n log₂n 32 | 33 | n² 34 | 35 | 2 36 | 37 | n! 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | n 48 | 0 49 | 10 50 | 20 51 | 30 52 | 40 53 | 50 54 | 60 55 | 70 56 | 80 57 | 90 58 | 100 59 | 60 | 61 | N 62 | 0 63 | 10 64 | 20 65 | 30 66 | 40 67 | 50 68 | 60 69 | 70 70 | 80 71 | 90 72 | 100 73 | 74 | 75 | 76 | 83 | 84 | 85 | 150 | -------------------------------------------------------------------------------- /src/recursion/.ipynb_checkpoints/factorial_using_recursion-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "c95a7dc4", 6 | "metadata": {}, 7 | "source": [ 8 | "The **factorial** function is a mathematical function that multiplies a given number, $n$, and all of the whole numbers from $n$ down to 1.\n", 9 | "\n", 10 | "For example, if $n$ is $4$ then we will get:\n", 11 | "\n", 12 | "$4*3*2*1 = 24$\n", 13 | "\n", 14 | "This is often notated using an exclamation point, as in $4!$ (which would be read as \"four factorial\").\n", 15 | "\n", 16 | "So $4! = 4*3*2*1 = 24$\n", 17 | "\n", 18 | "More generally, we can say that for any input $n$:\n", 19 | "\n", 20 | "$n! = n * (n-1) * (n-2) ... 1$\n", 21 | "\n", 22 | "If you look at this more closely, you will find that the factorial of any number is the product of that number and the factorial of the next smallest number. In other words:\n", 23 | "\n", 24 | "$n! = n*(n-1)!$\n", 25 | "\n", 26 | "Notice that this is recursive, meaning that we can solve for the factorial of any given number by first solving for the factorial of the next smallest number, and the next smallest number, and the next smallest number, and so on, until we reach 1.\n", 27 | "\n", 28 | "If you were to write a Python function called `factorial` to calculate the factorial of a number, then we could restate what we said above as follows:\n", 29 | "\n", 30 | "`factorial(n) = n * factorial(n-1)`\n", 31 | "\n", 32 | "So that is the goal of this exercise: To use recursion to write a function that will take a number and return the factorial of that number.\n", 33 | "\n", 34 | "For example, you should be able to call your function with `factorial(4)` and get back `24`.\n", 35 | "\n", 36 | "**Note:** By definition, $0! = 1$" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": 1, 42 | "id": "8fa20e02", 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "def factorial(n):\n", 47 | " if n <= 1:\n", 48 | " return 1\n", 49 | " else:\n", 50 | " return n * factorial(n-1)" 51 | ] 52 | } 53 | ], 54 | "metadata": { 55 | "kernelspec": { 56 | "display_name": "Python 3", 57 | "language": "python", 58 | "name": "python3" 59 | }, 60 | "language_info": { 61 | "codemirror_mode": { 62 | "name": "ipython", 63 | "version": 3 64 | }, 65 | "file_extension": ".py", 66 | "mimetype": "text/x-python", 67 | "name": "python", 68 | "nbconvert_exporter": "python", 69 | "pygments_lexer": "ipython3", 70 | "version": "3.8.8" 71 | } 72 | }, 73 | "nbformat": 4, 74 | "nbformat_minor": 5 75 | } 76 | -------------------------------------------------------------------------------- /src/recursion/.ipynb_checkpoints/fibonacci_using_recursion-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [], 3 | "metadata": {}, 4 | "nbformat": 4, 5 | "nbformat_minor": 5 6 | } 7 | -------------------------------------------------------------------------------- /src/recursion/.ipynb_checkpoints/intro_recursion-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "bb8f17fd", 6 | "metadata": {}, 7 | "source": [ 8 | "# Recursion\n", 9 | "\n", 10 | "In computer science Recursion is a technique for solving problems where the solution to a particular problem depends on the solution to a smaller instance of the same problem.\n", 11 | "\n", 12 | "- [GeeksforGeeks](https://www.geeksforgeeks.org/recursion/)\n", 13 | "\n", 14 | "Algorithmically\n", 15 | "- We can think that Recursion is a way to design solutions to problems by **divide and conquer** or **decrease and conquer**\n", 16 | "- Reduce a problem to simpler versions of the same problem\n", 17 | "\n", 18 | "Semantically\n", 19 | "- A programming technique where a **function calls itself**\n", 20 | "- Goal is to NOT have infinite recursion\n", 21 | "- Must have 1 or more base cases that are easy to solve" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "id": "d116385d", 27 | "metadata": {}, 28 | "source": [ 29 | " Recursion works really well with recursive structures like tress and graphs\n", 30 | "\n", 31 | "| Pros | Cons |\n", 32 | "| ---- | ---- |\n", 33 | "| Bridges the gap between elegance and complexity | Slowness due CPU overhead | \n", 34 | "| Reduces the need for complex loops and auxiliary data sctructures | Can lead to out of memory erros |\n", 35 | "| Can reduce time complexity easily with memoization | Can be unnecessarily complex if poorly constructed |" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "id": "15627471", 41 | "metadata": {}, 42 | "source": [ 43 | "## Power of 2\n", 44 | "\n", 45 | "Consider the problem of calculating $\\mathtt{2^5}$. Let's assume to calculate this, you need to do one multiplication after another. That's $2 * 2 * 2 * 2 * 2$. We know that $2^5 = 2 * 2^4$. If we know the value of $2^4$, we can easily calculate $2^5$.\n", 46 | "\n", 47 | "We can use recursion to solve this problem, since the solution to the original problem ($2^n$) depends on the solution to a smaller instance ($2^{n-1}$) of the same problem. The recursive solution is to calculate $2 * 2^{n-1}$ for all n that is greater than 0. If n is 0, return 1. We'll ignore all negative numbers.\n", 48 | "\n", 49 | "Let's look at what the recursive steps would be for calculating $2^5$.\n", 50 | "\n", 51 | "$2^5 = 2 * 2^4$\n", 52 | "\n", 53 | "$2^5 = 2 * 2 * 2^3$\n", 54 | "\n", 55 | "$2^5 = 2 * 2 * 2 * 2^2$\n", 56 | "\n", 57 | "$2^5 = 2 * 2 * 2 * 2 * 2^1$\n", 58 | "\n", 59 | "$2^5 = 2 * 2 * 2 * 2 * 2 * 2^0$\n" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": 1, 65 | "id": "d6da0354", 66 | "metadata": {}, 67 | "outputs": [], 68 | "source": [ 69 | "def power2(n):\n", 70 | " if n <= 0:\n", 71 | " return 1\n", 72 | " else:\n", 73 | " return 2 * power2(n - 1)" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": 2, 79 | "id": "d9181bdf", 80 | "metadata": {}, 81 | "outputs": [ 82 | { 83 | "data": { 84 | "text/plain": [ 85 | "32" 86 | ] 87 | }, 88 | "execution_count": 2, 89 | "metadata": {}, 90 | "output_type": "execute_result" 91 | } 92 | ], 93 | "source": [ 94 | "power2(5)" 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "id": "1a3506de", 100 | "metadata": {}, 101 | "source": [ 102 | "## Sum of integers\n", 103 | "Implement `sum_integers(n)` to calculate the sum of all integers from $1$ to $n$ using recursion. " 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": 3, 109 | "id": "fdc9ff8e", 110 | "metadata": {}, 111 | "outputs": [], 112 | "source": [ 113 | "def sum_integers(n):\n", 114 | " if n <= 1:\n", 115 | " return 1\n", 116 | " else:\n", 117 | " output = n + sum_integers(n - 1)\n", 118 | " return output" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": 4, 124 | "id": "73a4a61b", 125 | "metadata": {}, 126 | "outputs": [ 127 | { 128 | "data": { 129 | "text/plain": [ 130 | "55" 131 | ] 132 | }, 133 | "execution_count": 4, 134 | "metadata": {}, 135 | "output_type": "execute_result" 136 | } 137 | ], 138 | "source": [ 139 | "sum_integers(10)" 140 | ] 141 | }, 142 | { 143 | "cell_type": "markdown", 144 | "id": "af6301f9", 145 | "metadata": {}, 146 | "source": [ 147 | "## Sum of elements of array\n", 148 | "Implement `sum_array(n)` to calculate the sum of all elements in the array from $0$ to $n$ using recursion. " 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": 5, 154 | "id": "c8355278", 155 | "metadata": {}, 156 | "outputs": [], 157 | "source": [ 158 | "def sum_array(array):\n", 159 | " \n", 160 | " if len(array) == 1:\n", 161 | " return array[0]\n", 162 | " \n", 163 | " return array[0] + sum_array(array[1:])\n" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": 6, 169 | "id": "92c0dc4f", 170 | "metadata": {}, 171 | "outputs": [ 172 | { 173 | "data": { 174 | "text/plain": [ 175 | "55" 176 | ] 177 | }, 178 | "execution_count": 6, 179 | "metadata": {}, 180 | "output_type": "execute_result" 181 | } 182 | ], 183 | "source": [ 184 | "arr = [1, 2, 3, 4, 5, 6, 7, 8 , 9, 10]\n", 185 | "sum_array(arr)" 186 | ] 187 | } 188 | ], 189 | "metadata": { 190 | "kernelspec": { 191 | "display_name": "Python 3", 192 | "language": "python", 193 | "name": "python3" 194 | }, 195 | "language_info": { 196 | "codemirror_mode": { 197 | "name": "ipython", 198 | "version": 3 199 | }, 200 | "file_extension": ".py", 201 | "mimetype": "text/x-python", 202 | "name": "python", 203 | "nbconvert_exporter": "python", 204 | "pygments_lexer": "ipython3", 205 | "version": "3.8.8" 206 | } 207 | }, 208 | "nbformat": 4, 209 | "nbformat_minor": 5 210 | } 211 | -------------------------------------------------------------------------------- /src/recursion/factorial_using_recursion.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "c95a7dc4", 6 | "metadata": {}, 7 | "source": [ 8 | "The **factorial** function is a mathematical function that multiplies a given number, $n$, and all of the whole numbers from $n$ down to 1.\n", 9 | "\n", 10 | "For example, if $n$ is $4$ then we will get:\n", 11 | "\n", 12 | "$4*3*2*1 = 24$\n", 13 | "\n", 14 | "This is often notated using an exclamation point, as in $4!$ (which would be read as \"four factorial\").\n", 15 | "\n", 16 | "So $4! = 4*3*2*1 = 24$\n", 17 | "\n", 18 | "More generally, we can say that for any input $n$:\n", 19 | "\n", 20 | "$n! = n * (n-1) * (n-2) ... 1$\n", 21 | "\n", 22 | "If you look at this more closely, you will find that the factorial of any number is the product of that number and the factorial of the next smallest number. In other words:\n", 23 | "\n", 24 | "$n! = n*(n-1)!$\n", 25 | "\n", 26 | "Notice that this is recursive, meaning that we can solve for the factorial of any given number by first solving for the factorial of the next smallest number, and the next smallest number, and the next smallest number, and so on, until we reach 1.\n", 27 | "\n", 28 | "If you were to write a Python function called `factorial` to calculate the factorial of a number, then we could restate what we said above as follows:\n", 29 | "\n", 30 | "`factorial(n) = n * factorial(n-1)`\n", 31 | "\n", 32 | "So that is the goal of this exercise: To use recursion to write a function that will take a number and return the factorial of that number.\n", 33 | "\n", 34 | "For example, you should be able to call your function with `factorial(4)` and get back `24`.\n", 35 | "\n", 36 | "**Note:** By definition, $0! = 1$" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": 1, 42 | "id": "8fa20e02", 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "def factorial(n):\n", 47 | " if n <= 1:\n", 48 | " return 1\n", 49 | " else:\n", 50 | " return n * factorial(n-1)" 51 | ] 52 | } 53 | ], 54 | "metadata": { 55 | "kernelspec": { 56 | "display_name": "Python 3", 57 | "language": "python", 58 | "name": "python3" 59 | }, 60 | "language_info": { 61 | "codemirror_mode": { 62 | "name": "ipython", 63 | "version": 3 64 | }, 65 | "file_extension": ".py", 66 | "mimetype": "text/x-python", 67 | "name": "python", 68 | "nbconvert_exporter": "python", 69 | "pygments_lexer": "ipython3", 70 | "version": "3.8.8" 71 | } 72 | }, 73 | "nbformat": 4, 74 | "nbformat_minor": 5 75 | } 76 | -------------------------------------------------------------------------------- /src/recursion/fibonacci_using_recursion.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "dbbf01ce", 6 | "metadata": {}, 7 | "source": [ 8 | "# Fibonacci Sequency\n", 9 | "In mathematics, the Fibonacci numbers, commonly denoted $F_n$, form a sequence, called the Fibonacci sequence, such that each number is the sum of the two preceding ones, starting from $0$ and $1$.\n", 10 | "\n", 11 | "$$ F_0 = 0 \\\\\n", 12 | "F_1 = 1 \\\\\n", 13 | "F_n = F_{n-1} + F_{n-2}\n", 14 | "$$\n", 15 | "\n", 16 | "0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 1, 22 | "id": "7db00115", 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "def fibonacci(n):\n", 27 | " if n <= 1:\n", 28 | " return n\n", 29 | " else:\n", 30 | " return fibonacci(n-1) + fibonacci(n-2)" 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": 2, 36 | "id": "2708e35e", 37 | "metadata": {}, 38 | "outputs": [], 39 | "source": [ 40 | "n_terms = 10" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 3, 46 | "id": "9804c9fc", 47 | "metadata": {}, 48 | "outputs": [ 49 | { 50 | "name": "stdout", 51 | "output_type": "stream", 52 | "text": [ 53 | "The 10 terms from Fibonacci sequence are:\n", 54 | "0\n", 55 | "1\n", 56 | "1\n", 57 | "2\n", 58 | "3\n", 59 | "5\n", 60 | "8\n", 61 | "13\n", 62 | "21\n", 63 | "34\n" 64 | ] 65 | } 66 | ], 67 | "source": [ 68 | "# check if the number is valid\n", 69 | "if n_terms <= 0:\n", 70 | " print('Invalid number, please enter a positive interger')\n", 71 | "else:\n", 72 | " print('The ' + str(n_terms) + ' terms from Fibonacci sequence are:')\n", 73 | " for i in range(0,n_terms):\n", 74 | " print( fibonacci(i) )" 75 | ] 76 | } 77 | ], 78 | "metadata": { 79 | "kernelspec": { 80 | "display_name": "Python 3", 81 | "language": "python", 82 | "name": "python3" 83 | }, 84 | "language_info": { 85 | "codemirror_mode": { 86 | "name": "ipython", 87 | "version": 3 88 | }, 89 | "file_extension": ".py", 90 | "mimetype": "text/x-python", 91 | "name": "python", 92 | "nbconvert_exporter": "python", 93 | "pygments_lexer": "ipython3", 94 | "version": "3.8.8" 95 | } 96 | }, 97 | "nbformat": 4, 98 | "nbformat_minor": 5 99 | } 100 | -------------------------------------------------------------------------------- /src/recursion/intro_recursion.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "bb8f17fd", 6 | "metadata": {}, 7 | "source": [ 8 | "# Recursion\n", 9 | "\n", 10 | "In computer science Recursion is a technique for solving problems where the solution to a particular problem depends on the solution to a smaller instance of the same problem.\n", 11 | "\n", 12 | "- [GeeksforGeeks](https://www.geeksforgeeks.org/recursion/)\n", 13 | "\n", 14 | "Algorithmically\n", 15 | "- We can think that Recursion is a way to design solutions to problems by **divide and conquer** or **decrease and conquer**\n", 16 | "- Reduce a problem to simpler versions of the same problem\n", 17 | "\n", 18 | "Semantically\n", 19 | "- A programming technique where a **function calls itself**\n", 20 | "- Goal is to NOT have infinite recursion\n", 21 | "- Must have 1 or more base cases that are easy to solve" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "id": "d116385d", 27 | "metadata": {}, 28 | "source": [ 29 | " Recursion works really well with recursive structures like tress and graphs\n", 30 | "\n", 31 | "| Pros | Cons |\n", 32 | "| ---- | ---- |\n", 33 | "| Bridges the gap between elegance and complexity | Slowness due CPU overhead | \n", 34 | "| Reduces the need for complex loops and auxiliary data sctructures | Can lead to out of memory erros |\n", 35 | "| Can reduce time complexity easily with memoization | Can be unnecessarily complex if poorly constructed |" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "id": "15627471", 41 | "metadata": {}, 42 | "source": [ 43 | "## Power of 2\n", 44 | "\n", 45 | "Consider the problem of calculating $\\mathtt{2^5}$. Let's assume to calculate this, you need to do one multiplication after another. That's $2 * 2 * 2 * 2 * 2$. We know that $2^5 = 2 * 2^4$. If we know the value of $2^4$, we can easily calculate $2^5$.\n", 46 | "\n", 47 | "We can use recursion to solve this problem, since the solution to the original problem ($2^n$) depends on the solution to a smaller instance ($2^{n-1}$) of the same problem. The recursive solution is to calculate $2 * 2^{n-1}$ for all n that is greater than 0. If n is 0, return 1. We'll ignore all negative numbers.\n", 48 | "\n", 49 | "Let's look at what the recursive steps would be for calculating $2^5$.\n", 50 | "\n", 51 | "$2^5 = 2 * 2^4$\n", 52 | "\n", 53 | "$2^5 = 2 * 2 * 2^3$\n", 54 | "\n", 55 | "$2^5 = 2 * 2 * 2 * 2^2$\n", 56 | "\n", 57 | "$2^5 = 2 * 2 * 2 * 2 * 2^1$\n", 58 | "\n", 59 | "$2^5 = 2 * 2 * 2 * 2 * 2 * 2^0$\n" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": 1, 65 | "id": "d6da0354", 66 | "metadata": {}, 67 | "outputs": [], 68 | "source": [ 69 | "def power2(n):\n", 70 | " if n <= 0:\n", 71 | " return 1\n", 72 | " else:\n", 73 | " return 2 * power2(n - 1)" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": 2, 79 | "id": "d9181bdf", 80 | "metadata": {}, 81 | "outputs": [ 82 | { 83 | "data": { 84 | "text/plain": [ 85 | "32" 86 | ] 87 | }, 88 | "execution_count": 2, 89 | "metadata": {}, 90 | "output_type": "execute_result" 91 | } 92 | ], 93 | "source": [ 94 | "power2(5)" 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "id": "1a3506de", 100 | "metadata": {}, 101 | "source": [ 102 | "## Sum of integers\n", 103 | "Implement `sum_integers(n)` to calculate the sum of all integers from $1$ to $n$ using recursion. " 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": 3, 109 | "id": "fdc9ff8e", 110 | "metadata": {}, 111 | "outputs": [], 112 | "source": [ 113 | "def sum_integers(n):\n", 114 | " if n <= 1:\n", 115 | " return 1\n", 116 | " else:\n", 117 | " output = n + sum_integers(n - 1)\n", 118 | " return output" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": 4, 124 | "id": "73a4a61b", 125 | "metadata": {}, 126 | "outputs": [ 127 | { 128 | "data": { 129 | "text/plain": [ 130 | "55" 131 | ] 132 | }, 133 | "execution_count": 4, 134 | "metadata": {}, 135 | "output_type": "execute_result" 136 | } 137 | ], 138 | "source": [ 139 | "sum_integers(10)" 140 | ] 141 | }, 142 | { 143 | "cell_type": "markdown", 144 | "id": "af6301f9", 145 | "metadata": {}, 146 | "source": [ 147 | "## Sum of elements of array\n", 148 | "Implement `sum_array(n)` to calculate the sum of all elements in the array from $0$ to $n$ using recursion. " 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": 5, 154 | "id": "c8355278", 155 | "metadata": {}, 156 | "outputs": [], 157 | "source": [ 158 | "def sum_array(array):\n", 159 | " \n", 160 | " if len(array) == 1:\n", 161 | " return array[0]\n", 162 | " \n", 163 | " return array[0] + sum_array(array[1:])\n" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": 6, 169 | "id": "92c0dc4f", 170 | "metadata": {}, 171 | "outputs": [ 172 | { 173 | "data": { 174 | "text/plain": [ 175 | "55" 176 | ] 177 | }, 178 | "execution_count": 6, 179 | "metadata": {}, 180 | "output_type": "execute_result" 181 | } 182 | ], 183 | "source": [ 184 | "arr = [1, 2, 3, 4, 5, 6, 7, 8 , 9, 10]\n", 185 | "sum_array(arr)" 186 | ] 187 | } 188 | ], 189 | "metadata": { 190 | "kernelspec": { 191 | "display_name": "Python 3", 192 | "language": "python", 193 | "name": "python3" 194 | }, 195 | "language_info": { 196 | "codemirror_mode": { 197 | "name": "ipython", 198 | "version": 3 199 | }, 200 | "file_extension": ".py", 201 | "mimetype": "text/x-python", 202 | "name": "python", 203 | "nbconvert_exporter": "python", 204 | "pygments_lexer": "ipython3", 205 | "version": "3.8.8" 206 | } 207 | }, 208 | "nbformat": 4, 209 | "nbformat_minor": 5 210 | } 211 | --------------------------------------------------------------------------------