From 32638cce4ae9678cbc03b7f5787726f9e6a603c4 Mon Sep 17 00:00:00 2001 From: Kulratan Thapar Date: Fri, 17 Oct 2025 16:09:00 +0530 Subject: [PATCH] Add Python: DSA algorithms organized under algorithms/ and data-structures/ with per-folder READMEs and updated root README --- README.md | 34 +++++++++ code-snippets/algorithms/graphs/README.md | 13 ++++ code-snippets/algorithms/graphs/bfs_dfs.py | 73 +++++++++++++++++++ code-snippets/algorithms/graphs/dijkstra.py | 58 +++++++++++++++ code-snippets/algorithms/searching/README.md | 13 ++++ .../searching}/binary_search.py | 0 .../algorithms/searching/linear_search.py | 25 +++++++ code-snippets/algorithms/sorting/README.md | 21 ++++++ code-snippets/algorithms/sorting/heap_sort.py | 18 +++++ .../algorithms/sorting/insertion_sort.py | 23 ++++++ .../algorithms/sorting/merge_sort.py | 36 +++++++++ .../algorithms/sorting/quick_sort.py | 56 ++++++++++++++ .../algorithms/sorting/radix_sort.py | 35 +++++++++ .../algorithms/sorting/selection_sort.py | 23 ++++++ code-snippets/algorithms/techniques/README.md | 13 ++++ .../algorithms/techniques/sliding_window.py | 35 +++++++++ .../data-structures/disjoint-sets/README.md | 11 +++ .../disjoint-sets/union_find.py | 52 +++++++++++++ 18 files changed, 539 insertions(+) create mode 100644 code-snippets/algorithms/graphs/README.md create mode 100644 code-snippets/algorithms/graphs/bfs_dfs.py create mode 100644 code-snippets/algorithms/graphs/dijkstra.py create mode 100644 code-snippets/algorithms/searching/README.md rename code-snippets/{ => algorithms/searching}/binary_search.py (100%) create mode 100644 code-snippets/algorithms/searching/linear_search.py create mode 100644 code-snippets/algorithms/sorting/README.md create mode 100644 code-snippets/algorithms/sorting/heap_sort.py create mode 100644 code-snippets/algorithms/sorting/insertion_sort.py create mode 100644 code-snippets/algorithms/sorting/merge_sort.py create mode 100644 code-snippets/algorithms/sorting/quick_sort.py create mode 100644 code-snippets/algorithms/sorting/radix_sort.py create mode 100644 code-snippets/algorithms/sorting/selection_sort.py create mode 100644 code-snippets/algorithms/techniques/README.md create mode 100644 code-snippets/algorithms/techniques/sliding_window.py create mode 100644 code-snippets/data-structures/disjoint-sets/README.md create mode 100644 code-snippets/data-structures/disjoint-sets/union_find.py diff --git a/README.md b/README.md index 44a5003..b4522f5 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,12 @@ Please make sure to read our [CONTRIBUTING.md](CONTRIBUTING.md) and [CODE_OF_CON ``` code-snippets/ ├── algorithms/ +│ ├── sorting/ +│ ├── searching/ +│ ├── graphs/ +│ └── techniques/ ├── data-structures/ +│ └── disjoint-sets/ ├── web-development/ └── other/ ``` @@ -44,6 +49,35 @@ code-snippets/ - Follow best practices for the programming language you're using - Test your code before submitting +## 📘 DSA Snippets (Python) + +Beginner-friendly algorithm implementations with example usage: + +- Sorting: `code-snippets/algorithms/sorting/` with `quick_sort.py`, `merge_sort.py`, `insertion_sort.py`, `selection_sort.py`, `heap_sort.py`, `radix_sort.py` +- Searching: `code-snippets/algorithms/searching/` with `linear_search.py`, `binary_search.py` +- Graphs: `code-snippets/algorithms/graphs/` with `bfs_dfs.py` (BFS/DFS), `dijkstra.py` (shortest paths) +- Disjoint Sets: `code-snippets/data-structures/disjoint-sets/` with `union_find.py` +- Techniques: `code-snippets/algorithms/techniques/` with `sliding_window.py` + +### Run the examples (Windows): + +``` +python code-snippets\algorithms\sorting\quick_sort.py +python code-snippets\algorithms\sorting\merge_sort.py +python code-snippets\algorithms\sorting\insertion_sort.py +python code-snippets\algorithms\sorting\selection_sort.py +python code-snippets\algorithms\sorting\heap_sort.py +python code-snippets\algorithms\sorting\radix_sort.py +python code-snippets\algorithms\searching\linear_search.py +python code-snippets\algorithms\searching\binary_search.py +python code-snippets\algorithms\graphs\bfs_dfs.py +python code-snippets\algorithms\graphs\dijkstra.py +python code-snippets\data-structures\disjoint-sets\union_find.py +python code-snippets\algorithms\techniques\sliding_window.py +``` + +Each file prints sample outputs to demonstrate how the algorithm works. Feel free to open the files and tweak inputs to deepen understanding. + ## 🌈 Happy Hacking! Thank you for contributing to open source! Every contribution, no matter how small, makes a difference. Let's make Hacktoberfest 2025 amazing together! diff --git a/code-snippets/algorithms/graphs/README.md b/code-snippets/algorithms/graphs/README.md new file mode 100644 index 0000000..2596981 --- /dev/null +++ b/code-snippets/algorithms/graphs/README.md @@ -0,0 +1,13 @@ +# Graph Algorithms (Python) + +This folder contains basic graph algorithms: +- `bfs_dfs.py`: Breadth-First Search (BFS) and Depth-First Search (DFS) +- `dijkstra.py`: Dijkstra's shortest path (non-negative weights) + +## Run Examples (Windows) +``` +python code-snippets\algorithms\graphs\bfs_dfs.py +python code-snippets\algorithms\graphs\dijkstra.py +``` + +Each script includes example graphs and prints traversal or shortest path results. \ No newline at end of file diff --git a/code-snippets/algorithms/graphs/bfs_dfs.py b/code-snippets/algorithms/graphs/bfs_dfs.py new file mode 100644 index 0000000..db3824a --- /dev/null +++ b/code-snippets/algorithms/graphs/bfs_dfs.py @@ -0,0 +1,73 @@ +""" +Breadth-First Search (BFS) and Depth-First Search (DFS) +Graph is represented as adjacency list: dict[node] = list[neighbor] +""" +from collections import deque +from typing import Dict, List, Hashable, Set + +Graph = Dict[Hashable, List[Hashable]] + + +def bfs(graph: Graph, start: Hashable) -> List[Hashable]: + """Return list of nodes in BFS order from start.""" + visited: Set[Hashable] = set() + order: List[Hashable] = [] + q = deque([start]) + visited.add(start) + + while q: + node = q.popleft() + order.append(node) + for nei in graph.get(node, []): + if nei not in visited: + visited.add(nei) + q.append(nei) + return order + + +def dfs_recursive(graph: Graph, start: Hashable) -> List[Hashable]: + visited: Set[Hashable] = set() + order: List[Hashable] = [] + + def helper(node: Hashable): + visited.add(node) + order.append(node) + for nei in graph.get(node, []): + if nei not in visited: + helper(nei) + + helper(start) + return order + + +def dfs_iterative(graph: Graph, start: Hashable) -> List[Hashable]: + visited: Set[Hashable] = set() + order: List[Hashable] = [] + stack: List[Hashable] = [start] + + while stack: + node = stack.pop() + if node in visited: + continue + visited.add(node) + order.append(node) + # Add neighbors in reverse to mimic recursive order (optional) + for nei in reversed(graph.get(node, [])): + if nei not in visited: + stack.append(nei) + return order + + +if __name__ == "__main__": + g = { + 'A': ['B', 'C'], + 'B': ['D', 'E'], + 'C': ['F'], + 'D': [], + 'E': ['F'], + 'F': [] + } + + print("BFS from A:", bfs(g, 'A')) + print("DFS recursive from A:", dfs_recursive(g, 'A')) + print("DFS iterative from A:", dfs_iterative(g, 'A')) \ No newline at end of file diff --git a/code-snippets/algorithms/graphs/dijkstra.py b/code-snippets/algorithms/graphs/dijkstra.py new file mode 100644 index 0000000..9294b97 --- /dev/null +++ b/code-snippets/algorithms/graphs/dijkstra.py @@ -0,0 +1,58 @@ +""" +Dijkstra's Algorithm for shortest paths from a source node +Graph representation: dict[node] = list[(neighbor, weight)] +Assumes non-negative edge weights. +""" +from typing import Dict, List, Tuple, Hashable +import heapq + +Graph = Dict[Hashable, List[Tuple[Hashable, float]]] + + +def dijkstra(graph: Graph, start: Hashable) -> Tuple[Dict[Hashable, float], Dict[Hashable, Hashable | None]]: + """ + Return (dist, prev) where: + - dist[node] is the shortest distance from start to node + - prev[node] is the predecessor of node on the shortest path + """ + dist: Dict[Hashable, float] = {node: float('inf') for node in graph} + prev: Dict[Hashable, Hashable | None] = {node: None for node in graph} + dist[start] = 0.0 + + pq: List[Tuple[float, Hashable]] = [(0.0, start)] + + while pq: + d, node = heapq.heappop(pq) + if d > dist[node]: + continue # Skip outdated entry + for nei, w in graph.get(node, []): + nd = d + w + if nd < dist.get(nei, float('inf')): + dist[nei] = nd + prev[nei] = node + heapq.heappush(pq, (nd, nei)) + + return dist, prev + + +def reconstruct_path(prev: Dict[Hashable, Hashable | None], target: Hashable) -> List[Hashable]: + path: List[Hashable] = [] + cur: Hashable | None = target + while cur is not None: + path.append(cur) + cur = prev[cur] + return list(reversed(path)) + + +if __name__ == "__main__": + g: Graph = { + 'A': [('B', 4), ('C', 2)], + 'B': [('C', 5), ('D', 10)], + 'C': [('E', 3)], + 'D': [('F', 11)], + 'E': [('D', 4)], + 'F': [] + } + dist, prev = dijkstra(g, 'A') + print("Distances:", dist) + print("Path A->F:", reconstruct_path(prev, 'F')) \ No newline at end of file diff --git a/code-snippets/algorithms/searching/README.md b/code-snippets/algorithms/searching/README.md new file mode 100644 index 0000000..0736179 --- /dev/null +++ b/code-snippets/algorithms/searching/README.md @@ -0,0 +1,13 @@ +# Searching Algorithms (Python) + +This folder contains searching algorithms: +- `linear_search.py`: Find index(es) of a target in an array +- `binary_search.py`: Binary search on a sorted array + +## Run Examples (Windows) +``` +python code-snippets\algorithms\searching\linear_search.py +python code-snippets\algorithms\searching\binary_search.py +``` + +These scripts include simple examples and can be modified for custom inputs. \ No newline at end of file diff --git a/code-snippets/binary_search.py b/code-snippets/algorithms/searching/binary_search.py similarity index 100% rename from code-snippets/binary_search.py rename to code-snippets/algorithms/searching/binary_search.py diff --git a/code-snippets/algorithms/searching/linear_search.py b/code-snippets/algorithms/searching/linear_search.py new file mode 100644 index 0000000..250cea2 --- /dev/null +++ b/code-snippets/algorithms/searching/linear_search.py @@ -0,0 +1,25 @@ +""" +Linear Search +Time: O(n) +Space: O(1) +""" +from typing import List, Any + + +def linear_search(arr: List[Any], target: Any) -> int: + """Return first index of target in arr, or -1 if not found.""" + for i, val in enumerate(arr): + if val == target: + return i + return -1 + + +def linear_search_all(arr: List[Any], target: Any) -> List[int]: + """Return all indices where target appears in arr.""" + return [i for i, val in enumerate(arr) if val == target] + + +if __name__ == "__main__": + data = [3, 5, 2, 5, 9] + print("First index of 5:", linear_search(data, 5)) + print("All indices of 5:", linear_search_all(data, 5)) \ No newline at end of file diff --git a/code-snippets/algorithms/sorting/README.md b/code-snippets/algorithms/sorting/README.md new file mode 100644 index 0000000..e1653e9 --- /dev/null +++ b/code-snippets/algorithms/sorting/README.md @@ -0,0 +1,21 @@ +# Sorting Algorithms (Python) + +This folder contains common sorting algorithms with simple examples: +- `quick_sort.py`: Functional and in-place Quick Sort +- `merge_sort.py`: Merge Sort +- `insertion_sort.py`: Insertion Sort +- `selection_sort.py`: Selection Sort +- `heap_sort.py`: Heap Sort (using `heapq`) +- `radix_sort.py`: Radix Sort (LSD, non-negative integers) + +## Run Examples (Windows) +``` +python code-snippets\algorithms\sorting\quick_sort.py +python code-snippets\algorithms\sorting\merge_sort.py +python code-snippets\algorithms\sorting\insertion_sort.py +python code-snippets\algorithms\sorting\selection_sort.py +python code-snippets\algorithms\sorting\heap_sort.py +python code-snippets\algorithms\sorting\radix_sort.py +``` + +Each script prints sample outputs and can be edited to test custom inputs. \ No newline at end of file diff --git a/code-snippets/algorithms/sorting/heap_sort.py b/code-snippets/algorithms/sorting/heap_sort.py new file mode 100644 index 0000000..b0fa72a --- /dev/null +++ b/code-snippets/algorithms/sorting/heap_sort.py @@ -0,0 +1,18 @@ +""" +Heap Sort using Python's heapq (min-heap) +Time: O(n log n) +Space: O(n) due to heap container +""" +from typing import List, Any +import heapq + + +def heap_sort(arr: List[Any]) -> List[Any]: + heap = arr[:] + heapq.heapify(heap) + return [heapq.heappop(heap) for _ in range(len(heap))] + + +if __name__ == "__main__": + data = [4, 10, 3, 5, 1] + print("Heap Sort:", heap_sort(data)) \ No newline at end of file diff --git a/code-snippets/algorithms/sorting/insertion_sort.py b/code-snippets/algorithms/sorting/insertion_sort.py new file mode 100644 index 0000000..7af2ab4 --- /dev/null +++ b/code-snippets/algorithms/sorting/insertion_sort.py @@ -0,0 +1,23 @@ +""" +Insertion Sort implementation (returns a new sorted list) +Time: O(n^2) +Space: O(1) +""" +from typing import List, Any + + +def insertion_sort(arr: List[Any]) -> List[Any]: + a = arr[:] + for i in range(1, len(a)): + key = a[i] + j = i - 1 + while j >= 0 and a[j] > key: + a[j + 1] = a[j] + j -= 1 + a[j + 1] = key + return a + + +if __name__ == "__main__": + data = [9, 1, 5, 3, 7] + print("Insertion Sort:", insertion_sort(data)) \ No newline at end of file diff --git a/code-snippets/algorithms/sorting/merge_sort.py b/code-snippets/algorithms/sorting/merge_sort.py new file mode 100644 index 0000000..c6b2da5 --- /dev/null +++ b/code-snippets/algorithms/sorting/merge_sort.py @@ -0,0 +1,36 @@ +""" +Merge Sort implementation (returns a new sorted list) +Time: O(n log n) +Space: O(n) +""" +from typing import List, Any + + +def merge_sort(arr: List[Any]) -> List[Any]: + if len(arr) <= 1: + return arr[:] + + mid = len(arr) // 2 + left = merge_sort(arr[:mid]) + right = merge_sort(arr[mid:]) + return _merge(left, right) + + +def _merge(left: List[Any], right: List[Any]) -> List[Any]: + i = j = 0 + merged: List[Any] = [] + while i < len(left) and j < len(right): + if left[i] <= right[j]: + merged.append(left[i]) + i += 1 + else: + merged.append(right[j]) + j += 1 + merged.extend(left[i:]) + merged.extend(right[j:]) + return merged + + +if __name__ == "__main__": + data = [5, 2, 4, 6, 1, 3] + print("Merge Sort:", merge_sort(data)) \ No newline at end of file diff --git a/code-snippets/algorithms/sorting/quick_sort.py b/code-snippets/algorithms/sorting/quick_sort.py new file mode 100644 index 0000000..be32a7e --- /dev/null +++ b/code-snippets/algorithms/sorting/quick_sort.py @@ -0,0 +1,56 @@ +""" +Quick Sort implementations +- quick_sort: returns a new sorted list (functional style) +- quicksort_inplace: sorts the list in place (Hoare/Lomuto-style partition) + +Time: Average O(n log n), Worst O(n^2) +Space: O(log n) recursion stack (in-place variant) +""" +from typing import List, Any + + +def quick_sort(arr: List[Any]) -> List[Any]: + """Return a new sorted list using Quick Sort (functional version). + + This version chooses the middle element as pivot and constructs + left/mid/right partitions, then recursively sorts left and right. + """ + if len(arr) <= 1: + return arr[:] + + pivot = arr[len(arr) // 2] + left = [x for x in arr if x < pivot] + mid = [x for x in arr if x == pivot] + right = [x for x in arr if x > pivot] + return quick_sort(left) + mid + quick_sort(right) + + +def quicksort_inplace(arr: List[Any], low: int = 0, high: int | None = None) -> List[Any]: + """Sort the list in-place using Quick Sort (Lomuto partition).""" + if high is None: + high = len(arr) - 1 + + def partition(lo: int, hi: int) -> int: + pivot = arr[hi] + i = lo + for j in range(lo, hi): + if arr[j] <= pivot: + arr[i], arr[j] = arr[j], arr[i] + i += 1 + arr[i], arr[hi] = arr[hi], arr[i] + return i + + if low < high: + p = partition(low, high) + quicksort_inplace(arr, low, p - 1) + quicksort_inplace(arr, p + 1, high) + return arr + + +if __name__ == "__main__": + data = [9, 3, 5, 1, 4, 7, 2, 6, 8] + print("Functional quick_sort:", quick_sort(data)) + + data2 = [3, 6, 1, 8, 4, 5] + quicksort_inplace(data2) + print("In-place quicksort:", data2) \ No newline at end of file diff --git a/code-snippets/algorithms/sorting/radix_sort.py b/code-snippets/algorithms/sorting/radix_sort.py new file mode 100644 index 0000000..bdfb188 --- /dev/null +++ b/code-snippets/algorithms/sorting/radix_sort.py @@ -0,0 +1,35 @@ +""" +Radix Sort (LSD, base-10) for non-negative integers. +Time: O(d * (n + k)) where d = number of digits, k = base +Space: O(n + k) +""" +from typing import List + + +def radix_sort(arr: List[int]) -> List[int]: + """ + Sort a list of non-negative integers using LSD Radix Sort. + """ + if not arr: + return [] + if min(arr) < 0: + raise ValueError("radix_sort supports only non-negative integers") + + output = arr[:] + exp = 1 # 1, 10, 100, ... + max_val = max(output) + + while max_val // exp > 0: + buckets = [[] for _ in range(10)] + for num in output: + digit = (num // exp) % 10 + buckets[digit].append(num) + output = [num for bucket in buckets for num in bucket] + exp *= 10 + + return output + + +if __name__ == "__main__": + data = [170, 45, 75, 90, 802, 24, 2, 66] + print("Radix Sort:", radix_sort(data)) \ No newline at end of file diff --git a/code-snippets/algorithms/sorting/selection_sort.py b/code-snippets/algorithms/sorting/selection_sort.py new file mode 100644 index 0000000..433ea3c --- /dev/null +++ b/code-snippets/algorithms/sorting/selection_sort.py @@ -0,0 +1,23 @@ +""" +Selection Sort implementation (returns a new sorted list) +Time: O(n^2) +Space: O(1) +""" +from typing import List, Any + + +def selection_sort(arr: List[Any]) -> List[Any]: + a = arr[:] + n = len(a) + for i in range(n): + min_idx = i + for j in range(i + 1, n): + if a[j] < a[min_idx]: + min_idx = j + a[i], a[min_idx] = a[min_idx], a[i] + return a + + +if __name__ == "__main__": + data = [64, 25, 12, 22, 11] + print("Selection Sort:", selection_sort(data)) \ No newline at end of file diff --git a/code-snippets/algorithms/techniques/README.md b/code-snippets/algorithms/techniques/README.md new file mode 100644 index 0000000..4cd3769 --- /dev/null +++ b/code-snippets/algorithms/techniques/README.md @@ -0,0 +1,13 @@ +# Algorithmic Techniques + +This folder contains common algorithmic techniques: +- `sliding_window.py`: Sliding window examples + - Max sum subarray of size k + - Longest substring without repeating characters + +## Run Example (Windows) +``` +python code-snippets\algorithms\techniques\sliding_window.py +``` + +Edit the inputs in the file to explore different scenarios. \ No newline at end of file diff --git a/code-snippets/algorithms/techniques/sliding_window.py b/code-snippets/algorithms/techniques/sliding_window.py new file mode 100644 index 0000000..21586dc --- /dev/null +++ b/code-snippets/algorithms/techniques/sliding_window.py @@ -0,0 +1,35 @@ +""" +Sliding Window examples: +1) Max sum of a subarray of size k +2) Length of longest substring without repeating characters +""" +from typing import List + + +def max_subarray_sum_k(arr: List[int], k: int) -> int: + if k <= 0 or k > len(arr): + raise ValueError("k must be between 1 and len(arr)") + window_sum = sum(arr[:k]) + max_sum = window_sum + for i in range(k, len(arr)): + window_sum += arr[i] - arr[i - k] + if window_sum > max_sum: + max_sum = window_sum + return max_sum + + +def length_of_longest_substring(s: str) -> int: + last_index = {} + start = 0 + max_len = 0 + for i, ch in enumerate(s): + if ch in last_index and last_index[ch] >= start: + start = last_index[ch] + 1 + last_index[ch] = i + max_len = max(max_len, i - start + 1) + return max_len + + +if __name__ == "__main__": + print("Max sum subarray of size 3:", max_subarray_sum_k([2, 1, 5, 1, 3, 2], 3)) + print("Longest substring without repeat length:", length_of_longest_substring("abcabcbb")) \ No newline at end of file diff --git a/code-snippets/data-structures/disjoint-sets/README.md b/code-snippets/data-structures/disjoint-sets/README.md new file mode 100644 index 0000000..fa4e93c --- /dev/null +++ b/code-snippets/data-structures/disjoint-sets/README.md @@ -0,0 +1,11 @@ +# Disjoint Sets (Union-Find) + +This folder contains a Union-Find (Disjoint Set Union - DSU) implementation: +- `union_find.py`: Path compression + union by rank + +## Run Example (Windows) +``` +python code-snippets\data-structures\disjoint-sets\union_find.py +``` + +Example usage demonstrates set unions and connectivity checks. \ No newline at end of file diff --git a/code-snippets/data-structures/disjoint-sets/union_find.py b/code-snippets/data-structures/disjoint-sets/union_find.py new file mode 100644 index 0000000..7104724 --- /dev/null +++ b/code-snippets/data-structures/disjoint-sets/union_find.py @@ -0,0 +1,52 @@ +""" +Union-Find (Disjoint Set Union - DSU) with path compression and union by rank. +Operations: +- find(x): return representative of x +- union(x, y): merge sets containing x and y +- connected(x, y): check if x and y are in same set +""" +from typing import Dict, Hashable + + +class UnionFind: + def __init__(self, elements: list[Hashable] | None = None): + self.parent: Dict[Hashable, Hashable] = {} + self.rank: Dict[Hashable, int] = {} + if elements: + for e in elements: + self.make_set(e) + + def make_set(self, x: Hashable) -> None: + if x not in self.parent: + self.parent[x] = x + self.rank[x] = 0 + + def find(self, x: Hashable) -> Hashable: + if self.parent[x] != x: + self.parent[x] = self.find(self.parent[x]) + return self.parent[x] + + def union(self, x: Hashable, y: Hashable) -> None: + rx, ry = self.find(x), self.find(y) + if rx == ry: + return + if self.rank[rx] < self.rank[ry]: + self.parent[rx] = ry + elif self.rank[rx] > self.rank[ry]: + self.parent[ry] = rx + else: + self.parent[ry] = rx + self.rank[rx] += 1 + + def connected(self, x: Hashable, y: Hashable) -> bool: + return self.find(x) == self.find(y) + + +if __name__ == "__main__": + uf = UnionFind(['A', 'B', 'C', 'D']) + uf.union('A', 'B') + uf.union('C', 'D') + print("A connected to B:", uf.connected('A', 'B')) + print("A connected to C:", uf.connected('A', 'C')) + uf.union('B', 'C') + print("A connected to D after union:", uf.connected('A', 'D')) \ No newline at end of file