{"id":16562709,"url":"https://github.com/frankmike/fundamental-algorithms","last_synced_at":"2025-06-15T21:33:16.839Z","repository":{"id":160860176,"uuid":"588192029","full_name":"FrankMike/Fundamental-Algorithms","owner":"FrankMike","description":"A collection of fundamental algorithms","archived":false,"fork":false,"pushed_at":"2023-07-14T08:12:23.000Z","size":41,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-05T05:42:54.737Z","etag":null,"topics":["algorithms","algorithms-and-data-structures","data-structures","python","python3"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/FrankMike.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-01-12T14:44:49.000Z","updated_at":"2024-01-05T11:31:41.000Z","dependencies_parsed_at":null,"dependency_job_id":"cb707658-52ed-4682-93e6-89805c6fbd3c","html_url":"https://github.com/FrankMike/Fundamental-Algorithms","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/FrankMike/Fundamental-Algorithms","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FrankMike%2FFundamental-Algorithms","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FrankMike%2FFundamental-Algorithms/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FrankMike%2FFundamental-Algorithms/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FrankMike%2FFundamental-Algorithms/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FrankMike","download_url":"https://codeload.github.com/FrankMike/Fundamental-Algorithms/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FrankMike%2FFundamental-Algorithms/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260053248,"owners_count":22951844,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["algorithms","algorithms-and-data-structures","data-structures","python","python3"],"created_at":"2024-10-11T20:36:52.984Z","updated_at":"2025-06-15T21:33:16.795Z","avatar_url":"https://github.com/FrankMike.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Fundamental Algorithms\n\n## Searching:\n- Linear Search\n- Binary Search\n- Breadth First Search (BFS)\n- Depth First Search (DFS)\n\n## Sorting:\n- Insertion Sort Algorithm\n- Heap Sort Algorithm\n- Selection Sort Algorithm\n- Merge Sort Algorithm\n- Counting Sort Algorithm\n- Quick Sort Algorithm\n\n## Graph:\n- Kruskal's Algorithm (MST Minimum Spanning Tree)\n- Dijkstra's Algorithm\n- Bellman Ford Algorithm\n- Floyd Warshall Algorithm\n- Topological Sort Algorithm\n- Flood Fill Algorithm\n- Lee Algorithm\n- Kahn's Topological Sort Algorithm\n\n## Tree Traversals:\n- Inorder\n- Preorder\n- Postorder\n\n## Arrays:\n- Kadane's Algorithm\n- Floyd's Cycle Detection Algorithm\n- KMP Algorithm\n- Quick select Algorithm\n- Boyer-Moore Majority Vote Algorithm\n\n## Basics:\n- Huffman Coding Compression Algorithm\n- Union Find Algorithm\n- Euclid's Algorithm\n\n\n\n# Algorithm analysis\n\n## Searching\n### *Linear search:*\n\nA ***linear*** search or ***sequential*** search seaquentially checks each element of a list until a match is found or the whole list has been searched.\n\nPseudocode follows:\n\n```\nprocedure linear_search (list, value)\n\n   for each item in the list\n      if match item == value\n         return the item's location\n      end if\n   end for\n\nend procedure\n```\n\nLinear search is simple to implement, it can be used whether the array/list is sorted or not and can be used on arrays/lists of any data type.\nIt's well suited for small datasets but has a time complexity of O(n), which makes it slow for large datasets.\n\n### *Binary search:*\nAlso known as ***half-interval search***, ***logarithmic search***, or ***binary chop***, is a search algorithm that finds the position of a target value within a **sorted array**.\n\n**Binary search** compares the target value to the middle element of the array. If they are not equal, the half in which the target cannot lie is eliminated and the search continues on the remaining half, again taking the middle element to compare to the target value, and repeating this until the target value is found. If the search ends with the remaining half being empty, the target is not in the array.\n\nBinary Search can be implemented as iterative or recursive method as shown on the pseudocode.\n\nIteration method:\n```\n# Iterative method\n\nbinarySearch(arr, x, low, high)\n        repeat till low = high\n               mid = (low + high)/2\n                   if (x == arr[mid])\n                   return mid\n   \n                   else if (x \u003e arr[mid]) // x is on the right side\n                       low = mid + 1\n   \n                   else                  // x is on the left side\n                       high = mid - 1\n```\n\nRecursive method:\n```\n# Recursive method\n\nbinarySearch(arr, x, low, high)\n           if low \u003e high\n               return False \n   \n           else\n               mid = (low + high) / 2 \n                   if x == arr[mid]\n                   return mid\n       \n               else if x \u003e arr[mid]        // x is on the right side\n                   return binarySearch(arr, x, mid + 1, high)\n               \n               else                        // x is on the left side\n                   return binarySearch(arr, x, low, mid - 1) \n```\n\nFaster than linear search, especially for large arrays.\nThe loogaithmic time in the worst case scenario is O(log n), however the array must be sorted. This adds at least an additional O(n log n) time complexity for the sorting step.\n\n### *Breadth First Search (BFS):*\n\n***Breadth-First Search (BFS)*** is an algorithm for searching a ***tree*** data structure for a node that satisfies a given property.\n\nIt starts at the tree root and explores all nodes at the present depth prior to moving on to the nodes at the next depth level.\n\nFor a graph Breadth-First Search, to avoid processing a node in a graph cycle more than once, the vertices are divided into two categories:\n- Visited\n- Not Visited\n\nIt is assumed that all vertices are reachable from the starting vertex.\nExtra memory, a queue in particular, is needed to keep track of the child nodes that were encountered but not yet explored.\n\nPseudocode for BFS implementation on a Graph:\n```\nBreadth_First_Serach( Graph, X ) // Here, Graph is the graph that we already have and X is the source node\n\nLet Q be the queue\nQ.enqueue( X ) // Inserting source node X into the queue\nMark X node as visited.\n\nWhile ( Q is not empty )\nY = Q.dequeue( ) // Removing the front node from the queue\n\nProcess all the neighbors of Y, For all the neighbors Z of Y\nIf Z is not visited, Q. enqueue( Z ) // Stores Z in Q\nMark Z as visited\n```\n\nThe time complexity is O(V+E) where V is the number of Nodes of Vertices and E is the number of Edges.\nThe auxiliary space complexity is O(V) in the worst case scenario.\n\n\n### *Depth First Search (DFS):*\n\nAlgorithm used for traversing or searching ***tree*** or ***graph*** data structures. DFS starts at the root node (selecting some arbitrary node as the root node in the case of a graph) and explores as far as possible along each branch before backtracking.\nExtra memory, usually a stack, is needed to keep track of the nodes discovered so far along a specific branch wich helps in backtracking of the graph.\nTo avoid processing a node more than once, a boolean is used to store the visited nodes.\nA graph can have more than one DFS traversal.\n\n\n```\nDFS(G, u)\n    u.visited = true\n    for each v ∈ G.Adj[u]\n        if v.visited == false\n            DFS(G,v)\n     \ninit() {\n    For each u ∈ G\n        u.visited = false\n     For each u ∈ G\n       DFS(G, u)\n}\n```\nWhere u is the root.\n\nThe time complexity is O(V+E) where V is the number of Nodes of Vertices and E is the number of Edges.\nThe auxiliary space complexity is O(V) in the worst case scenario.\n\n\n## Sorting\n### *Insertion Sort:*\n***Insertion Sort*** is a sorting algorithm that builds the final sorted array/list one item at a time by comparisons. It's much less efficient on large lists\nThe time complexity is quadratic O(n^2).\nInsertion sort iterates, consuming one input element each repetition, and grows a sorted output list. At each iteration, insertion sort removes one element from the input data, finds the location it belongs within the sorted list, and inserts it there. It repeats until no input elements remain.\n\n ```\n procedure insertionSort(A: list of sortable items)\n   n = length(A)\n   for i = 1 to n - 1 do\n       j = i\n       while j \u003e 0 and A[j-1] \u003e A[j] do\n           swap(A[j], A[j-1])\n           j = j - 1\n       end while\n   end for\nend procedure\n ```\n [Insertion Sort](https://www.programiz.com/dsa/insertion-sort)\n\n ### *Heap Sort:*\n\nHeap sort is a comparison-based sorting technique based on Binary Heap data structure. It is similar to the selection sort where we first find the minimum element and place the minimum element at the beginning. Repeat the same process for the remaining elements.\nPseudocode for Heap Sort:\n```\nprocedure heapsort(a, count) is\n    input: an unordered array a of length count\n \n    (Build the heap in array a so that largest value is at the root)\n    heapify(a, count)\n\n    (The following loop maintains the invariants that a[0:end] is a heap and every element\n     beyond end is greater than everything before it (so a[end:count] is in sorted order))\n    end ← count - 1\n    while end \u003e 0 do\n        (a[0] is the root and largest value. The swap moves it in front of the sorted elements.)\n        swap(a[end], a[0])\n        (the heap size is reduced by one)\n        end ← end - 1\n        (the swap ruined the heap property, so restore it)\n        siftDown(a, 0, end)\n```\nThe sorting routine uses two subroutines, heapify and siftDown. The former is the common in-place heap construction routine, while the latter is a common subroutine for implementing heapify.\nProcedure for Heapify function.\n```\n(Put elements of 'a' in heap order, in-place)\nprocedure heapify(a, count) is\n    (start is assigned the index in 'a' of the last parent node)\n    (the last element in a 0-based array is at index count-1; find the parent of that element)\n    start ← iParent(count-1)\n    \n    while start ≥ 0 do\n        (sift down the node at index 'start' to the proper place such that all nodes below\n         the start index are in heap order)\n        siftDown(a, start, count - 1)\n        (go to the next parent node)\n        start ← start - 1\n    (after sifting down the root all nodes/elements are in heap order)\n\n(Repair the heap whose root element is at index 'start', assuming the heaps rooted at its children are valid)\nprocedure siftDown(a, start, end) is\n    root ← start\n\n    while iLeftChild(root) ≤ end do    (While the root has at least one child)\n        child ← iLeftChild(root)   (Left child of root)\n        swap ← root                (Keeps track of child to swap with)\n\n        if a[swap] \u003c a[child] then\n            swap ← child\n        (If there is a right child and that child is greater)\n        if child+1 ≤ end and a[swap] \u003c a[child+1] then\n            swap ← child + 1\n        if swap = root then\n            (The root holds the largest element. Since we assume the heaps rooted at the\n             children are valid, this means that we are done.)\n            return\n        else\n            Swap(a[root], a[swap])\n            root ← swap          (repeat to continue sifting down the child now)\n```\nThe time complexity of Heap Sort is O(n log n).\n\n[Heap Sort](https://www.programiz.com/dsa/heap-sort)\n\n### *Selection Sort:*\n\nSelection sort is a simple and efficient sorting algorithm that works by repeatedly selecting the smallest (or largest) element from the unsorted portion of the list and moving it to the sorted portion of the list. The algorithm repeatedly selects the smallest (or largest) element from the unsorted portion of the list and swaps it with the first element of the unsorted portion. This process is repeated for the remaining unsorted portion of the list until the entire list is sorted. One variation of selection sort is called “Bidirectional selection sort” that goes through the list of elements by alternating between the smallest and largest element, this way the algorithm can be faster in some cases.\n\nThe algorithm maintains two subarrays in a given array.\n- The subarray which already sorted. \n- The remaining subarray was unsorted.\n\nThe time complexity is O(n^2) in the worst case scenario for the comparisons and O(n) for swaps.\n\n```\nprocedure selection sort \n   list  : array of items\n   n     : size of list\n\n   for i = 1 to n - 1\n   /* set current element as minimum*/\n      min = i    \n  \n      /* check the element to be minimum */\n\n      for j = i+1 to n \n         if list[j] \u003c list[min] then\n            min = j;\n         end if\n      end for\n\n      /* swap the minimum element with the current element*/\n      if indexMin != i  then\n         swap list[min] and list[i]\n      end if\n   end for\n\t\nend procedure\n```\n\n[Selection Sort](https://www.programiz.com/dsa/selection-sort)\n\n### *Merge Sort:*\n\nMerge sort is a sorting algorithm that works by dividing an array into smaller subarrays, sorting each subarray, and then merging the sorted subarrays back together to form the final sorted array.\n\nIn simple terms, we can say that the process of merge sort is to divide the array into two halves, sort each half, and then merge the sorted halves back together. This process is repeated until the entire array is sorted.\nThe algorithm explained:\n```\nstep 1: start\n\nstep 2: declare array and left, right, mid variable\n\nstep 3: perform merge function.\n    if left \u003e right\n        return\n    mid= (left+right)/2\n    mergesort(array, left, mid)\n    mergesort(array, mid+1, right)\n    merge(array, left, mid, right)\n\nstep 4: Stop\n```\n\nTop Down pseudocode:\n```\nfunction merge_sort(list m) is\n    // Base case. A list of zero or one elements is sorted, by definition.\n    if length of m ≤ 1 then\n        return m\n\n    // Recursive case. First, divide the list into equal-sized sublists\n    // consisting of the first half and second half of the list.\n    // This assumes lists start at index 0.\n    var left := empty list\n    var right := empty list\n    for each x with index i in m do\n        if i \u003c (length of m)/2 then\n            add x to left\n        else\n            add x to right\n\n    // Recursively sort both sublists.\n    left := merge_sort(left)\n    right := merge_sort(right)\n\n    // Then merge the now-sorted sublists.\n    return merge(left, right)\n```\n\nBottom up implementation pseudocode:\n```\nfunction merge_sort(node head) is\n    // return if empty list\n    if head = nil then\n        return nil\n    var node array[32]; initially all nil\n    var node result\n    var node next\n    var int  i\n    result := head\n    // merge nodes into array\n    while result ≠ nil do\n        next := result.next;\n        result.next := nil\n        for (i = 0; (i \u003c 32) \u0026\u0026 (array[i] ≠ nil); i += 1) do\n            result := merge(array[i], result)\n            array[i] := nil\n        // do not go past end of array\n        if i = 32 then\n            i -= 1\n        array[i] := result\n        result := next\n    // merge array into single list\n    result := nil\n    for (i = 0; i \u003c 32; i += 1) do\n        result := merge(array[i], result)\n    return result\n```\n\nTime complexity of Merge Sort is O(n log n) in the worst case scenario.\n\n[Merge Sort](https://www.programiz.com/dsa/merge-sort)\n\n\n### *Counting Sort:*\n\nCounting sort is an algorithm for sorting a collection of objects according to keys that are small positive integers; that is, it is an integer sorting algorithm.\nCounting sort is a sorting technique based on keys between a specific range. It works by counting the number of objects having distinct key values (a kind of hashing). Then do some arithmetic operations to calculate the position of each object in the output sequence. \n\n```\nfunction CountingSort(input, k)\n    \n    count ← array of k + 1 zeros\n    output ← array of same length as input\n    \n    for i = 0 to length(input) - 1 do\n        j = key(input[i])\n        count[j] = count[j] + 1\n\n    for i = 1 to k do\n        count[i] = count[i] + count[i - 1]\n\n    for i = length(input) - 1 down to 0 do\n        j = key(input[i])\n        count[j] = count[j] - 1\n        output[count[j]] = input[i]\n\n    return output\n```\n\nTime complexity of Counting sort is O(n+k) where k is the range of the non-negative key values.\n\n[Counting Sort](https://www.programiz.com/dsa/counting-sort)\n\n### *Quick Sort:*\n\nQuickSort is a Divide and Conquer algorithm. It picks an element as a pivot and partitions the given array around the picked pivot. There are many different versions of quickSort that pick pivot in different ways. \n- Always pick the first element as a pivot.\n- Always pick the last element as a pivot (implemented below)\n- Pick a random element as a pivot.\n- Pick median as the pivot.\nThe key process in quickSort is a partition(). The target of partitions is, given an array and an element x of an array as the pivot, put x at its correct position in a sorted array and put all smaller elements (smaller than x) before x, and put all greater elements (greater than x) after x. All this should be done in linear time.\n\nPseudocode for recursive Quick Sort function:\n\n```\n/* low  –\u003e Starting index,  high  –\u003e Ending index */\n\nquickSort(arr[], low, high) {\n\n    if (low \u003c high) {\n\n        /* pi is partitioning index, arr[pi] is now at right place */\n\n        pi = partition(arr, low, high);\n\n        quickSort(arr, low, pi – 1);  // Before pi\n\n        quickSort(arr, pi + 1, high); // After pi\n\n    }\n\n}\n```\n\nPseudocode for the partition function:\n```\n/* This function takes last element as pivot, places the pivot element at its correct position in sorted array, and places all smaller (smaller than pivot) to left of pivot and all greater elements to right of pivot */\n\npartition (arr[], low, high)\n{\n    // pivot (Element to be placed at right position)\n    pivot = arr[high];  \n\n    i = (low – 1)  // Index of smaller element and indicates the \n    // right position of pivot found so far\n\n    for (j = low; j \u003c= high- 1; j++){\n\n        // If current element is smaller than the pivot\n        if (arr[j] \u003c pivot){\n            i++;    // increment index of smaller element\n            swap arr[i] and arr[j]\n        }\n    }\n    swap arr[i + 1] and arr[high])\n    return (i + 1)\n}\n```\n\n[Quick Sort](https://www.programiz.com/dsa/quick-sort)\n\n## Graph\n### *Kruskal's Algorithm:*\n\nKruskal's algorithm is a minimum spanning tree algorithm that takes a graph as input and finds the subset of the edges of that graph which\n- form a tree that includes every vertex\n- has the minimum sum of weights among all the trees that can be formed from the graph\n\nIt falls under a class of algorithms called greedy algorithms that find the local optimum in the hopes of finding a global optimum.\n\nWe start from the edges with the lowest weight and keep adding edges until we reach our goal.\n\nThe steps for implementing Kruskal's algorithm are as follows:\n\n- Sort all the edges from low weight to high\n- Take the edge with the lowest weight and add it to the spanning tree. If adding the edge created a cycle, then reject this edge.\n- Keep adding edges until we reach all vertices.\n\nPseudocode:\n```\nKRUSKAL(G):\nA = ∅\nFor each vertex v ∈ G.V:\n    MAKE-SET(v)\nFor each edge (u, v) ∈ G.E ordered by increasing order by weight(u, v):\n    if FIND-SET(u) ≠ FIND-SET(v):       \n    A = A ∪ {(u, v)}\n    UNION(u, v)\nreturn A\n```\n\nThe time complexity Of Kruskal's Algorithm is: O(E log E).\n\n[Kruskal's Algorithm](https://www.programiz.com/dsa/kruskal-algorithm)\n\n\n### *Dijkstra's Algorithm:*\n\nDijkstra's algorithm allows us to find the shortest path between any two vertices of a graph.\n\nIt differs from the minimum spanning tree because the shortest distance between two vertices might not include all the vertices of the graph\n\nDijkstra's Algorithm works on the basis that any subpath B -\u003e D of the shortest path A -\u003e D between vertices A and D is also the shortest path between vertices B and D.\n\nDjikstra used this property in the opposite direction i.e we overestimate the distance of each vertex from the starting vertex. Then we visit each node and its neighbors to find the shortest subpath to those neighbors.\n\nThe algorithm uses a greedy approach in the sense that we find the next best solution hoping that the end result is the best solution for the whole problem.\n\nPseudocode:\n```\nfunction dijkstra(G, S)\n    for each vertex V in G\n        distance[V] \u003c- infinite\n        previous[V] \u003c- NULL\n        If V != S, add V to Priority Queue Q\n    distance[S] \u003c- 0\n\t\n    while Q IS NOT EMPTY\n        U \u003c- Extract MIN from Q\n        for each unvisited neighbour V of U\n            tempDistance \u003c- distance[U] + edge_weight(U, V)\n            if tempDistance \u003c distance[V]\n                distance[V] \u003c- tempDistance\n                previous[V] \u003c- U\n    return distance[], previous[]\n```\n\nTime Complexity: O(E Log V)\n\nwhere, E is the number of edges and V is the number of vertices.\n\nSpace Complexity: O(V)\n\n[Dijkstra's Algorithm](https://www.programiz.com/dsa/dijkstra-algorithm)\n\n\n### *Bellman Ford's Algorithm:*\n\nBellman Ford algorithm helps us find the shortest path from a vertex to all other vertices of a weighted graph.\n\nIt is similar to Dijkstra's algorithm but it can work with graphs in which edges can have negative weights.\n\nPseudocode:\n```\nfunction bellmanFord(G, S)\n  for each vertex V in G\n    distance[V] \u003c- infinite\n      previous[V] \u003c- NULL\n  distance[S] \u003c- 0\n\n  for each vertex V in G\t\t\t\t\n    for each edge (U,V) in G\n      tempDistance \u003c- distance[U] + edge_weight(U, V)\n      if tempDistance \u003c distance[V]\n        distance[V] \u003c- tempDistance\n        previous[V] \u003c- U\n\n  for each edge (U,V) in G\n    If distance[U] + edge_weight(U, V) \u003c distance[V}\n      Error: Negative Cycle Exists\n\n  return distance[], previous[]\n```\n\nThe time complexity on worst case scenario O(VE).\n\n[Bellman Ford's Algorithm](https://www.programiz.com/dsa/bellman-ford-algorithm)\n\n\n### *Floyd-Warshall Algorithm:*\n\nFloyd-Warshall Algorithm is an algorithm for finding the shortest path between all the pairs of vertices in a weighted graph. This algorithm works for both the directed and undirected weighted graphs. But, it does not work for the graphs with negative cycles (where the sum of the edges in a cycle is negative)\n\nThis algorithm follows the dynamic programming approach to find the shortest paths.\n\nPseudocode:\n```\nn = no of vertices\nA = matrix of dimension n*n\nfor k = 1 to n\n    for i = 1 to n\n        for j = 1 to n\n            Ak[i, j] = min (Ak-1[i, j], Ak-1[i, k] + Ak-1[k, j])\nreturn A\n```\n\n[Floyd-Warshall Algorithm](https://www.programiz.com/dsa/floyd-warshall-algorithm)\n\n\n### *Topological Sorting for Directed Acyclic Graph (DAG):*\n\nTopological sorting for Directed Acyclic Graph (DAG) is a linear ordering of vertices such that for every directed edge u v, vertex u comes before v in the ordering.\n\nTopological Sorting for a graph is not possible if the graph is not a DAG.\n\nAny DAG has at least one topological ordering, and algorithms are known for constructing a topological ordering of any DAG in linear time. Topological sorting has many applications especially in ranking problems such as feedback arc set. Topological sorting is possible even when the DAG has disconnected components.\n\nWe can modify DFS to find the Topological Sorting of a graph. \nIn DFS, \n- We start from a vertex, we first print it, and then \n- Recursively call DFS for its adjacent vertices. \nIn topological sorting,\n\n- We use a temporary stack. \n- We don’t print the vertex immediately, \n- we first recursively call topological sorting for all its adjacent vertices, then push it to a stack. \n- Finally, print the contents of the stack.\n\nTime Complexity: O(V + E) The above algorithm is simply DFS with a working stack and a result stack. Unlike the recursive solution, recursion depth is not an issue here.\nAuxiliary space: O(V), The extra space is needed for the 2 stacks used\n\n[Topological Sorting for Directed Acyclic Graph (DAG)](https://www.geeksforgeeks.org/topological-sorting/)\n\n\n### *Flood Fill Algorithm:*\n\nFlood fill, also called seed fill, is a flooding algorithm that determines and alters the area connected to a given node in a multi-dimensional array with some matching attribute.\n\nBFS Approach: The idea is to use BFS traversal to replace the color with the new color. \n\nCreate an empty queue lets say Q.\nPush the starting location of the pixel as given in the input and apply replacement color to it.\nIterate until Q is not empty and pop the front node (pixel position).\nCheck the pixels adjacent to the current pixel and push into the queue if valid (had not been colored with replacement color and have the same color as the old color).\n\n\n[Flood Fill Algorithm](https://www.geeksforgeeks.org/flood-fill-algorithm/)\n\n\n### *Lee Algorithm:*\n\nThe algorithm, also known as Shortest path in a Binary Maze, is a breadth-first based algorithm that uses queues to store the steps. It usually uses the following steps:\n\nChoose a starting point and add it to the queue.\nAdd the valid neighboring cells to the queue.\nRemove the position you are on from the queue and continue to the next element.\nRepeat steps 2 and 3 until the queue is empty.\n\nGiven an MxN matrix where each element can either be 0 or 1. We need to find the shortest path between a given source cell to a destination cell. The path can only be created out of a cell if its value is 1.\n\nLee algorithm uses BFS.  \n\nWe start from the source cell and call the BFS procedure.\nWe maintain a queue to store the coordinates of the matrix and initialize it with the source cell.\nWe also maintain a Boolean array visited of the same size as our input matrix and initialize all its elements to false.\nWe LOOP till queue is not empty\nDequeue front cell from the queue\nReturn if the destination coordinates have been reached.\nFor each of its four adjacent cells, if the value is 1 and they are not visited yet, we enqueue it in the queue and also mark them as visited\n\n[Lee Algorithm](https://www.geeksforgeeks.org/shortest-path-in-a-binary-maze/)\n\n### *Kahn's Topological Sorting Algorithm:*\n\nTopological sorting for Directed Acyclic Graph (DAG) is a linear ordering of vertices such that for every directed edge uv, vertex u comes before v in the ordering. Topological Sorting for a graph is not possible if the graph is not a DAG.\n\nTime Complexity: O(V+E). \nThe outer for loop will be executed V number of times and the inner for loop will be executed E number of times.\nAuxiliary Space: O(V). \nThe queue needs to store all the vertices of the graph. So the space required is O(V)\n\n[Kahn's Topological Sorting Algorithm](https://www.geeksforgeeks.org/topological-sorting-indegree-based-solution/)\n\n\n## *Tree Traversals*\n\nTraversing a tree means visiting every node in the tree. You might, for instance, want to add all the values in the tree or find the largest one. For all these operations, you will need to visit each node of the tree.\n\nLinear data structures like arrays, stacks, queues, and linked list have only one way to read the data. But a hierarchical data structure like a tree can be traversed in different ways.\n\n[Tree Traversals](https://www.programiz.com/dsa/tree-traversal)\n\n### *Pre Order:*\n\nVisit root node\nVisit all the nodes in the left subtree\nVisit all the nodes in the right subtree\n\n### *In Order:*\n\nFirst, visit all the nodes in the left subtree\nThen the root node\nVisit all the nodes in the right subtree\n\n### *Post Order:*\n\nVisit all the nodes in the left subtree\nVisit all the nodes in the right subtree\nVisit the root node\n\n\n## Arrays\n\n### *Kadane's Algorithm:*\n\nDynamic Programming is a method for solving a complex problem by breaking it down into a collection of simpler subproblems, solving each of those subproblems just once, and storing their solutions using a memory-based data structure (array, map, etc.). So the next time the same sub-problem occurs, instead of recomputing its solution, one simply looks up the previously computed solution, thereby saving computation time.\n\nThe idea of Kadane’s algorithm is to maintain a variable max_ending_here that stores the maximum sum contiguous subarray ending at current index and a variable max_so_far stores the maximum sum of contiguous subarray found so far, Everytime there is a positive-sum value in max_ending_here compare it with max_so_far and update max_so_far if it is greater than max_so_far.\n\nPseudocode:\n\n```\nInitialize:\n    max_so_far = INT_MIN\n    max_ending_here = 0\n\nLoop for each element of the array\n\n  (a) max_ending_here = max_ending_here + a[i]\n  (b) if(max_so_far \u003c max_ending_here)\n            max_so_far = max_ending_here\n  (c) if(max_ending_here \u003c 0)\n            max_ending_here = 0\nreturn max_so_far\n```\n\n[Kadane's Algorithm](https://www.geeksforgeeks.org/largest-sum-contiguous-subarray/)\n\n\n### *Floyd's Cycle Detection Algorithm:*\n\nFloyd’s cycle finding algorithm or Hare-Tortoise algorithm is a pointer algorithm that uses only two pointers, moving through the sequence at different speeds. This algorithm is used to find a loop in a linked list. It uses two pointers one moving twice as fast as the other one. The faster one is called the faster pointer and the other one is called the slow pointer.\n\nWhile traversing the linked list one of these things will occur-\n\nThe Fast pointer may reach the end (NULL) this shows that there is no loop in the linked list.\nThe Fast pointer again catches the slow pointer at some time therefore a loop exists in the linked list.\n\n- Initialize two-pointers and start traversing the linked list.\n- Move the slow pointer by one position.\n- Move the fast pointer by two positions.\n- If both pointers meet at some point then a loop exists and if the fast pointer meets the end position then no loop exists.\n\nTime complexity: O(n), as the loop is traversed once.\nAuxiliary Space: O(1), only two pointers are used therefore constant space complexity.\n\n\n[Floyd's Cycle Detection Algorithm](https://www.geeksforgeeks.org/floyds-cycle-finding-algorithm/)\n\n\n### *KMP Algorithm:*\n\nPattern searching is an important problem in computer science. When we do search for a string in a notepad/word file or browser or database, pattern-searching algorithms are used to show the search results. We have discussed the Naive pattern-searching algorithm in the previous post. \nThe worst-case complexity of the Naive algorithm is O(m(n-m+1)). The time complexity of the KMP algorithm is O(n) in the worst case. KMP (Knuth Morris Pratt) Pattern Searching.\n\nThe KMP matching algorithm uses degenerating property (pattern having the same sub-patterns appearing more than once in the pattern) of the pattern and improves the worst-case complexity to O(n). \n\nThe basic idea behind KMP’s algorithm is: whenever we detect a mismatch (after some matches), we already know some of the characters in the text of the next window. We take advantage of this information to avoid matching the characters that we know will anyway match. \n\n[KMP Algorithm for Pattern Searching](https://www.geeksforgeeks.org/kmp-algorithm-for-pattern-searching/)\n\n\n### *Quick select Algorithm:*\n\nQuickselect is a selection algorithm to find the k-th smallest element in an unordered list. It is related to the quick sort sorting algorithm.\n\nThe algorithm is similar to QuickSort. The difference is, instead of recurring for both sides (after finding pivot), it recurs only for the part that contains the k-th smallest element. The logic is simple, if index of the partitioned element is more than k, then we recur for the left part. If index is the same as k, we have found the k-th smallest element and we return. If index is less than k, then we recur for the right part. This reduces the expected complexity from O(n log n) to O(n), with a worst-case of O(n^2).\n\nPseudocode:\n\n```\nfunction quickSelect(list, left, right, k)\n\n   if left = right\n      return list[left]\n\n   Select a pivotIndex between left and right\n\n   pivotIndex := partition(list, left, right, \n                                  pivotIndex)\n   if k = pivotIndex\n      return list[k]\n   else if k \u003c pivotIndex\n      right := pivotIndex - 1\n   else\n      left := pivotIndex + 1 \n```\n\n[Quick Select Algorithm](https://www.geeksforgeeks.org/quickselect-algorithm/)\n\n### *Boyer-Moore Majority Vote Algorithm:*\n\nThe Boyer-Moore voting algorithm is one of the popular optimal algorithms which is used to find the majority element among the given elements that have more than N/ 2 occurrences. This works perfectly fine for finding the majority element which takes 2 traversals over the given elements, which works in O(N) time complexity and O(1) space complexity.\n\nThis algorithm works on the fact that if an element occurs more than N/2 times, it means that the remaining elements other than this would definitely be less than N/2. So let us check the proceeding of the algorithm.\n\nFirst, choose a candidate from the given set of elements if it is the same as the candidate element, increase the votes. Otherwise, decrease the votes if votes become 0, select another new element as the new candidate.\n\n[Boyer-Moore Majority Vote Algorithm](https://www.geeksforgeeks.org/boyer-moore-majority-voting-algorithm/)\n\n\n## Basics\n\n### *Huffman Coding Compression Algorithm:*\n\nHuffman Coding is a technique of compressing data to reduce its size without losing any of the details. It was first developed by David Huffman.\n\nHuffman Coding is generally useful to compress the data in which there are frequently occurring characters.\nEach character occupies 8 bits. There are a total of 15 characters in the above string. Thus, a total of 8 * 15 = 120 bits are required to send this string.\n\nUsing the Huffman Coding technique, we can compress the string to a smaller size.\n\nHuffman coding first creates a tree using the frequencies of the character and then generates code for each character.\n\nOnce the data is encoded, it has to be decoded. Decoding is done using the same tree.\n\nHuffman Coding prevents any ambiguity in the decoding process using the concept of prefix code ie. a code associated with a character should not be present in the prefix of any other code. The tree created above helps in maintaining the property.\n\nPseudocode:\n\n```\ncreate a priority queue Q consisting of each unique character.\nsort then in ascending order of their frequencies.\nfor all the unique characters:\n    create a newNode\n    extract minimum value from Q and assign it to leftChild of newNode\n    extract minimum value from Q and assign it to rightChild of newNode\n    calculate the sum of these two minimum values and assign it to the value of newNode\n    insert this newNode into the tree\nreturn rootNode\n```\n\nThe time complexity for encoding each unique character based on its frequency is O(nlog n).\n\nExtracting minimum frequency from the priority queue takes place 2*(n-1) times and its complexity is O(log n). Thus the overall complexity is O(nlog n).\n\n[Huffman Coding Compression Algorithm](https://www.programiz.com/dsa/huffman-coding)\n\n\n### *Union Find Algorithm:*\n\nA union-find algorithm is an algorithm that performs two useful operations on such a data structure:\n\nFind: Determine which subset a particular element is in. This can be used for determining if two elements are in the same subset.\nUnion: Join two subsets into a single subset. Here first we have to check if the two subsets belong to same set. If no, then we cannot perform union. \n\nNote that the implementation of union() and find() is naive and takes O(n) time in the worst case.\nAuxiliary Space: O(1).\n\n[Union Find Algorithm](https://www.geeksforgeeks.org/introduction-to-disjoint-set-data-structure-or-union-find-algorithm/)\n\n\n### *Euclid's Algorithm:*\n\nThe Euclidean algorithm is a way to find the greatest common divisor of two positive integers. GCD of two numbers is the largest number that divides both of them. A simple way to find GCD is to factorize both numbers and multiply common prime factors.\n\nThe algorithm is based on the below facts. \n\nIf we subtract a smaller number from a larger one (we reduce a larger number), GCD doesn’t change. So if we keep subtracting repeatedly the larger of two, we end up with GCD.\nNow instead of subtraction, if we divide the smaller number, the algorithm stops when we find the remainder 0.\n\nThe extended Euclidean algorithm updates the results of gcd(a, b) using the results calculated by the recursive call gcd(b%a, a). Let values of x and y calculated by the recursive call be x1 and y1. x and y are updated using the below expressions.\n\nTime Complexity: O(log N)\nAuxiliary Space: O(log N)\n\n[Euclidean Algorithm](https://www.geeksforgeeks.org/euclidean-algorithms-basic-and-extended/)\n\n### *Fermat's little theorem:*\n\nFermat's little theorem states that p is a prime number, then for any integer a, the number a^p - a is an integer multiple of p.\na^p is equivalent to a(mod p).\n\n```\n// returns a^b mod(MOD) (a to the power b mod MOD)\nfunc modular_exponentiation(a, b, MOD)\n\tans = 1\n\ta = a mod(MOD)\n\twhile b is more than 0\n\t\tif b is an odd number\n\t\t\tans = (ans * a) mod(MOD)\n\t\ta = (a * a) mod(MOD)\n\t\tb = b / 2\n\treturn ans\n// it’s assumed that input m is a prime number\nprint(modular_exponentiation(a, m-2, m))\n```\n\n[Fermat's little theorem](https://en.wikipedia.org/wiki/Fermat%27s_little_theorem)\n\n\n# Thanks to\n\n[Wikipedia](https://en.wikipedia.org)\n\n[Geeks for Geeks](https://www.geeksforgeeks.org/)\n\n[Programiz](https://www.programiz.com/)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrankmike%2Ffundamental-algorithms","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffrankmike%2Ffundamental-algorithms","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrankmike%2Ffundamental-algorithms/lists"}