├── .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 | 
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 | 
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 | 
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 | 
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 | 
10 |
11 | *MakeSet* creates 8 singletons.
12 |
13 | 
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 | 
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 | 
18 |
19 | Hash collision resolved by separate chaining.
20 |
21 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
19 |
20 | AVL tree with balance factors (green)
21 |
22 | 
23 |
24 | ### AVL Tree Rotations
25 |
26 | **Left-Left Rotation**
27 |
28 | 
29 |
30 | **Right-Right Rotation**
31 |
32 | 
33 |
34 | **Left-Right Rotation**
35 |
36 | 
37 |
38 | **Right-Left Rotation**
39 |
40 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
74 |
75 | #### Left Right Case (See g, p and x)
76 |
77 | 
78 |
79 | #### Right Right Case (See g, p and x)
80 |
81 | 
82 |
83 | #### Right Left Case (See g, p and x)
84 |
85 | 
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 | 
28 |
29 | 
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 | 
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 |
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 |
--------------------------------------------------------------------------------