diff --git a/.gitignore b/.gitignore index 5152c3d1b..77866c7fd 100755 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ build/ pythonenv3.8/ .vscode/ # Ignoring the virtual Environment when using GitHub Codespaces -.venv/ \ No newline at end of file +.venv/ +.DS_Store diff --git a/algorithms/backtrack/__init__.py b/algorithms/backtrack/__init__.py index f8cdab753..48bfd12ec 100644 --- a/algorithms/backtrack/__init__.py +++ b/algorithms/backtrack/__init__.py @@ -1,5 +1,5 @@ from .add_operators import * -from .anagram import * +from ..strings.anagram import * from .array_sum_combinations import * from .combination_sum import * from .factor_combinations import * diff --git a/algorithms/graph/topological_sorting.py b/algorithms/graph/topological_sorting.py new file mode 100644 index 000000000..7c0d030b2 --- /dev/null +++ b/algorithms/graph/topological_sorting.py @@ -0,0 +1,32 @@ +def findOrder(self, numCourses, prerequisites): + def topologicalsort(g): + indegrees = {node : 0 for node in g} + + for node in g: + for neighbor in g[node]: + indegrees[neighbor] += 1 + + q = [] + for node in g: + if indegrees[node] == 0: + q.append(node) + + order = [] + while q: + node = q.pop() + order.append(node) + for neighbor in g[node]: + indegrees[neighbor] -= 1 + if indegrees[neighbor] == 0: + q.append(neighbor) + + return order + + g = {i : [] for i in range(numCourses)} + for p in prerequisites: + src,dest = p + g[src].append(dest) + + order = topologicalsort(g) + + return reversed(order) if len(order) == numCourses else [] \ No newline at end of file diff --git a/algorithms/heap/binary_heap.py b/algorithms/heap/binary_heap.py index 776e315f6..0c59cd65a 100644 --- a/algorithms/heap/binary_heap.py +++ b/algorithms/heap/binary_heap.py @@ -1,4 +1,4 @@ -r""" +""" Binary Heap. A min heap is a complete binary tree where each node is smaller than its children. The root, therefore, is the minimum element in the tree. The min heap uses an array to represent the data and operation. For example a min heap: @@ -29,39 +29,7 @@ 55 90 87 55 90 55 90 """ -from abc import ABCMeta, abstractmethod - - -class AbstractHeap(metaclass=ABCMeta): - """Abstract Class for Binary Heap.""" - - def __init__(self): - """Pass.""" - - @abstractmethod - def perc_up(self, i): - """Pass.""" - - @abstractmethod - def insert(self, val): - """Pass.""" - - @abstractmethod - def perc_down(self, i): - """Pass.""" - - @abstractmethod - def min_child(self, i): - """Pass.""" - - @abstractmethod - def remove_min(self): - """Pass.""" - - -class BinaryHeap(AbstractHeap): - """Binary Heap Class""" - +class BinaryHeap(): def __init__(self): self.current_size = 0 self.heap = [(0)] @@ -73,23 +41,22 @@ def perc_up(self, i): self.heap[i], self.heap[i//2] = self.heap[i//2], self.heap[i] i = i // 2 - def insert(self, val): - """ + """ Method insert always start by inserting the element at the bottom. It inserts rightmost spot so as to maintain the complete tree property. Then, it fixes the tree by swapping the new element with its parent, until it finds an appropriate spot for the element. It essentially perc_up the minimum element Complexity: O(logN) - """ + """ + def insert(self, val): self.heap.append(val) self.current_size = self.current_size + 1 self.perc_up(self.current_size) - """ - Method min_child returns the index of smaller of 2 children of parent at index i - """ - + """ + Method min_child returns the index of smaller of 2 children of parent at index i + """ def min_child(self, i): if 2 * i + 1 > self.current_size: # No right child return 2 * i @@ -98,12 +65,13 @@ def min_child(self, i): return 2 * i def perc_down(self, i): - while 2 * i < self.current_size: + while 2 * i <= self.current_size: min_child = self.min_child(i) if self.heap[min_child] < self.heap[i]: # Swap min child with parent self.heap[min_child], self.heap[i] = self.heap[i], self.heap[min_child] i = min_child + """ Remove Min method removes the minimum element and swap it with the last element in the heap( the bottommost, rightmost element). Then, it @@ -111,12 +79,11 @@ def perc_down(self, i): min heap property is restored Complexity: O(logN) """ - def remove_min(self): - ret = self.heap[1] # the smallest value at beginning + popped = self.heap[1] # the smallest value at beginning # Replace it by the last value self.heap[1] = self.heap[self.current_size] self.current_size = self.current_size - 1 self.heap.pop() self.perc_down(1) - return ret + return popped \ No newline at end of file diff --git a/algorithms/linkedlist/DoublyLinkedList.py b/algorithms/linkedlist/DoublyLinkedList.py new file mode 100644 index 000000000..0b03b2617 --- /dev/null +++ b/algorithms/linkedlist/DoublyLinkedList.py @@ -0,0 +1,52 @@ +class DoublyLinkedListNode(object): + def __init__(self, value): + self.value = value + self.next = None + self.prev = None + +class Doubly(): + def _add_node(self, node): + """ + Always add the new node right after head. + """ + node.prev = self.head + node.next = self.head.next + + self.head.next.prev = node + self.head.next = node + + def _remove_node(self, node): + """ + Remove an existing node from the linked list. + """ + prev = node.prev + new = node.next + + prev.next = new + new.prev = prev + + def _move_to_head(self, node): + """ + Move certain node in between to the head. + """ + self._remove_node(node) + self._add_node(node) + + def _pop_tail(self): + """ + Pop the current tail. + """ + res = self.tail.prev + self._remove_node(res) + return res + + def __init__(self, capacity): + """ + :type capacity: int + """ + self.size = 0 + self.capacity = capacity + self.head, self.tail = DoublyLinkedListNode(), DoublyLinkedListNode() + + self.head.next = self.tail + self.tail.prev = self.head \ No newline at end of file diff --git a/algorithms/maths/find_prime_factors.py b/algorithms/maths/find_prime_factors.py new file mode 100644 index 000000000..0e0d71bb3 --- /dev/null +++ b/algorithms/maths/find_prime_factors.py @@ -0,0 +1,21 @@ +# Gives the shortest prime factor for a number +# Assign N the biggest num and then call getPrimeFactorization for each num +import math +N = 1000001 +spf = [-1] * N +def shortesPrimeFactorForEachNum(): + global spf + for i in range(4, N, 1): + spf[i] = 2 + for i in range(3, math.ceil(N ** 0.5)): + if spf[i] == -1: + for j in range(i ** 2, N, i): + if spf[j] == -1: + spf[j] = i +shortesPrimeFactorForEachNum() +def getPrimeFactorization(x): + ret = list() + while (x != 1): + ret.append(spf[x]) + x = x // spf[x] + return ret \ No newline at end of file diff --git a/algorithms/maths/prime_factorization.py b/algorithms/maths/prime_factorization.py new file mode 100644 index 000000000..f1ea74cff --- /dev/null +++ b/algorithms/maths/prime_factorization.py @@ -0,0 +1,38 @@ +from math import * +N = 10000001 +# stores smallest prime factor for +# every number +spf = [0 for i in range(N)] +# Calculating SPF (Smallest Prime Factor) +# for every number till N. +# Time Complexity : O(nloglogn) +def sieve(): + spf[1] = 1 + for i in range(2, N): + # marking smallest prime factor + # for every number to be itself. + spf[i] = i + # separately marking spf for + # every even number as 2 + for i in range(4, N, 2): + spf[i] = 2 + for i in range(3, ceil(N ** 0.5)): + # checking if i is prime + if (spf[i] == i): + # marking SPF for all numbers + # divisible by i + for j in range(i * i, N, i): + # marking spf[j] if it is + # not previously marked + if (spf[j] == j): + spf[j] = i +# A O(log n) function returning prime +# factorization by dividing by smallest +# prime factor at every step +def getPrimeFactorization(x): + ret = list() + while (x != 1): + ret.append(spf[x]) + x = x // spf[x] + return ret +sieve() \ No newline at end of file diff --git a/algorithms/maths/prime_factorization_10^9.py b/algorithms/maths/prime_factorization_10^9.py new file mode 100644 index 000000000..7cca60c61 --- /dev/null +++ b/algorithms/maths/prime_factorization_10^9.py @@ -0,0 +1,22 @@ +''' Uses the Idea that all prime numbers are odd and for numbers like 9 we can just +start from 3 and continously divide to eliminate 9 ''' + +import math +def primeFactors(n): + res = [] + # Print the number of two's that divide n + while n & 1 ^ 1: + res.append(2) + n >>= 1 + # n must be odd at this point + # so a skip of 2 ( i = i + 2) can be used + for i in range(3, int(math.sqrt(n)) + 1, 2): + # while i divides n , print i and divide n + while n % i == 0: + res.append(i) + n //= i + # Condition if n is a prime + # number greater than 2 + if n > 2: + res.append(n) + return res diff --git a/algorithms/maths/primes_sieve_of_eratosthenes.py b/algorithms/maths/primes_sieve_of_eratosthenes.py index b0d1d96c5..ab6a8e3a9 100644 --- a/algorithms/maths/primes_sieve_of_eratosthenes.py +++ b/algorithms/maths/primes_sieve_of_eratosthenes.py @@ -31,14 +31,13 @@ def get_primes(n): if n <= 0: raise ValueError("'n' must be a positive integer.") # If x is even, exclude x from list (-1): - sieve_size = (n // 2 - 1) if n % 2 == 0 else (n // 2) + sieve_size = (n >> 1) if n & 1 else ((n >> 1) - 1) sieve = [True for _ in range(sieve_size)] # Sieve - primes = [] # List of Primes - if n >= 2: - primes.append(2) # 2 is prime by default + primes = [] + if n >= 2: primes.append(2) # 2 is prime by default for i in range(sieve_size): if sieve[i]: - value_at_i = i*2 + 3 + value_at_i = i << 1 + 3 primes.append(value_at_i) for j in range(i, sieve_size, value_at_i): sieve[j] = False diff --git a/algorithms/shivsQuickBucket/2dDP_Space_optimization.py b/algorithms/shivsQuickBucket/2dDP_Space_optimization.py new file mode 100644 index 000000000..b18339fa9 --- /dev/null +++ b/algorithms/shivsQuickBucket/2dDP_Space_optimization.py @@ -0,0 +1,36 @@ +from functools import cache + +# 2D DP +class Solution: + def maxProfit(self, prices, fee: int) -> int: + @cache + def recursion(prev, curri): + if not curri < len(prices): return 0 + + profit = 0 + if not prev: + profit = max(recursion(prev, curri + 1), recursion(prices[curri], curri + 1)) + elif prices[curri] > prev: + profit = max((prices[curri] - prev - fee) + recursion(0, curri + 1), recursion(prev, curri + 1)) + else: + profit = recursion(prev, curri + 1) + return profit + return recursion(0, 0) + +# Converted to 2D DP space optimized +class Solution: + def maxProfit(self, prices, fee: int) -> int: + @cache + def recursion(curri, b): + if curri == len(prices): return 0 + + if b: + buy = recursion(curri + 1, 0) - prices[curri] - fee + notBuy = recursion(curri + 1, 1) + return max(buy, notBuy) + + sell = recursion(curri + 1, 1) + prices[curri] + notSell = recursion(curri + 1, 0) + return max(sell, notSell) + + return recursion(0, 1) \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/AVL.py b/algorithms/shivsQuickBucket/AVL.py new file mode 100644 index 000000000..97398b273 --- /dev/null +++ b/algorithms/shivsQuickBucket/AVL.py @@ -0,0 +1,146 @@ +# Self balancing BST +class TreeNode: + def __init__(self, val=0): + self.val = val + self.left = None + self.right = None + self.height = 1 + + def insert(self, root, key): + # Step 1 - Perform normal BST insertion + if not root: + return TreeNode(key) + elif key < root.val: + root.left = self.insert(root.left, key) + else: + root.right = self.insert(root.right, key) + + # Step 2 - Update the height of the ancestor node + root.height = 1 + max(self.getHeight(root.left), + self.getHeight(root.right)) + + return self.rebalance(root, key) + + # Recursive function to delete a node with + # given key from subtree with given root. + # It returns root of the modified subtree. + def delete(self, root, key): + # Step 1 - Perform standard BST deletion + if not root: + return root + + elif key < root.val: + root.left = self.delete(root.left, key) + + elif key > root.val: + root.right = self.delete(root.right, key) + + else: + if root.left is None: + temp = root.right + root = None + return temp + + elif root.right is None: + temp = root.left + root = None + return temp + + # root.right none is handled above -> self.successor cannot return None + root.val = self.successor(root) + root.right = self.delete(root.right, root.val) + + # If the tree has only one node, simply return it + if root is None: + return root + + # Step 2 - Update the height of the ancestor node + root.height = 1 + max(self.getHeight(root.left), + self.getHeight(root.right)) + + return self.rebalance(root, key) + + def leftRotate(self, z): + + y = z.right + T2 = y.left + + # Perform rotation + y.left = z + z.right = T2 + + # Update heights + z.height = 1 + max(self.getHeight(z.left), + self.getHeight(z.right)) + y.height = 1 + max(self.getHeight(y.left), + self.getHeight(y.right)) + + # Return the new root + return y + + def rightRotate(self, z): + + y = z.left + T2 = y.right + + # Perform rotation + y.right = z + z.left = T2 + + # Update heights + z.height = 1 + max(self.getHeight(z.left), + self.getHeight(z.right)) + y.height = 1 + max(self.getHeight(y.left), + self.getHeight(y.right)) + + # Return the new root + return y + + def getHeight(self, root): + if not root: + return 0 + return root.height + + def getBalance(self, root): + if not root: + return 0 + return self.getHeight(root.left) - self.getHeight(root.right) + + def successor(self, root): + root = root.right + while root.left: + root = root.left + return root + + def rebalance(self, root, key): + # Step 3 - Get the balance factor + balance = self.getBalance(root) + + # Step 4 - If the node is unbalanced, then try out the 4 cases + # Case 1 - Left Left + if balance > 1 and key < root.left.val: + return self.rightRotate(root) + + # Case 2 - Right Right + if balance < -1 and key > root.right.val: + return self.leftRotate(root) + + # Case 3 - Left Right + if balance > 1 and key > root.left.val: + root.left = self.leftRotate(root.left) + return self.rightRotate(root) + + # Case 4 - Right Left + if balance < -1 and key < root.right.val: + root.right = self.rightRotate(root.right) + return self.leftRotate(root) + + return root + + def preOrder(self, root): + if not root: + return + + print("{0} ".format(root.val), end="") + self.preOrder(root.left) + self.preOrder(root.right) diff --git a/algorithms/shivsQuickBucket/BIT.py b/algorithms/shivsQuickBucket/BIT.py new file mode 100644 index 000000000..bb0ae72b6 --- /dev/null +++ b/algorithms/shivsQuickBucket/BIT.py @@ -0,0 +1,49 @@ +from math import log2 + + +class BIT: + def __init__(self, n): + self.n = n + 1 + self.tree = [0 for _ in range(self.n)] + + def update(self, idx, val=1): + idx += 1 + while idx < self.n: + self.tree[idx] += val + idx += idx&-idx + + def query(self, idx): + idx += 1 + total = 0 + while idx: + total += self.tree[idx] + idx -= idx&-idx + return total + + # finding kth smallest element Upper Bound with Time - O(logN ^ 2) + def find(self, k): + l, r = 1, self.n + while l <= r: + m = (l + r) >> 1 + if self.query(m) >= k: + res = m + r = m - 1 + else: + l = m + 1 + return res + + # Upper bound Kth smallest -- O(logN) using binary lifting + def find_BL(self, k): + pos = rs = 0 + nn = int(log2(self.n)) + + for i in reversed(range(nn + 1)): + if pos + (1 << i) < self.n and rs + self.tree[pos + (1 << i)] < k: + pos += (1 << i) + rs += self.tree[pos] + return pos + 1 # because pos will have val less than k -> lower bound + + +# Note - +# This BIT is for 1, 10 ** 5 scenarios +# For 0-indexed cases just make everything + 1 shift the system by 1 on the right \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/BrianKernighans.py b/algorithms/shivsQuickBucket/BrianKernighans.py new file mode 100644 index 000000000..fa47e1c71 --- /dev/null +++ b/algorithms/shivsQuickBucket/BrianKernighans.py @@ -0,0 +1,7 @@ +def countSetBits(n): + # Brian Kernighan’s counts set bits in O(log(n)) + count = 0 + while n: + n -= n & -n + count += 1 + return count \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/CountSetBitsTillX.py b/algorithms/shivsQuickBucket/CountSetBitsTillX.py new file mode 100644 index 000000000..ab8921d66 --- /dev/null +++ b/algorithms/shivsQuickBucket/CountSetBitsTillX.py @@ -0,0 +1,9 @@ +def check(N): + count = 0 + for i in range(1, 64): + cnt1 = 1 << (i * x) - 1 # count of 1s from 0 - 1000 on xth position + cnt01 = 1 << (i * x) # count of 0s and 1s from 0 - 1000 on xth position + d, r = divmod(N + 1, cnt01) + count += d * cnt1 + max(0, r - cnt1) + +return check(1000) # counts set bits for ith positions in binary representation \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/FloydWarshall.py b/algorithms/shivsQuickBucket/FloydWarshall.py new file mode 100644 index 000000000..fceb6e8f8 --- /dev/null +++ b/algorithms/shivsQuickBucket/FloydWarshall.py @@ -0,0 +1,17 @@ +import math +# O(V^3) +def fw(graph, V): + dist = list(map(lambda i: list(map(lambda j: j, i)), graph)) + for k in range(V): + for i in range(V): + for j in range(V): + dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]) + return dist + +INF = math.inf +graph = [[0, 5, INF, 10], + [INF, 0, 3, INF], + [INF, INF, 0, 1], + [INF, INF, INF, 0] + ] +print(fw(graph, 4)) \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/LRU_Cache.py b/algorithms/shivsQuickBucket/LRU_Cache.py new file mode 100644 index 000000000..628408df8 --- /dev/null +++ b/algorithms/shivsQuickBucket/LRU_Cache.py @@ -0,0 +1,54 @@ +class ListNode: + def __init__(self, key, val): + self.key = key + self.val = val + self.next = None + self.prev = None + +class LRUCache: + def __init__(self, capacity: int): + self.capacity = capacity + self.dic = {} + self.head = ListNode(-1, -1) + self.tail = ListNode(-1, -1) + self.head.next = self.tail + self.tail.prev = self.head + + def get(self, key: int) -> int: + if key not in self.dic: + return -1 + + node = self.dic[key] + self.remove(node) + self.add(node) + return node.val + + def put(self, key: int, value: int) -> None: + if key in self.dic: + old_node = self.dic[key] + self.remove(old_node) + + node = ListNode(key, value) + self.dic[key] = node + self.add(node) + + if len(self.dic) > self.capacity: + node_to_delete = self.head.next + self.remove(node_to_delete) + del self.dic[node_to_delete.key] + + def add(self, node): + previous_end = self.tail.prev + previous_end.next = node + node.prev = previous_end + node.next = self.tail + self.tail.prev = node + + def remove(self, node): + node.prev.next = node.next + node.next.prev = node.prev + +# Your LRUCache object will be instantiated and called as such: +# obj = LRUCache(capacity) +# param_1 = obj.get(key) +# obj.put(key,value) \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/LazySegmentTree.py b/algorithms/shivsQuickBucket/LazySegmentTree.py new file mode 100644 index 000000000..a6861bf8c --- /dev/null +++ b/algorithms/shivsQuickBucket/LazySegmentTree.py @@ -0,0 +1,52 @@ +class SEG: + def __init__(self, n, fn, query_fn): + self.n = n + self.H = 1 + while 1 << self.H < n: + self.H += 1 + + self.fn = fn + self.query_fn = query_fn + self.tree = [0] * (2 * n) + self.lazy = [0] * n + + def _apply(self, idx, val): + self.tree[idx] = self.fn(self.tree[idx], val) + if idx < self.n: # checking so we do not apply lazy to leaf nodes (our original array) + self.lazy[idx] = self.fn(self.lazy[idx], val) + + def _pull(self, idx): + while idx > 1: + idx >>= 1 + self.tree[idx] = self.fn(self.lazy[idx], self.query_fn(self.tree[idx*2], self.tree[idx*2 + 1])) + + def _push(self, idx): + for h in range(self.H, 0, -1): + y = idx >> h + if self.lazy[y]: + self._apply(y * 2, self.lazy[y]) + self._apply(y * 2+ 1, self.lazy[y]) + self.lazy[y] = 0 + + def update(self, l, r, val): + l, r = l + self.n, r + self.n + L, R = l, r + while l <= r: + if l & 1: + self._apply(l, val) + if not r & 1: + self._apply(r, val) + l, r = (l + 1) >> 1, (r - 1) >> 1 + self._pull(L); self._pull(R) + + def query(self, l, r): + l, r = l + self.n, r + self.n + self._push(l); self._push(r) + res = None + while l <= r: + if l & 1: + res = self.tree[l] if res is None else self.query_fn(res, self.tree[l]) + if not r & 1: + res = self.tree[r] if res is None else self.query_fn(res, self.tree[r]) + l, r = (l + 1) >> 1, (r - 1) >> 1 + return res \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/LongestIncreasingSubsequence.py b/algorithms/shivsQuickBucket/LongestIncreasingSubsequence.py new file mode 100644 index 000000000..ba916eb89 --- /dev/null +++ b/algorithms/shivsQuickBucket/LongestIncreasingSubsequence.py @@ -0,0 +1,24 @@ +# 300. Longest Increasing Subsequence + +''' +Logic is: + If the curr element is greater the last element added to the subsequence: + append it to increase the size of the subsequence + else + replace a greater element in the subsequence with this smaller element + which will open doors for us to add another element in future + # why else ? -> + we would want to minimize our elements in the subsequence as to + increase our chances of adding a newer element in future + However this will not give the correct subsequence because of else statement +''' +import bisect + + +def lengthOfLIS(nums) -> int: + sub = [nums[0]] + for num in nums[1:]: + id = bisect.bisect_left(sub, num) # bisect_left or bisect_right always give the first greater element if the num is not present in sub + if id == len(sub): sub.append(num) + else: sub[id] = num # increase our chances of adding a newer element in future + return len(sub) \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/Mo's_Algorithm.py b/algorithms/shivsQuickBucket/Mo's_Algorithm.py new file mode 100644 index 000000000..c04131145 --- /dev/null +++ b/algorithms/shivsQuickBucket/Mo's_Algorithm.py @@ -0,0 +1,44 @@ +import math + +# Function that accepts array and list of queries and print sum of each query +def queryResults(arr,Q): + + #Q.sort(): # Sort by L + #sort all queries so that all queries in the increasing order of R values . + Q.sort(key=lambda x: x[1]) + + # Initialize current L, current R and current sum + currL,currR,currSum = 0,0,0 + + # Traverse through all queries + for i in range(len(Q)): + L,R = Q[i] # L and R values of current range + + # Remove extra elements from previous range + # if previous range is [0, 3] and current + # range is [2, 5], then a[0] and a[1] are subtracted + while currLL: + currSum+=arr[currL-1] + currL-=1 + while currR<=R: + currSum+=arr[currR] + currR+=1 + + # Remove elements of previous range + # when previous range is [0, 10] and current range + # is [3, 8], then a[9] and a[10] are subtracted + while currR>R+1: + currSum-=arr[currR-1] + currR-=1 + + # Print the sum of current range + print("Sum of",Q[i],"is",currSum) + +arr = [1, 1, 2, 1, 3, 4, 5, 2, 8] +Q = [[0, 4], [1, 3], [2, 4]] +queryResults(arr,Q) \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/RecursiveSegmentTree.py b/algorithms/shivsQuickBucket/RecursiveSegmentTree.py new file mode 100644 index 000000000..e232cf9e7 --- /dev/null +++ b/algorithms/shivsQuickBucket/RecursiveSegmentTree.py @@ -0,0 +1,30 @@ +from collections import Counter + + +class SEG: + def __init__(self): + self.tree = Counter() + self.lazy = Counter() + + def update(self, s, e, l=0, r=10**9, idx=1): + if s > r or e < l: return + + if s <= l <= r <= e: + self.tree[idx] += 1 + self.lazy[idx] += 1 + else: + m = (l + r) >> 1 + self.update(s, e, l, m, idx * 2) + self.update(s, e, m + 1, r, idx * 2 + 1) + self.tree[idx] = self.lazy[idx] + self.fn(self.tree[2 * idx], self.tree[2 * idx + 1]) + + def query(self, s, e, l=0, r=10**9, idx=1): + if s > r or e < l: return + + if s <= l <= r <= e: + return self.tree[idx] + else: + m = (l + r) >> 1 + A = self.query(s, e, l, m, idx * 2) + B = self.query(s, e, m + 1, r, idx * 2 + 1) + return self.fn(A, B) diff --git a/algorithms/shivsQuickBucket/RollingHash.py b/algorithms/shivsQuickBucket/RollingHash.py new file mode 100644 index 000000000..f5640e164 --- /dev/null +++ b/algorithms/shivsQuickBucket/RollingHash.py @@ -0,0 +1,33 @@ +""" + s: string + W: window size + Calculates the rolling hash values of all substrings of length window_size in string s. + Uses the polynomial rolling hash algorithm with base and mod as constants. +""" +base = 1000000007 +mod = 344555666677777 + +def get_hash(s, W): + hash = 0 + for i in range(W): + hash = (hash * base + ord(s[i])) % mod + return hash + +def rolling_hash(s, W): + n = len(s) + power = [1] * (n + 1) + for i in range(1, n + 1): + power[i] = (power[i - 1] * base) % mod + + res = [0] * (n - W + 1) + + curr = get_hash(s, W) + + # roll for haystack + res[0] = curr + for i in range(1, n - W + 1): + curr = (curr - power[W - 1] * ord(s[i - 1])) % mod + curr = (curr * base + ord(s[i + W - 1])) % mod + res[i] = curr + + return res \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/RollingHashQuery.py b/algorithms/shivsQuickBucket/RollingHashQuery.py new file mode 100644 index 000000000..84fa95656 --- /dev/null +++ b/algorithms/shivsQuickBucket/RollingHashQuery.py @@ -0,0 +1,19 @@ +class prefixHash: + def __init__(self ,s): + self.mod = mod = 344555666677777 + self.basePower = basePower = 10 ** 7 + 7 + #fermat's little theorem since basePower and mod are co-prime + self.invbase = invbase = pow(basePower, mod - 2, mod) + self.prefix = prefix = [0] # r - l in query so for 1st prefix -> l needs to be 0 right? + self.invprefix = invprefix = [1] + hash, power, invpower = 0, 1, 1 + + for x in s: + hash = (hash + x * power) % mod + power = (power * basePower) % mod + invpower = (invpower * invbase) % mod + prefix.append(hash) + invprefix.append(invpower) + + def query(self, l, r): + return ((self.prefix[r] - self.prefix[l]) * self.invprefix[l]) % self.mod diff --git a/algorithms/shivsQuickBucket/SegmentTree.py b/algorithms/shivsQuickBucket/SegmentTree.py new file mode 100644 index 000000000..a004c9be4 --- /dev/null +++ b/algorithms/shivsQuickBucket/SegmentTree.py @@ -0,0 +1,53 @@ +""" +SegmentTree creates a segment tree with a given array and a "commutative" function, +this non-recursive version uses less memory than the recursive version and include: +1. range queries in log(N) time +2. update an element in log(N) time +the function should be commutative and takes 2 values and returns the same type value + +Examples - +mytree = SegmentTree([2, 4, 5, 3, 4],max) +print(mytree.query(2, 4)) +mytree.update(3, 6) +print(mytree.query(0, 3)) ... + +mytree = SegmentTree([4, 5, 2, 3, 4, 43, 3], lambda a, b: a + b) +print(mytree.query(0, 6)) +mytree.update(2, -10) +print(mytree.query(0, 6)) ... + +mytree = SegmentTree([(1, 2), (4, 6), (4, 5)], lambda a, b: (a[0] + b[0], a[1] + b[1])) +print(mytree.query(0, 2)) +mytree.update(2, (-1, 2)) +print(mytree.query(0, 2)) ... +""" + + +class SEG: + def __init__(self, arr, function): + self.n = len(arr) + self.tree = [0 for _ in range(self.n)] + arr + self.fn = function + self.build_tree(self.n) + + def build_tree(self, n): + for i in range(n - 1, 0, -1): + self.tree[i] = self.fn(self.tree[2 * i], self.tree[2 * i + 1]) + + def update(self, idx, value): + idx += self.n + self.tree[idx] = value + while idx > 1: + idx >>= 1 + self.tree[idx] = self.fn(self.tree[2 * idx], self.tree[2 * idx + 1]) + + def query(self, l, r): + l, r = l + self.n, r + self.n + res = None + while l <= r: + if l & 1: + res = self.tree[l] if res is None else self.fn(res, self.tree[l]) + if not (r & 1): + res = self.tree[r] if res is None else self.fn(res, self.tree[r]) + l, r = (l + 1) >> 1, (r - 1) >> 1 + return res diff --git a/algorithms/shivsQuickBucket/SieveOfEratosthenes.py b/algorithms/shivsQuickBucket/SieveOfEratosthenes.py new file mode 100644 index 000000000..4497dfb44 --- /dev/null +++ b/algorithms/shivsQuickBucket/SieveOfEratosthenes.py @@ -0,0 +1,15 @@ +def sieve(n = 500001): + prime = [True for _ in range(n + 1)] + + p = 2 + while p * p <= n: + if prime[p]: + for i in range(p * p, n + 1, p): + prime[i] = False + p += 1 + + primes = [] + for num in range(2, n + 1): + if prime[num]: primes.append(num) + + return primes \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/binary_heap.py b/algorithms/shivsQuickBucket/binary_heap.py new file mode 100644 index 000000000..c38f3b36a --- /dev/null +++ b/algorithms/shivsQuickBucket/binary_heap.py @@ -0,0 +1,102 @@ +class Heap(): + def __init__(self): + self.size = 0 + self.heap = [0] + + def up(self, i): + while i >> 1 > 0: + if self.heap[i] < self.heap[i >> 1]: + # Swap value of child with value of its parent + self.heap[i], self.heap[i >> 1] = self.heap[i >> 1], self.heap[i] + i >>= 1 + + def insert(self, val): + self.heap.append(val) + self.size = self.size + 1 + self.up(self.size) + + def min_child(self, i): + if 2 * i + 1 > self.size: + # No right child + return 2 * i + if self.heap[2 * i] > self.heap[2 * i + 1]: + return 2 * i + 1 + return 2 * i + + def down(self, i): + while 2 * i <= self.size: + min_child = self.min_child(i) + if self.heap[min_child] < self.heap[i]: + # Swap min child with parent + self.heap[min_child], self.heap[i] = self.heap[i], self.heap[min_child] + i = min_child + + def pop(self): + popped = self.heap[1] + # the smallest value at beginning + # Replace it by the last value + self.heap[1] = self.heap[self.size] + self.size = self.size - 1 + self.heap.pop() + self.down(1) + return popped + + + + + + + + + + + +""" +Binary Heap. A min heap is a complete binary tree where each node is smaller than +its children. The root, therefore, is the minimum element in the tree. The min +heap uses an array to represent the data and operation. For example a min heap: + + 4 + / \ + 50 7 + / \ / +55 90 87 + +Heap [0, 4, 50, 7, 55, 90, 87] + +Method in class: insert, remove_min +For example insert(2) in a min heap: + + 4 4 2 + / \ / \ / \ + 50 7 --> 50 2 --> 50 4 + / \ / \ / \ / \ / \ / \ +55 90 87 2 55 90 87 7 55 90 87 7 + +For example remove_min() in a min heap: + + 4 87 7 + / \ / \ / \ + 50 7 --> 50 7 --> 50 87 + / \ / / \ / \ +55 90 87 55 90 55 90 + +""" +""" + Method insert always start by inserting the element at the bottom. + It inserts rightmost spot so as to maintain the complete tree property. + Then, it fixes the tree by swapping the new element with its parent, + until it finds an appropriate spot for the element. It essentially + ups the minimum element + Complexity: O(logN) +""" +""" + Remove Min method removes the minimum element and swap it with the last + element in the heap( the bottommost, rightmost element). Then, it + downs this element, swapping it with one of its children until the + min heap property is restored + Complexity: O(logN) +""" +""" + Method min_child returns the index of smaller of 2 children of parent at index i +""" diff --git a/algorithms/shivsQuickBucket/binary_lifting.py b/algorithms/shivsQuickBucket/binary_lifting.py new file mode 100644 index 000000000..a362f8e47 --- /dev/null +++ b/algorithms/shivsQuickBucket/binary_lifting.py @@ -0,0 +1,22 @@ +import math +class BL: + def __init__(self, n, parent): + # up stores 2^ith parent + self.height = 1 + int(math.log2(n)) + self.up = [[-1] * (self.height) for _ in range(n)] + # update parents first and then go for higher order parents preprocessing + for j in range(self.height): + for i in range(n): + if j == 0: + self.up[i][0] = parent[i] + continue + elif self.up[i][j - 1] != -1: + self.up[i][j] = self.up[self.up[i][j - 1]][j - 1] + + # Function to return the Kth ancestor of V + def getKthAncestor(self, node: int, k: int) -> int: + while k > 0 and node != -1: + i = int(math.log2(k)) # lower power of 2 + node = self.up[node][i] + k -= (1 << i) + return node \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/bitmaskDP.py b/algorithms/shivsQuickBucket/bitmaskDP.py new file mode 100644 index 000000000..c52083f18 --- /dev/null +++ b/algorithms/shivsQuickBucket/bitmaskDP.py @@ -0,0 +1,29 @@ +# 473. Matchsticks to Square + +class Solution: + def makesquare(self, matches) -> bool: + n, side = len(matches), sum(matches) // 4 # Precomputing the required side for a square + + if sum(matches) % 4 or max(matches) > side: return False # matches array is invalid + + + self.cache = {} # will be used for memoization + + # this helper function has the remaining side ('s') to be formed, already made number of sides ('k') and + # the mask stores the number of values from matches already taken (sort of like visited) + + def helper(k, s, mask): + if (k, s, mask) in self.cache: return self.cache[(k, s, mask)] # if we have seen these values lets not compute them again + if k == 4: return True # if we reach here we have made a square so return true + + if not s: # if we exhaust a side there needs to be a search initiated for the next side + self.cache[(k, s, mask)] = helper(k + 1, side, mask) # store the new result in cache + return self.cache[(k, s, mask)] + + for i in range(n): + if mask & (1 << i) or matches[i] > s: continue # if we have already taken a number or that number is not suitable skip it + self.cache[(k, s, mask)] = helper(k, s - matches[i], mask ^ (1 << i)) + if self.cache[(k, s, mask)]: return True # if in future we find this to make a square then return true from here + + return False + return helper(0, side, 0) \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/dfs.py b/algorithms/shivsQuickBucket/dfs.py new file mode 100644 index 000000000..bcf0e6108 --- /dev/null +++ b/algorithms/shivsQuickBucket/dfs.py @@ -0,0 +1,17 @@ +grid = [] +m, n = len(grid), len(grid[0]) +directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] +def isValid(a, b): + return 0 <= a < m and 0 <= b < n and grid[a][b] == 1 +seen = set() +def dfs(q): + res = [] + while q: + ri, cj = q.pop() + if (ri, cj) in seen: continue + res.append((ri, cj)) + seen.add((ri, cj)) + for i, j in directions: # we can travel neighbours here same thing + nr, nc = ri + i, cj + j + if isValid(nr, nc): q.append((nr, nc)) + return res \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/disset.py b/algorithms/shivsQuickBucket/disset.py new file mode 100644 index 000000000..e5728906f --- /dev/null +++ b/algorithms/shivsQuickBucket/disset.py @@ -0,0 +1,23 @@ +class DSU: + def __init__(self, n): + self.root = list(range(n)) + self.rank = [1] * n + self.count = 0 + + def find(self, x): + if self.root[x] == x: return x + self.root[x] = self.find(self.root[x]) + return self.root[x] + + def union(self, x, y): + rootx, rooty = self.find(x), self.find(y) + if rootx != rooty: + if self.rank[rootx] >= self.rank[rooty]: + self.root[rooty] = rootx + self.rank[rootx] += self.rank[rooty] + else: + self.root[rootx] = rooty + self.rank[rooty] += self.rank[rootx] + self.count += 1 + return True + else: return False \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/easy_trie.py b/algorithms/shivsQuickBucket/easy_trie.py new file mode 100644 index 000000000..fd6dda9f1 --- /dev/null +++ b/algorithms/shivsQuickBucket/easy_trie.py @@ -0,0 +1,52 @@ +from collections import deque +''' +You may also try to make a treenode in cases where you need to store metadata +for a particular children of the node like this - +class TreeNode: +def __init__(self): + self.children = {} + self.metadata = String | Number | Boolean +''' +# Trie Template for adding words checking if a prefix is valid in trie and returning list of words with that prefix + +trie = {} +def add_word(curr, word): + for c in word: + if c not in curr: curr[c] = {} + curr = curr[c] + curr['#'] = word + +def has_word(curr, word): + for c in word: + if c in curr: curr = curr[c] + else: return False + return '#' in curr + +def has_prefix(curr, pre): + for c in pre: + if c in curr: curr = curr[c] + else: return False + return curr + +def get_words_using_prefix(head, pre): + curr = has_prefix(head, pre) + if not curr: return [] + + #BFS + words = [] + q = deque([(curr, pre)]) + while q: + size = len(q) + for _ in range(size): + curr, pre = q.popleft() + if '#' in curr: words.append(pre) + for nei in curr: + q.append((curr[nei], pre + nei)) + return words + +words = ['abcd', 'defg'] +prefixes = ['a', 'def'] +for word in words: + add_word(trie, word) +for pre in prefixes: + print(get_words_using_prefix(trie, pre)) \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/euler_tour.py b/algorithms/shivsQuickBucket/euler_tour.py new file mode 100644 index 000000000..b34905827 --- /dev/null +++ b/algorithms/shivsQuickBucket/euler_tour.py @@ -0,0 +1,32 @@ +from collections import defaultdict as dd + +def eulerTour(u, index): + seen[u] = True + Euler[index] = u + index += 1 + for nei in adj[u]: + if not seen[u]: + index = eulerTour(nei, index) + Euler[index] = u + index += 1 + return index +n = 10 # No. of nodes +seen = dd(bool) +adj = dd(list) +Euler = [0] * (2 * (10 ** 5)) +eulerTour(1, 0) +for i in range(2 * n - 1): + print(Euler[i], end = " ") + +# Another way for this - + +def Euler(node): + tin[node] = timer + timer += 1 + path[tin[node]] = node + for child in adj[node]: + Euler(child) + tout[node] = timer - 1 + +tin, tout = [0] * (n + 1), [0] * (n + 1) +path = [0] * (n + 1) diff --git a/algorithms/shivsQuickBucket/get_palindromes.py b/algorithms/shivsQuickBucket/get_palindromes.py new file mode 100644 index 000000000..21d2529b2 --- /dev/null +++ b/algorithms/shivsQuickBucket/get_palindromes.py @@ -0,0 +1,8 @@ +'''Generate palindromes from 1 to 10 ** 9''' + +l = [] +for i in range(1, 100000): + s = str(i) + l.append(int(s + s[::-1])) + l.append(int(s + s[::-1][1:])) +l.sort() \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/kmp.py b/algorithms/shivsQuickBucket/kmp.py new file mode 100644 index 000000000..9cc65228f --- /dev/null +++ b/algorithms/shivsQuickBucket/kmp.py @@ -0,0 +1,32 @@ +# if we find out where to jump to for matching a pattern in a big text we can reduce computations +# as pattern can have repeated values -> only works if this is true +# that is why we create pi table to store the jump values +def kmp(text, pattern): + m, n = len(text), len(pattern) + pi = [0] * n + + j = 0 + # making pi table -> finding the longest prefix that is also a suffix + # Examples - P = ABCDABD π = (0, 0, 0, 0, 1, 2, 0) + # pi table means -> agar yahan khtm hui matching to starting se start karo if 0 is there otherwise start + # start with the position givne in pi table kyunki wahan tk ka matching already hogya tha + for i in range(1, n): + while j and pattern[i] != pattern[j]: + j = pi[j - 1] + if pattern[i] == pattern[j]: + j += 1 + pi[i] = j + + # finding pattern in text + j = 0 + res = [] + for i in range(m): + while j and text[i] != pattern[j]: + j = pi[j - 1] + if text[i] == pattern[j]: + j += 1 + if j == n: + res.append(i - n + 1) + j = pi[j - 1] + + return res \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/krushkals.py b/algorithms/shivsQuickBucket/krushkals.py new file mode 100644 index 000000000..b4190698e --- /dev/null +++ b/algorithms/shivsQuickBucket/krushkals.py @@ -0,0 +1,49 @@ +# 1135. Connecting Cities With Minimum Cost +# MST + +from collections import defaultdict as dd +import heapq +# Krushkal's solution +class Disset: #Disjoint Set Data Structure + def __init__(self, n): + self.root = [i for i in range(n)] + self.rank = [0] * n + self.cost = 0 + + def find(self, x): + if self.root[x] == x: + return x + self.root[x] = self.find(self.root[x]) + return self.root[x] + + def union(self, x, y, cost): + rootx = self.find(x) + rooty = self.find(y) + if rootx == rooty: return False + else: + if self.rank[rootx] > self.rank[rooty]: + self.root[rooty] = rootx + self.rank[rootx] += 1 + else: + self.root[rootx] = rooty + self.rank[rooty] += 1 + self.cost += cost + return True + +class Solution: + def minimumCost(self, n, connections) -> int: + if len(connections) < n - 1: return -1 + + # Make Disjoint sets DS + disset = Disset(n + 1) + + # Sort the edges based on cost + connections.sort(key = lambda connection: connection[2], reverse = True) + + # Keep choosing edges till we get to N - 1 edges + edges = 0 + while connections and edges < n - 1: + node1, node2, cost = connections.pop() + if disset.union(node1, node2, cost): edges += 1 + + return disset.cost if edges == n - 1 else -1 \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/lowest_common_ancestor.py b/algorithms/shivsQuickBucket/lowest_common_ancestor.py new file mode 100644 index 000000000..46edf4106 --- /dev/null +++ b/algorithms/shivsQuickBucket/lowest_common_ancestor.py @@ -0,0 +1,32 @@ +""" +Given a binary tree, find the lowest common ancestor +(LCA) of two given nodes in the tree. + +According to the definition of LCA on Wikipedia: + “The lowest common ancestor is defined between two nodes + v and w as the lowest node in T that has both v and w as + descendants + (where we allow a node to be a descendant of itself).” + + _______3______ + / \ + ___5__ ___1__ + / \ / \ + 6 _2 0 8 + / \ + 7 4 +For example, the lowest common ancestor (LCA) of nodes 5 and 1 is 3. +Another example is LCA of nodes 5 and 4 is 5, +since a node can be a descendant of itself according to the LCA definition. +""" + + +def lca(root, p, q): + if root is None or root is p or root is q: return root + + left = lca(root.left, p, q) + right = lca(root.right, p, q) + + if left is not None and right is not None: return root + + return left if left else right diff --git a/algorithms/shivsQuickBucket/manacher.py b/algorithms/shivsQuickBucket/manacher.py new file mode 100644 index 000000000..23c9809c5 --- /dev/null +++ b/algorithms/shivsQuickBucket/manacher.py @@ -0,0 +1,24 @@ +''' Find longest palindrome substring in O(N) time ''' + +def manacher(s): + news = '#'.join('&{}|'.format(s)) + + c = r = 0 + n = len(news) + P = [0] * n + + for i in range(1, n - 1): + if i < r: # update P[i] from it's mirror seen before centre + P[i] = min(r - i, P[2 * c - i]) + while news[i + P[i] + 1] == news[i - P[i] - 1]: + P[i] += 1 + if i + P[i] > r: # if we have come after r update r for future Is + c, r = i, i + P[i] + + resL = resid = 0 + for id, val in enumerate(P): + if val > resL: + resL = val + resid = id + + return resL, s[(resid - resL) >> 1: (resid + resL) >> 1] \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/mergeSort.py b/algorithms/shivsQuickBucket/mergeSort.py new file mode 100644 index 000000000..ab5e09f85 --- /dev/null +++ b/algorithms/shivsQuickBucket/mergeSort.py @@ -0,0 +1,34 @@ +def merge(nums, l, m, r): + L, R = nums[l: m + 1], nums[m + 1: r + 1] + + i = j = 0 + k = l + while i < len(L) and j < len(R): + if L[i] < R[j]: + nums[k] = L[i] + i += 1 + else: + nums[k] = R[j] + j += 1 + k += 1 + while i < len(L): + nums[k] = L[i] + i += 1 + k += 1 + while j < len(R): + nums[k] = R[j] + j += 1 + k += 1 + + +def mergeSort(nums, l, r): + if l < r: + m = (l + r) >> 1 + mergeSort(nums, l, m) + mergeSort(nums, m + 1, r) + merge(nums, l, m, r) + + +nums = [5, 4, 3, 2, 1] +mergeSort(nums, 0, len(nums) - 1) +# Worst case - (NlogN) diff --git a/algorithms/shivsQuickBucket/ncr.py b/algorithms/shivsQuickBucket/ncr.py new file mode 100644 index 000000000..091b22adc --- /dev/null +++ b/algorithms/shivsQuickBucket/ncr.py @@ -0,0 +1,27 @@ +# uses modular inversion +def ncr(n, r): + if (r > n): return 0 + + mod = 1000000007 + inv = [0 for i in range(r + 1)] + inv[0] = 1 + if(r+1>=2): inv[1] = 1 + + for i in range(2, r + 1): + inv[i] = mod - (mod // i) * inv[mod % i] % mod + + ans = 1 + for i in range(2, r + 1): + ans = ((ans % mod) * (inv[i] % mod)) % mod + + for i in range(n, n - r, -1): + ans = ((ans % mod) * (i % mod)) % mod + + return ans + +def ncr(n, r): + sum = 1 + for i in range(1, r + 1): + sum *= ((n - r + i) / i) + return int(sum) +'''Refer geeks for geeks binomial coefficient''' \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/prime_factorization_10^9.py b/algorithms/shivsQuickBucket/prime_factorization_10^9.py new file mode 100644 index 000000000..7cca60c61 --- /dev/null +++ b/algorithms/shivsQuickBucket/prime_factorization_10^9.py @@ -0,0 +1,22 @@ +''' Uses the Idea that all prime numbers are odd and for numbers like 9 we can just +start from 3 and continously divide to eliminate 9 ''' + +import math +def primeFactors(n): + res = [] + # Print the number of two's that divide n + while n & 1 ^ 1: + res.append(2) + n >>= 1 + # n must be odd at this point + # so a skip of 2 ( i = i + 2) can be used + for i in range(3, int(math.sqrt(n)) + 1, 2): + # while i divides n , print i and divide n + while n % i == 0: + res.append(i) + n //= i + # Condition if n is a prime + # number greater than 2 + if n > 2: + res.append(n) + return res diff --git a/algorithms/shivsQuickBucket/prims.py b/algorithms/shivsQuickBucket/prims.py new file mode 100644 index 000000000..06b84a060 --- /dev/null +++ b/algorithms/shivsQuickBucket/prims.py @@ -0,0 +1,29 @@ +# 1135. Connecting Cities With Minimum Cost + +from collections import defaultdict as dd +import heapq +# Prims solution +class Solution: + def minimumCost(self, n, connections) -> int: + if len(connections) < n - 1: return -1 + visited = set() + + # make neighbours dictionary + neighbours = dd(list) + for p1, p2, cost in connections: + neighbours[p1].append((cost, p2)) + neighbours[p2].append((cost, p1)) + + res = 0 + + # start with the heap operations + heap = [(0, connections[0][0])] + while heap and len(visited) < n: + cost, node = heapq.heappop(heap) + # this pop means this node will now be connected + if node not in visited: + res += cost + visited.add(node) + for cost, nei in neighbours[node]: + heapq.heappush(heap, (cost, nei)) + return res if len(visited) == n else -1 \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/quickDijkstra's.py b/algorithms/shivsQuickBucket/quickDijkstra's.py new file mode 100644 index 000000000..60ce49ce1 --- /dev/null +++ b/algorithms/shivsQuickBucket/quickDijkstra's.py @@ -0,0 +1,24 @@ +from heapq import heappop, heappush +from math import inf + + +class Dijkstra(): + def __init__(self): + return + + @staticmethod + def get_shortest_path(adj, src): + n = len(adj) + min_d = [inf] * n + heap = [(0, src)] + min_d[src] = 0 + + while heap: + cost, node = heappop(heap) + if cost > min_d[node]: continue + for new_node, new_cost in adj[node]: + total_cost = cost + new_cost + if total_cost < min_d[new_node]: + min_d[new_node] = total_cost + heappush(heap, (total_cost, new_node)) + return min_d \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/quicksort.py b/algorithms/shivsQuickBucket/quicksort.py new file mode 100644 index 000000000..a13284f1e --- /dev/null +++ b/algorithms/shivsQuickBucket/quicksort.py @@ -0,0 +1,23 @@ +def partition(nums, l, r): + pivot = nums[r] + i = l - 1 + + for j in range(l, r): + if nums[j] <= pivot: + nums[i + 1], nums[j] = nums[j], nums[i + 1] + i += 1 + + nums[i + 1], nums[r] = nums[r], nums[i + 1] + return i + 1 + + +def quick(nums, l, r): + if l < r: + pi = partition(nums, l, r) + quick(nums, l, pi - 1) + quick(nums, pi + 1, r) + + +nums = [5, 4, 3, 2, 1] +quick(nums, 0, len(nums) - 1) +# Worst case - (N^2) diff --git a/algorithms/shivsQuickBucket/sortStringNums.py b/algorithms/shivsQuickBucket/sortStringNums.py new file mode 100644 index 000000000..77bc475b4 --- /dev/null +++ b/algorithms/shivsQuickBucket/sortStringNums.py @@ -0,0 +1,24 @@ +''' +Comparison Functions - functools.cmp_to_key +For example, a balance scale compares two samples giving a relative ordering: lighter, equal, or heavier. +Likewise, a comparison function such as cmp(a, b) will return a negative value for less-than, zero if the inputs are equal, or a positive value for greater-than. +usage - +sorted(words, key=cmp_to_key(strcoll)) # locale-aware sort order + +Q - https://www.geeksforgeeks.org/problems/largest-number-formed-from-an-array1117/1 +''' + +import functools +class Solution: + def printLargest(self, n, arr): + sarr = [str(num) for num in arr] + + def checkk(x, y): + if x + y > y + x: + return -1 + elif x + y < y + x: + return 1 + else: + return 0 + + return ''.join(sorted(sarr, key=functools.cmp_to_key(checkk))) diff --git a/algorithms/shivsQuickBucket/sweepline.py b/algorithms/shivsQuickBucket/sweepline.py new file mode 100644 index 000000000..dc546c2b6 --- /dev/null +++ b/algorithms/shivsQuickBucket/sweepline.py @@ -0,0 +1,13 @@ +# 1109. Corporate Flight Bookings + +class Solution: + # Sweep Line solution + def corpFlightBookings(self, bookings, n: int): + res = [0] * (n + 1) + for i, j, k in bookings: + res[i - 1] += k + res[j] -= k + + for i in range(1, n): + res[i] += res[i - 1] + return res[:n] \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/tarjansAlgo.py b/algorithms/shivsQuickBucket/tarjansAlgo.py new file mode 100644 index 000000000..7186d3b10 --- /dev/null +++ b/algorithms/shivsQuickBucket/tarjansAlgo.py @@ -0,0 +1,38 @@ +# 1192. Critical Connections in a Network +# Tarjan's Algorithm + +# tin -> Time of insertion +# low -> lowest time of insertion that can be reached + +from collections import defaultdict as dd + + +class Solution: + def criticalConnections(self, n: int, connections): + graph = dd(list) + for u, v in connections: + graph[u].append(v) + graph[v].append(u) + visited = set([0]) + tin = [0] * n + low = [0] * n + res = [] + + # DFS + def recursion(node, parent, timer): + tin[node] = low[node] = timer + timer += 1 + for nei in graph[node]: + if nei == parent: + continue + if nei not in visited: + visited.add(nei) + recursion(nei, node, timer + 1) + low[node] = min(low[node], low[nei]) + if low[nei] > tin[node]: + res.append([node, nei]) + else: + low[node] = min(low[node], low[nei]) + + recursion(0, 0, 1) + return res diff --git a/algorithms/shivsQuickBucket/trial.py b/algorithms/shivsQuickBucket/trial.py new file mode 100644 index 000000000..926d7042d --- /dev/null +++ b/algorithms/shivsQuickBucket/trial.py @@ -0,0 +1,22 @@ + +def manacher(s): + news = '#'.join('&{}|'.format(s)) + n = len(news) + P = [0] * n + r = c = 0 + + for i in range(1, n - 1): + if i < r: + P[i] = min(r - i, P[2 * c - 1]) + while news[i - P[i] - 1] == news[i + P[i] + 1]: + P[i] += 1 + if i + P[i] > r: + c, r = i, i + P[i] + + resL = resid = 0 + for id, val in enumerate(P): + if val > resL: + resL = val + resid = id + + return resL, s[(resid - resL) >> 1: (resid + resL) >> 1] \ No newline at end of file diff --git a/algorithms/shivsQuickBucket/zfunction.py b/algorithms/shivsQuickBucket/zfunction.py new file mode 100644 index 000000000..83b742767 --- /dev/null +++ b/algorithms/shivsQuickBucket/zfunction.py @@ -0,0 +1,13 @@ +def zfunc(s): + N = len(s) + z = [0 for x in range(N)] + l, r = 0,0 + for i in range(1, N): + if i <= r: + z[i] = min(r - i +1, z[i-l]) + while i + z[i] < N and s[z[i]] == s[i + z[i]]: + z[i] += 1 + if i + z[i] - 1 > r: + l = i + r = i + z[i] - 1 + return z \ No newline at end of file diff --git a/algorithms/backtrack/anagram.py b/algorithms/strings/anagram.py similarity index 100% rename from algorithms/backtrack/anagram.py rename to algorithms/strings/anagram.py diff --git a/algorithms/strings/knuth_morris_pratt.py b/algorithms/strings/knuth_morris_pratt.py index e1953a218..a77fd042d 100644 --- a/algorithms/strings/knuth_morris_pratt.py +++ b/algorithms/strings/knuth_morris_pratt.py @@ -23,7 +23,8 @@ def knuth_morris_pratt(text : Sequence, pattern : Sequence) -> List[int]: pi = [0 for i in range(m)] i = 0 j = 0 - # making pi table + # making pi table -> finding the longest prefix that is also a suffix + # Examples - P = ABCDABD π = (0, 0, 0, 0, 1, 2, 0) for i in range(1, m): while j and pattern[i] != pattern[j]: j = pi[j - 1] diff --git a/algorithms/strings/rabin_karp.py b/algorithms/strings/rabin_karp.py index d900a7380..8ba2e0ba2 100644 --- a/algorithms/strings/rabin_karp.py +++ b/algorithms/strings/rabin_karp.py @@ -1,28 +1,23 @@ -# Following program is the python implementation of # Rabin Karp Algorithm - +from string import ascii_lowercase class RollingHash: def __init__(self, text, size_word): self.text = text self.hash = 0 self.size_word = size_word + self.mapping = dict(zip(ascii_lowercase, list(range(1, 27)))) for i in range(0, size_word): - #ord maps the character to a number - #subtract out the ASCII value of "a" to start the indexing at zero - self.hash += (ord(self.text[i]) - ord("a")+1)*(26**(size_word - i -1)) + self.hash += self.mapping[text[i]]*(26**(size_word - i -1)) - #start index of current window self.window_start = 0 - #end of index window self.window_end = size_word - def move_window(self): + def roll_window(self): if self.window_end <= len(self.text) - 1: - #remove left letter from hash value - self.hash -= (ord(self.text[self.window_start]) - ord("a")+1)*26**(self.size_word-1) + self.hash -= self.mapping[self.text[self.window_start]]*26**(self.size_word-1) self.hash *= 26 - self.hash += ord(self.text[self.window_end])- ord("a")+1 + self.hash += self.mapping[self.text[self.window_end]] self.window_start += 1 self.window_end += 1 @@ -37,12 +32,11 @@ def rabin_karp(word, text): rolling_hash = RollingHash(text, len(word)) word_hash = RollingHash(word, len(word)) - #word_hash.move_window() for i in range(len(text) - len(word) + 1): if rolling_hash.hash == word_hash.hash: if rolling_hash.window_text() == word: return i - rolling_hash.move_window() + rolling_hash.roll_window() return None diff --git a/algorithms/tree/fenwick_tree/fenwick_copy.py b/algorithms/tree/fenwick_tree/fenwick_copy.py new file mode 100644 index 000000000..95fdffc2b --- /dev/null +++ b/algorithms/tree/fenwick_tree/fenwick_copy.py @@ -0,0 +1,42 @@ +""" +Fenwick Tree / Binary Indexed Tree + +Consider we have an array arr[0 . . . n-1]. We would like to +1. Compute the sum of the first i elements. +2. Modify the value of a specified element of the array arr[i] = x where 0 <= i <= n-1. + +A simple solution is to run a loop from 0 to i-1 and calculate the sum of the elements. To update a value, simply do arr[i] = x. +The first operation takes O(n) time and the second operation takes O(1) time. +Another simple solution is to create an extra array and store the sum of the first i-th elements at the i-th index in this new array. +The sum of a given range can now be calculated in O(1) time, but the update operation takes O(n) time now. +This works well if there are a large number of query operations but a very few number of update operations. + + +There are two solutions that can perform both the query and update operations in O(logn) time. +1. Fenwick Tree +2. Segment Tree + +Compared with Segment Tree, Binary Indexed Tree requires less space and is easier to implement. +""" + +class Fenwick_Tree(object): + def __init__(self, freq): + self.arr = freq + self.n = len(freq) + def get_sum(self, bit_tree, i): + s = 0 + i = i+1 + while i > 0: + s += bit_tree[i] + i -= i & (-i) + return s + def update_bit(self, bit_tree, i, v): + i += 1 + while i <= self.n: + bit_tree[i] += v + i += i & (-i) + def construct(self): + bit_tree = [0]*(self.n+1) + for i in range(self.n): + self.update_bit(bit_tree, i, self.arr[i]) + return bit_tree \ No newline at end of file diff --git a/algorithms/tree/segment_tree/FastSegMentTree.py b/algorithms/tree/segment_tree/FastSegMentTree.py new file mode 100644 index 000000000..11da22e59 --- /dev/null +++ b/algorithms/tree/segment_tree/FastSegMentTree.py @@ -0,0 +1,32 @@ +class SegmentTree: + def __init__(self, arr, function): + self.tree = [None for _ in range(len(arr))] + arr + self.n = len(arr) + self.fn = function + self.build_tree() + + def build_tree(self): + for i in range(self.n - 1, 0, -1): + self.tree[i] = self.fn(self.tree[i * 2], self.tree[i * 2 + 1]) + + def query(self, l, r): + l += self.n + r += self.n + ans = 0 + while l < r: + if l & 1: + ans = self.fn(ans, self.tree[l]) + l += 1 + if r & 1: + r -= 1 + ans = self.fn(ans, self.tree[r]) + l >>= 1 + r >>= 1 + return ans + + def update(self, i, val): + i += self.n + self.tree[i] = val + while i > 1: + i >>= 1 + self.tree[i] = self.fn(self.tree[i * 2], self.tree[i * 2 + 1]) \ No newline at end of file diff --git a/algorithms/tree/segment_tree/iterative_segment_tree.py b/algorithms/tree/segment_tree/iterative_segment_tree.py index 84018edc4..ede08f629 100644 --- a/algorithms/tree/segment_tree/iterative_segment_tree.py +++ b/algorithms/tree/segment_tree/iterative_segment_tree.py @@ -34,20 +34,20 @@ def build_tree(self): for i in range(self.size - 1, 0, -1): self.tree[i] = self.fn(self.tree[i * 2], self.tree[i * 2 + 1]) - def update(self, p, v): - p += self.size - self.tree[p] = v - while p > 1: - p = p // 2 - self.tree[p] = self.fn(self.tree[p * 2], self.tree[p * 2 + 1]) + def update(self, i, value): + i += self.size + self.tree[i] = value + while i > 1: + i = i // 2 + self.tree[i] = self.fn(self.tree[i * 2], self.tree[i * 2 + 1]) def query(self, l, r): l, r = l + self.size, r + self.size res = None while l <= r: - if l % 2 == 1: + if l & 1: res = self.tree[l] if res is None else self.fn(res, self.tree[l]) - if r % 2 == 0: + if not (r & 1): res = self.tree[r] if res is None else self.fn(res, self.tree[r]) - l, r = (l + 1) // 2, (r - 1) // 2 + l, r = (l + 1) >> 1, (r - 1) >> 1 return res diff --git a/algorithms/tree/segment_tree/segment_tree_non_commutative.py b/algorithms/tree/segment_tree/segment_tree_non_commutative.py new file mode 100644 index 000000000..0c7101f2a --- /dev/null +++ b/algorithms/tree/segment_tree/segment_tree_non_commutative.py @@ -0,0 +1,40 @@ +from math import ceil, log2 +class SegmentTree: + def __init__(self, arr, n): + arr, n = self.correct_arr(arr, n) + self.tree = [None] * n + arr + self.n = n + self.op = lambda x, y: x + y + self.build_tree() + + def correct_arr(self, arr, n): + new_size = 2 ** ceil(log2(n)) + arr += ['0'] * (new_size - n) + n = new_size + return arr, n + + def build_tree(self): + for i in range(self.n - 1, 0, -1): + self.tree[i] = self.op(self.tree[2 * i], self.tree[(2 * i) + 1]) + + def update(self, p): + p += self.n + if self.tree[p] == '0': + self.tree[p] = '1' + while p > 1: + p //= 2 + self.tree[p] = self.op(self.tree[2 * p], self.tree[(2 * p) + 1]) + + def query(self,l, r): + l += self.n + r += self.n + resl, resr = None, None + while l <= r: + if l & 1: + resl = self.tree[l] if resl is None else self.op(resl, self.tree[l]) + if not (r & 1): + resr = self.tree[r] if resr is None else self.op(self.tree[r], resr) + l, r = (l + 1) // 2, (r - 1) // 2 + if resl and resr: return resl + resr + elif resl and not resr: return resl + else: return resr \ No newline at end of file diff --git a/algorithms/tree/trie/easy_trie.py b/algorithms/tree/trie/easy_trie.py new file mode 100644 index 000000000..9c494c3d2 --- /dev/null +++ b/algorithms/tree/trie/easy_trie.py @@ -0,0 +1,44 @@ +from collections import deque +# Trie Template for adding words checking if a prefix is valid in trie and returning list of words with that prefix +def add_word(trie, word): + root = trie + for c in word: + if c not in root: root[c] = {} + root = root[c] + root['#'] = {} + +def has_word(trie, word): + for c in word: + if c in trie: + trie = trie[c] + else: return False + return '#' in trie + +def has_prefix(trie, pre): + for c in pre: + if c in trie: + trie = trie[c] + else: return False + return trie + +def get_words_using_prefix(trie, pre): + trie = has_prefix(trie, pre) + if not trie: return [] + words = [] + if '#' in trie: words.append(pre) + queue = deque([(trie, pre)]) + while queue: + curr, pre = queue.popleft() + for key in curr: + temp = curr[key] + if '#' in temp: words.append(pre + key) + queue.append((temp, pre + key)) + return words + +words = ['abcd', 'defg'] +prefixes = ['a', 'def'] +head = {} +for word in words: + add_word(head, word) +for pre in prefixes: + print(get_words_using_prefix(head, pre)) \ No newline at end of file diff --git a/algorithms/unionfind/disset.py b/algorithms/unionfind/disset.py new file mode 100644 index 000000000..27a486707 --- /dev/null +++ b/algorithms/unionfind/disset.py @@ -0,0 +1,24 @@ +class Disset: + def __init__(self, n): + self.root = list(range(n + 1)) + self.rank = [0] * (n + 1) + + def find(self, x): + if self.root[x] == x: return x + self.root[x] = self.find(self.root[x]) + return self.root[x] + + def union(self, x, y): + rootx = self.find(x) + rooty = self.find(y) + if rootx == rooty: + return False + if rootx != rooty: + if self.rank[rootx] > self.rank[rooty]: + self.root[rooty] = rootx + elif self.rank[rootx] < self.rank[rooty]: + self.root[rootx] = rooty + else: + self.root[rootx] = rooty + self.rank[rooty] += 1 + return True \ No newline at end of file