From 88cdc9f26397d8b0e6565a92ed727978a437e377 Mon Sep 17 00:00:00 2001 From: Eshaan Rawat Date: Mon, 7 Oct 2024 14:40:18 -0700 Subject: [PATCH 1/8] Added Union Find Data Structure --- Data Structures and Algorithms/union_find.py | 88 ++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 Data Structures and Algorithms/union_find.py diff --git a/Data Structures and Algorithms/union_find.py b/Data Structures and Algorithms/union_find.py new file mode 100644 index 00000000..b01f53ca --- /dev/null +++ b/Data Structures and Algorithms/union_find.py @@ -0,0 +1,88 @@ +# Basic implementation of the Union Find data structure +# Assume we have n nodes labeled from 0 to n - 1 + +class UnionFind: + def __init__(self, n): + # every node is originally its own parent + self.par = [i for i in range(n)] + # self.par = list(range(n)) -- also valid + + # every node originally is in its own + # component of size 1 - this changes during + # the union operation + self.rank = [1] * n + + def find(self, n) -> int: + ''' + Finds the parent node of n + ''' + + # can be optimized with path compression + while n != self.par[n]: + n = self.par[n] + return n + + + def union(self, n1, n2) -> bool: + ''' + Connects two nodes together if not + already connected + ''' + + # find the parent of node 1 and 2 + p1 = self.find(n1) + p2 = self.find(n2) + + # nodes are already connected + # cannot union together + if p1 == p2: + return False + + # for efficiency, make bigger component + # parent of smaller component - reduces + # number of steps we have to take in find() + + if self.rank[p1] >= self.rank[p2]: + # p2 is smaller, so when union it has a + # new parent, p1 + self.par[p2] = p1 + + # p1 gets all the nodes of p2, increasing + # its rank, or size + self.rank[p1] += self.rank[p2] + else: + self.par[p1] = p2 + self.rank[p2] += self.rank[p1] + + return True + + def nodes_connected(self, n1, n2) -> bool: + ''' + Returns if two nodes are connected + ''' + + # connected if parent is the same + return self.find(n1) == self.find(n2) + + + +def verify(): + n = 7 + u = UnionFind(n) + + # False, nodes not connected + print(u.nodes_connected(0, 1)) + + # True, just connected 0 and 1 + u.union(0, 1) + print(u.nodes_connected(0, 1)) + + # Rank is 2, includes 0 and 1 + print(u.rank[0]) + + u.union(4, 5) + u.union(1, 4) + + # True, 0 - 1 and 4 - 5 are connected + # 1 to 4 connects both components + print(u.nodes_connected(0, 5)) \ No newline at end of file From 4f7e345464d2cc12851cbf528e1cbe3e0df403e7 Mon Sep 17 00:00:00 2001 From: Eshaan Rawat Date: Thu, 10 Oct 2024 12:09:37 -0700 Subject: [PATCH 2/8] Added README.md for union find --- .../union_find/README.md | 84 +++++++++++++++++++ .../{ => union_find}/union_find.py | 0 2 files changed, 84 insertions(+) create mode 100644 Data Structures and Algorithms/union_find/README.md rename Data Structures and Algorithms/{ => union_find}/union_find.py (100%) diff --git a/Data Structures and Algorithms/union_find/README.md b/Data Structures and Algorithms/union_find/README.md new file mode 100644 index 00000000..6f39cf37 --- /dev/null +++ b/Data Structures and Algorithms/union_find/README.md @@ -0,0 +1,84 @@ +# Union Find (Disjoint Set Union) - Implementation and Use + +## Table of Contents +- [Why Union Find?](#why-union-find) +- [Functions and Examples](#functions-and-examples) +- [Setup](#setup) +- [Additional Resources](#additional-resources) +- [Leetcode Questions](#leetcode-questions) + +## Why Union Find? +Union Find is a popular data structure that allows us to solve many different types of graph +problems. It works best with undirected graphs, and it allows us to figure out whether a node +is connected to another node. + +Some problems it can be used to solve: +- Find the minimum spanning tree in a graph (Kruskal's) +- Check if there is a path between two nodes +- Finding redundant edges +- Representing networks + + +## Functions and Examples +Union Find seems complex at first, but it is actually a lot easier when you understand that there are +only two functions. +- Find(n) : returns the parent of a node n +- Union(n1, n2) : connects n1 and n2 if they are not previously connected + +Let's look at an example! +```python +u = UnionFind(7) # create a UnionFind object with 7 nodes (numbered 0 to 6) + +u.union(0, 1) # connects 0 and 1 together +u.union(5, 6) # connects 5 and 6 together + +u.find(1) # returns 0, since 0 is parent of 1 +u.find(5) # returns 5, since 5 is its own parent + +u.union(1, 2) # connects 2 to the component 0-1 +u.find(2) # 2s parent is now 0 + +# Now our structure looks like this + +# 0-1-2 3 4 5-6 + +u.union(1, 6) # first we find the parents of 1 and 6 + # parents are 0, and 5 + # connect the smaller component to the bigger + # now 5's parent is 0 + +u.find(6) # now this goes: + # 6 parent is 5 -> 5 parent is 0 -> 0 is its own parent +``` + +And that's it! You can use the sample code to test different examples with Union Find. +In the code, par keeps track of the parent of each node and rank keeps track of the size of +each component. + +## Setup + +First clone the repo + > `cd union_find` to get into this folder. + > call the verify function anywhere, consider adding ``` if __name__ == '__main__'``` + > `python union_find.py` to run the demo + + You can modify the structure in the verify function and play around with it. + + ## Additional Resources + + Here are some resources I found useful when learning: + - Neetcode Graph Videos on YouTube + - William Fiset - Union Find Video on YouTube + - Union Find Medium Article by Claire Lee + - Union Find Visualizer - Visualgo + + ## Leetcode Questions + - 200 - Number of Islands + - 684 - Redundant Connection + - 695 - Max Area of an Island + - 827 - Making a Large Island + - 2316 - Count Unreachable Pairs of Nodes in an Undirected Graph + - 2421 - Maximum Score of a Good Path + - 2709 - Greatest Common Divisor Traversal + + I hope this was helpful. If there are any mistakes or issues or if you want to contribute to union find, feel free to contact me at rawateshaan0 [at] gmail [dot] com \ No newline at end of file diff --git a/Data Structures and Algorithms/union_find.py b/Data Structures and Algorithms/union_find/union_find.py similarity index 100% rename from Data Structures and Algorithms/union_find.py rename to Data Structures and Algorithms/union_find/union_find.py From 50bbd38ea7c33d657a75e51f97c1c4dc800559f8 Mon Sep 17 00:00:00 2001 From: Kavindu Dhananjaya Date: Fri, 10 Jan 2025 21:51:05 +0530 Subject: [PATCH 3/8] Create Cycle_Sort.py --- .../Sorting Algorithms/Cycle_Sort.py | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 Data Structures and Algorithms/Sorting Algorithms/Cycle_Sort.py diff --git a/Data Structures and Algorithms/Sorting Algorithms/Cycle_Sort.py b/Data Structures and Algorithms/Sorting Algorithms/Cycle_Sort.py new file mode 100644 index 00000000..834c0a33 --- /dev/null +++ b/Data Structures and Algorithms/Sorting Algorithms/Cycle_Sort.py @@ -0,0 +1,49 @@ +def cycleSort(array): + writes = 0 # keeps track of the number of writes or swaps made during the sorting process + + # Loop through the array to find cycles to rotate. + for cycleStart in range(0, len(array) - 1): + item = array[cycleStart] + + # Find where to put the item. + position = cycleStart + for i in range(cycleStart + 1, len(array)): + if array[i] < item: + position += 1 + + # If the item is already there, this is not a cycle. + if position == cycleStart: + continue + + # Otherwise, put the item there or right after any duplicates. + while item == array[position]: + position += 1 + array[position], item = item, array[position] + writes += 1 + + # Rotate the rest of the cycle. + while position != cycleStart: + + # Find where to put the item. + position = cycleStart + for i in range(cycleStart + 1, len(array)): + if array[i] < item: + position += 1 + + # Put the item there or right after any duplicates. + while item == array[position]: + position += 1 + array[position], item = item, array[position] + writes += 1 + + return writes + + + +arr = [1, 8, 3, 9, 10, 10, 2, 4 ] +n = len(arr) +cycleSort(arr) + +print("After sort : ") +for i in range(0, n) : + print(arr[i], end = ' ') From f2fc2d2b9fda26ce5958a5e41bf2e39e7f4a38d8 Mon Sep 17 00:00:00 2001 From: Kavindu Dhananjaya Date: Fri, 10 Jan 2025 22:08:24 +0530 Subject: [PATCH 4/8] Update Cycle_Sort.py --- .../Sorting Algorithms/Cycle_Sort.py | 99 ++++++++++--------- 1 file changed, 50 insertions(+), 49 deletions(-) diff --git a/Data Structures and Algorithms/Sorting Algorithms/Cycle_Sort.py b/Data Structures and Algorithms/Sorting Algorithms/Cycle_Sort.py index 834c0a33..1afd985f 100644 --- a/Data Structures and Algorithms/Sorting Algorithms/Cycle_Sort.py +++ b/Data Structures and Algorithms/Sorting Algorithms/Cycle_Sort.py @@ -1,49 +1,50 @@ -def cycleSort(array): - writes = 0 # keeps track of the number of writes or swaps made during the sorting process - - # Loop through the array to find cycles to rotate. - for cycleStart in range(0, len(array) - 1): - item = array[cycleStart] - - # Find where to put the item. - position = cycleStart - for i in range(cycleStart + 1, len(array)): - if array[i] < item: - position += 1 - - # If the item is already there, this is not a cycle. - if position == cycleStart: - continue - - # Otherwise, put the item there or right after any duplicates. - while item == array[position]: - position += 1 - array[position], item = item, array[position] - writes += 1 - - # Rotate the rest of the cycle. - while position != cycleStart: - - # Find where to put the item. - position = cycleStart - for i in range(cycleStart + 1, len(array)): - if array[i] < item: - position += 1 - - # Put the item there or right after any duplicates. - while item == array[position]: - position += 1 - array[position], item = item, array[position] - writes += 1 - - return writes - - - -arr = [1, 8, 3, 9, 10, 10, 2, 4 ] -n = len(arr) -cycleSort(arr) - -print("After sort : ") -for i in range(0, n) : - print(arr[i], end = ' ') +from typing import List + +def cycle_sort(nums: List[int]) -> int: + + writes = 0 + + for cycle_start in range(len(nums) - 1): + current = nums[cycle_start] + + # Find the target position for the current item. + target_position = cycle_start + for i in range(cycle_start + 1, len(nums)): + if nums[i] < current: + target_position += 1 + + # Skip if the item is already in the correct position. + if target_position == cycle_start: + continue + + # Handle duplicates by finding the next available position. + while current == nums[target_position]: + target_position += 1 + + nums[target_position], current = current, nums[target_position] + writes += 1 + + # Rotate the rest of the cycle. + while target_position != cycle_start: + target_position = cycle_start + for i in range(cycle_start + 1, len(nums)): + if nums[i] < current: + target_position += 1 + + while current == nums[target_position]: + target_position += 1 + + nums[target_position], current = current, nums[target_position] + writes += 1 + + return writes + + +if __name__ == "__main__": + arr = [1, 8, 3, 9, 10, 10, 2, 4] + print("Before sort:", arr) + + writes = cycle_sort(arr) + + print("After sort:", arr) + print(f"Number of writes: {writes}") From 0afa310347577d565e2b4398ba6b2e5dddc1f7b2 Mon Sep 17 00:00:00 2001 From: Kavindu Dhananjaya Date: Tue, 14 Jan 2025 15:54:18 +0530 Subject: [PATCH 5/8] Create README.md --- .../Sorting Algorithms/README.md | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Data Structures and Algorithms/Sorting Algorithms/README.md diff --git a/Data Structures and Algorithms/Sorting Algorithms/README.md b/Data Structures and Algorithms/Sorting Algorithms/README.md new file mode 100644 index 00000000..a951a505 --- /dev/null +++ b/Data Structures and Algorithms/Sorting Algorithms/README.md @@ -0,0 +1,25 @@ +# Cycle Sort Algorithm + +## Overview +Cycle Sort is a comparison-based sorting algorithm that is efficient when minimizing memory writes is important. It is an in-place sorting algorithm that rearranges the elements by identifying cycles in the permutation of elements. + +## Algorithm Explanation +The algorithm works by: +1. Identifying the correct position of each element in the array. +2. Placing the element in its correct position and replacing the element already there in the cycle. +3. Repeating the process for the remaining unsorted elements. + +## Complexity +- **Time Complexity**: + - Best, Worst, and Average Case: O(n²) (due to nested cycles). +- **Space Complexity**: O(1) (in-place sorting). + +## Usage Example +```python +from Cycle_Sort import cycle_sort + +arr = [4, 5, 3, 2, 1] +print("Original array:", arr) +writes = cycle_sort(arr) +print("Sorted array:", arr) +print("Number of writes performed:", writes) From 5fd8183d67c3ba0bb0e3669afff331c7b4eb128d Mon Sep 17 00:00:00 2001 From: Kavindu Dhananjaya Date: Wed, 29 Jan 2025 14:32:04 +0530 Subject: [PATCH 6/8] Create Pigeonhole_Sort.py --- .../Sorting Algorithms/Pigeonhole_Sort.py | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 Data Structures and Algorithms/Sorting Algorithms/Pigeonhole_Sort.py diff --git a/Data Structures and Algorithms/Sorting Algorithms/Pigeonhole_Sort.py b/Data Structures and Algorithms/Sorting Algorithms/Pigeonhole_Sort.py new file mode 100644 index 00000000..11a60934 --- /dev/null +++ b/Data Structures and Algorithms/Sorting Algorithms/Pigeonhole_Sort.py @@ -0,0 +1,33 @@ +# Python program to implement Pigeonhole Sort + +def pigeonhole_sort(a): + # size of range of values in the list + # (ie, number of pigeonholes we need) + my_min = min(a) + my_max = max(a) + size = my_max - my_min + 1 + + # our list of pigeonholes + holes = [0] * size + + # Populate the pigeonholes. + for x in a: + assert type(x) is int, "integers only" + holes[x - my_min] += 1 + + # Put the elements back into the array in order. + i = 0 + for count in range(size): + while holes[count] > 0: + holes[count] -= 1 + a[i] = count + my_min + i += 1 + + +a = [8, 1, 2, 7, 4, 5, 8] +print("Sorted order is : ", end = ' ') + +pigeonhole_sort(a) + +for i in range(0, len(a)): + print(a[i], end = ' ') From c6add6082951df7395b36964ff7409119c2a0d0a Mon Sep 17 00:00:00 2001 From: Kavindu Dhananjaya Date: Wed, 29 Jan 2025 14:41:33 +0530 Subject: [PATCH 7/8] Update README.md --- .../Sorting Algorithms/README.md | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Data Structures and Algorithms/Sorting Algorithms/README.md b/Data Structures and Algorithms/Sorting Algorithms/README.md index a951a505..7ddfda4b 100644 --- a/Data Structures and Algorithms/Sorting Algorithms/README.md +++ b/Data Structures and Algorithms/Sorting Algorithms/README.md @@ -23,3 +23,27 @@ print("Original array:", arr) writes = cycle_sort(arr) print("Sorted array:", arr) print("Number of writes performed:", writes) +``` +# Pigeonhole Sort Algorithm + +## Overview +Pigeonhole Sort is a sorting algorithm that works well for sorting lists where the range of values (i.e., the difference between the maximum and minimum values) is not significantly larger than the number of elements in the list. It is a non-comparison-based sorting algorithm. + +The algorithm works by placing each element into its corresponding "pigeonhole" (a slot or bucket) and then iterating through the pigeonholes in order to reconstruct the sorted list. + +## Complexity +- **Time Complexity**: + - The time complexity of Pigeonhole Sort is O(n + range), where n is the number of elements in the list and range is the difference between the maximum and minimum values. + + - This makes it efficient for lists with a small range of values. +- **Space Complexity**: The space complexity is O(range), as it requires additional space for the holes list. +- **Limitations**: Pigeonhole Sort is not suitable for lists with a large range of values, as it would require a lot of memory for the holes list. + +## Usage Example +```python +from PigeonHole_Sort import pigeonhole_sort + +arr = [4, 5, 3, 2, 1] +print("Original array:", arr) +writes = pigeonhole_sort(arr) +print("Sorted array:", arr) From 73445bcbd8ef04e4f383625d0e411f7be448a4c7 Mon Sep 17 00:00:00 2001 From: Himaja Date: Mon, 10 Feb 2025 09:31:07 +0530 Subject: [PATCH 8/8] Added queues.py with content --- Data Structures and Algorithms/queues.py | 27 ++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 Data Structures and Algorithms/queues.py diff --git a/Data Structures and Algorithms/queues.py b/Data Structures and Algorithms/queues.py new file mode 100644 index 00000000..95f67510 --- /dev/null +++ b/Data Structures and Algorithms/queues.py @@ -0,0 +1,27 @@ +class Queue: + def __init__(self): + self.queue = [] + + def enqueue(self, item): + self.queue.append(item) + + def dequeue(self): + if not self.is_empty(): + return self.queue.pop(0) + return "Queue is empty" + + def is_empty(self): + return len(self.queue) == 0 + + def peek(self): + return self.queue[0] if not self.is_empty() else None + + def size(self): + return len(self.queue) + +# Example Usage +q = Queue() +q.enqueue(10) +q.enqueue(20) +print(q.dequeue()) # Output: 10 +print(q.peek()) # Output: 20