toolkit-ds 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ds_toolkit/__init__.py +21 -0
- ds_toolkit/avl.py +192 -0
- ds_toolkit/bst.py +117 -0
- ds_toolkit/doubly_linked_list.py +75 -0
- ds_toolkit/heap.py +73 -0
- ds_toolkit/queue.py +91 -0
- ds_toolkit/singly_linked_list.py +63 -0
- ds_toolkit/stack.py +86 -0
- ds_toolkit/union_find.py +36 -0
- toolkit_ds-0.1.0.dist-info/LICENSE +21 -0
- toolkit_ds-0.1.0.dist-info/METADATA +300 -0
- toolkit_ds-0.1.0.dist-info/RECORD +14 -0
- toolkit_ds-0.1.0.dist-info/WHEEL +5 -0
- toolkit_ds-0.1.0.dist-info/top_level.txt +1 -0
ds_toolkit/__init__.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from .singly_linked_list import SinglyLinkedList
|
|
2
|
+
from .doubly_linked_list import DoublyLinkedList
|
|
3
|
+
from .stack import Stack, LinkedListStack
|
|
4
|
+
from .queue import Queue, LinkedListQueue
|
|
5
|
+
from .heap import BinaryMinHeap
|
|
6
|
+
from .bst import BinarySearchTree
|
|
7
|
+
from .avl import AVLTree
|
|
8
|
+
from .union_find import UnionFind
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"SinglyLinkedList",
|
|
12
|
+
"DoublyLinkedList",
|
|
13
|
+
"Stack",
|
|
14
|
+
"LinkedListStack",
|
|
15
|
+
"Queue",
|
|
16
|
+
"LinkedListQueue",
|
|
17
|
+
"BinaryMinHeap",
|
|
18
|
+
"BinarySearchTree",
|
|
19
|
+
"AVLTree",
|
|
20
|
+
"UnionFind",
|
|
21
|
+
]
|
ds_toolkit/avl.py
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
class AVLNode:
|
|
2
|
+
"""Node class for AVL Tree."""
|
|
3
|
+
def __init__(self, value):
|
|
4
|
+
self.value = value
|
|
5
|
+
self.left = None
|
|
6
|
+
self.right = None
|
|
7
|
+
self.height = 1
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AVLTree:
|
|
11
|
+
"""AVL Tree implementation."""
|
|
12
|
+
|
|
13
|
+
def __init__(self):
|
|
14
|
+
self.root = None
|
|
15
|
+
|
|
16
|
+
def get_height(self, node):
|
|
17
|
+
"""Get the height of a node."""
|
|
18
|
+
if not node:
|
|
19
|
+
return 0
|
|
20
|
+
return node.height
|
|
21
|
+
|
|
22
|
+
def get_balance(self, node):
|
|
23
|
+
"""Get the balance factor of a node."""
|
|
24
|
+
if not node:
|
|
25
|
+
return 0
|
|
26
|
+
return self.get_height(node.left) - self.get_height(node.right)
|
|
27
|
+
|
|
28
|
+
def right_rotate(self, z):
|
|
29
|
+
"""Perform a right rotation."""
|
|
30
|
+
y = z.left
|
|
31
|
+
T3 = y.right
|
|
32
|
+
y.right = z
|
|
33
|
+
z.left = T3
|
|
34
|
+
|
|
35
|
+
# Update heights
|
|
36
|
+
z.height = 1 + max(self.get_height(z.left), self.get_height(z.right))
|
|
37
|
+
y.height = 1 + max(self.get_height(y.left), self.get_height(y.right))
|
|
38
|
+
|
|
39
|
+
return y
|
|
40
|
+
|
|
41
|
+
def left_rotate(self, z):
|
|
42
|
+
"""Perform a left rotation."""
|
|
43
|
+
y = z.right
|
|
44
|
+
T2 = y.left
|
|
45
|
+
y.left = z
|
|
46
|
+
z.right = T2
|
|
47
|
+
|
|
48
|
+
# Update heights
|
|
49
|
+
z.height = 1 + max(self.get_height(z.left), self.get_height(z.right))
|
|
50
|
+
y.height = 1 + max(self.get_height(y.left), self.get_height(y.right))
|
|
51
|
+
|
|
52
|
+
return y
|
|
53
|
+
|
|
54
|
+
def insert(self, value):
|
|
55
|
+
"""Insert a value into the AVL tree."""
|
|
56
|
+
self.root = self._insert(self.root, value)
|
|
57
|
+
|
|
58
|
+
def _insert(self, node, value):
|
|
59
|
+
"""Recursive function to insert a value."""
|
|
60
|
+
if not node:
|
|
61
|
+
return AVLNode(value)
|
|
62
|
+
|
|
63
|
+
if value < node.value:
|
|
64
|
+
node.left = self._insert(node.left, value)
|
|
65
|
+
else:
|
|
66
|
+
node.right = self._insert(node.right, value)
|
|
67
|
+
|
|
68
|
+
# Update the height of the node
|
|
69
|
+
node.height = 1 + max(self.get_height(node.left), self.get_height(node.right))
|
|
70
|
+
|
|
71
|
+
# Get the balance factor
|
|
72
|
+
balance = self.get_balance(node)
|
|
73
|
+
|
|
74
|
+
# Perform rotations to balance the tree
|
|
75
|
+
# Case 1: Left Left
|
|
76
|
+
if balance > 1 and value < node.left.value:
|
|
77
|
+
return self.right_rotate(node)
|
|
78
|
+
# Case 2: Right Right
|
|
79
|
+
if balance < -1 and value > node.right.value:
|
|
80
|
+
return self.left_rotate(node)
|
|
81
|
+
# Case 3: Left Right
|
|
82
|
+
if balance > 1 and value > node.left.value:
|
|
83
|
+
node.left = self.left_rotate(node.left)
|
|
84
|
+
return self.right_rotate(node)
|
|
85
|
+
# Case 4: Right Left
|
|
86
|
+
if balance < -1 and value < node.right.value:
|
|
87
|
+
node.right = self.right_rotate(node.right)
|
|
88
|
+
return self.left_rotate(node)
|
|
89
|
+
|
|
90
|
+
return node
|
|
91
|
+
|
|
92
|
+
def delete(self, value):
|
|
93
|
+
"""Delete a value from the AVL tree."""
|
|
94
|
+
if self.is_empty():
|
|
95
|
+
raise Exception("Cannot delete from an empty tree")
|
|
96
|
+
self.root = self._delete(self.root, value)
|
|
97
|
+
|
|
98
|
+
def is_empty(self):
|
|
99
|
+
return self.root is None
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _delete(self, node, value):
|
|
103
|
+
"""Recursive function to delete a value."""
|
|
104
|
+
if not node:
|
|
105
|
+
return node
|
|
106
|
+
|
|
107
|
+
# Perform standard BST delete
|
|
108
|
+
if value < node.value:
|
|
109
|
+
node.left = self._delete(node.left, value)
|
|
110
|
+
elif value > node.value:
|
|
111
|
+
node.right = self._delete(node.right, value)
|
|
112
|
+
else:
|
|
113
|
+
# Node with only one child or no child
|
|
114
|
+
if not node.left:
|
|
115
|
+
return node.right
|
|
116
|
+
elif not node.right:
|
|
117
|
+
return node.left
|
|
118
|
+
|
|
119
|
+
# Node with two children: Get the inorder successor
|
|
120
|
+
temp = self._get_min_value_node(node.right)
|
|
121
|
+
node.value = temp.value
|
|
122
|
+
node.right = self._delete(node.right, temp.value)
|
|
123
|
+
|
|
124
|
+
# Update the height of the node
|
|
125
|
+
node.height = 1 + max(self.get_height(node.left), self.get_height(node.right))
|
|
126
|
+
|
|
127
|
+
# Get the balance factor
|
|
128
|
+
balance = self.get_balance(node)
|
|
129
|
+
|
|
130
|
+
# Perform rotations to balance the tree
|
|
131
|
+
# Case 1: Left Left
|
|
132
|
+
if balance > 1 and self.get_balance(node.left) >= 0:
|
|
133
|
+
return self.right_rotate(node)
|
|
134
|
+
# Case 2: Right Right
|
|
135
|
+
if balance < -1 and self.get_balance(node.right) <= 0:
|
|
136
|
+
return self.left_rotate(node)
|
|
137
|
+
# Case 3: Left Right
|
|
138
|
+
if balance > 1 and self.get_balance(node.left) < 0:
|
|
139
|
+
node.left = self.left_rotate(node.left)
|
|
140
|
+
return self.right_rotate(node)
|
|
141
|
+
# Case 4: Right Left
|
|
142
|
+
if balance < -1 and self.get_balance(node.right) > 0:
|
|
143
|
+
node.right = self.right_rotate(node.right)
|
|
144
|
+
return self.left_rotate(node)
|
|
145
|
+
|
|
146
|
+
return node
|
|
147
|
+
|
|
148
|
+
def _get_min_value_node(self, node):
|
|
149
|
+
"""Get the node with the smallest value."""
|
|
150
|
+
current = node
|
|
151
|
+
while current.left:
|
|
152
|
+
current = current.left
|
|
153
|
+
return current
|
|
154
|
+
|
|
155
|
+
def inorder(self):
|
|
156
|
+
"""Perform inorder traversal and return the result as a list."""
|
|
157
|
+
result = []
|
|
158
|
+
self._inorder(self.root, result)
|
|
159
|
+
return result
|
|
160
|
+
|
|
161
|
+
def _inorder(self, node, result):
|
|
162
|
+
if not node:
|
|
163
|
+
return
|
|
164
|
+
self._inorder(node.left, result)
|
|
165
|
+
result.append(node.value)
|
|
166
|
+
self._inorder(node.right, result)
|
|
167
|
+
|
|
168
|
+
def preorder(self):
|
|
169
|
+
"""Perform preorder traversal and return the result as a list."""
|
|
170
|
+
result = []
|
|
171
|
+
self._preorder(self.root, result)
|
|
172
|
+
return result
|
|
173
|
+
|
|
174
|
+
def _preorder(self, node, result):
|
|
175
|
+
if not node:
|
|
176
|
+
return
|
|
177
|
+
result.append(node.value)
|
|
178
|
+
self._preorder(node.left, result)
|
|
179
|
+
self._preorder(node.right, result)
|
|
180
|
+
|
|
181
|
+
def postorder(self):
|
|
182
|
+
"""Perform postorder traversal and return the result as a list."""
|
|
183
|
+
result = []
|
|
184
|
+
self._postorder(self.root, result)
|
|
185
|
+
return result
|
|
186
|
+
|
|
187
|
+
def _postorder(self, node, result):
|
|
188
|
+
if not node:
|
|
189
|
+
return
|
|
190
|
+
self._postorder(node.left, result)
|
|
191
|
+
self._postorder(node.right, result)
|
|
192
|
+
result.append(node.value)
|
ds_toolkit/bst.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
class BSTNode:
|
|
2
|
+
"""Node class for a Binary Search Tree."""
|
|
3
|
+
def __init__(self, value):
|
|
4
|
+
self.value = value
|
|
5
|
+
self.left = None
|
|
6
|
+
self.right = None
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BinarySearchTree:
|
|
10
|
+
"""Binary Search Tree implementation."""
|
|
11
|
+
def __init__(self):
|
|
12
|
+
self.root = None
|
|
13
|
+
|
|
14
|
+
def insert(self, value):
|
|
15
|
+
"""Insert a value into the BST."""
|
|
16
|
+
if not self.root:
|
|
17
|
+
self.root = BSTNode(value)
|
|
18
|
+
else:
|
|
19
|
+
self._insert(self.root, value)
|
|
20
|
+
|
|
21
|
+
def _insert(self, node, value):
|
|
22
|
+
"""Recursive insertion."""
|
|
23
|
+
if value < node.value:
|
|
24
|
+
if not node.left:
|
|
25
|
+
node.left = BSTNode(value)
|
|
26
|
+
else:
|
|
27
|
+
self._insert(node.left, value)
|
|
28
|
+
elif value > node.value:
|
|
29
|
+
if not node.right:
|
|
30
|
+
node.right = BSTNode(value)
|
|
31
|
+
else:
|
|
32
|
+
self._insert(node.right, value)
|
|
33
|
+
|
|
34
|
+
def find(self, value):
|
|
35
|
+
"""Search for a value in the BST."""
|
|
36
|
+
return self._find(self.root, value)
|
|
37
|
+
|
|
38
|
+
def _find(self, node, value):
|
|
39
|
+
"""Recursive search."""
|
|
40
|
+
if not node:
|
|
41
|
+
return False
|
|
42
|
+
if value == node.value:
|
|
43
|
+
return True
|
|
44
|
+
elif value < node.value:
|
|
45
|
+
return self._find(node.left, value)
|
|
46
|
+
else:
|
|
47
|
+
return self._find(node.right, value)
|
|
48
|
+
|
|
49
|
+
def delete(self, value):
|
|
50
|
+
"""Delete a value from the BST."""
|
|
51
|
+
self.root = self._delete(self.root, value)
|
|
52
|
+
|
|
53
|
+
def _delete(self, node, value):
|
|
54
|
+
"""Recursive deletion."""
|
|
55
|
+
if not node:
|
|
56
|
+
return node
|
|
57
|
+
|
|
58
|
+
if value < node.value:
|
|
59
|
+
node.left = self._delete(node.left, value)
|
|
60
|
+
elif value > node.value:
|
|
61
|
+
node.right = self._delete(node.right, value)
|
|
62
|
+
else:
|
|
63
|
+
# Node with only one child or no child
|
|
64
|
+
if not node.left:
|
|
65
|
+
return node.right
|
|
66
|
+
elif not node.right:
|
|
67
|
+
return node.left
|
|
68
|
+
|
|
69
|
+
# Node with two children: Get the inorder successor
|
|
70
|
+
temp = self._min_value_node(node.right)
|
|
71
|
+
node.value = temp.value
|
|
72
|
+
node.right = self._delete(node.right, temp.value)
|
|
73
|
+
|
|
74
|
+
return node
|
|
75
|
+
|
|
76
|
+
def _min_value_node(self, node):
|
|
77
|
+
"""Get the node with the minimum value."""
|
|
78
|
+
current = node
|
|
79
|
+
while current.left:
|
|
80
|
+
current = current.left
|
|
81
|
+
return current
|
|
82
|
+
|
|
83
|
+
def inorder(self):
|
|
84
|
+
"""Perform an inorder traversal."""
|
|
85
|
+
result = []
|
|
86
|
+
self._inorder(self.root, result)
|
|
87
|
+
return result
|
|
88
|
+
|
|
89
|
+
def _inorder(self, node, result):
|
|
90
|
+
if node:
|
|
91
|
+
self._inorder(node.left, result)
|
|
92
|
+
result.append(node.value)
|
|
93
|
+
self._inorder(node.right, result)
|
|
94
|
+
|
|
95
|
+
def preorder(self):
|
|
96
|
+
"""Perform a preorder traversal."""
|
|
97
|
+
result = []
|
|
98
|
+
self._preorder(self.root, result)
|
|
99
|
+
return result
|
|
100
|
+
|
|
101
|
+
def _preorder(self, node, result):
|
|
102
|
+
if node:
|
|
103
|
+
result.append(node.value)
|
|
104
|
+
self._preorder(node.left, result)
|
|
105
|
+
self._preorder(node.right, result)
|
|
106
|
+
|
|
107
|
+
def postorder(self):
|
|
108
|
+
"""Perform a postorder traversal."""
|
|
109
|
+
result = []
|
|
110
|
+
self._postorder(self.root, result)
|
|
111
|
+
return result
|
|
112
|
+
|
|
113
|
+
def _postorder(self, node, result):
|
|
114
|
+
if node:
|
|
115
|
+
self._postorder(node.left, result)
|
|
116
|
+
self._postorder(node.right, result)
|
|
117
|
+
result.append(node.value)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
class DoublyLinkedListNode:
|
|
2
|
+
"""Node class for a doubly linked list."""
|
|
3
|
+
def __init__(self, value):
|
|
4
|
+
self.value = value
|
|
5
|
+
self.prev = None
|
|
6
|
+
self.next = None
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DoublyLinkedList:
|
|
10
|
+
"""Doubly Linked List implementation."""
|
|
11
|
+
def __init__(self):
|
|
12
|
+
self.head = None
|
|
13
|
+
self.tail = None
|
|
14
|
+
|
|
15
|
+
def push_front(self, value):
|
|
16
|
+
"""Insert a node at the front of the list."""
|
|
17
|
+
new_node = DoublyLinkedListNode(value)
|
|
18
|
+
if not self.head:
|
|
19
|
+
self.head = self.tail = new_node
|
|
20
|
+
else:
|
|
21
|
+
new_node.next = self.head
|
|
22
|
+
self.head.prev = new_node
|
|
23
|
+
self.head = new_node
|
|
24
|
+
|
|
25
|
+
def push_back(self, value):
|
|
26
|
+
"""Insert a node at the end of the list."""
|
|
27
|
+
new_node = DoublyLinkedListNode(value)
|
|
28
|
+
if not self.tail:
|
|
29
|
+
self.head = self.tail = new_node
|
|
30
|
+
else:
|
|
31
|
+
new_node.prev = self.tail
|
|
32
|
+
self.tail.next = new_node
|
|
33
|
+
self.tail = new_node
|
|
34
|
+
|
|
35
|
+
def pop_front(self):
|
|
36
|
+
"""Remove and return the node from the front of the list."""
|
|
37
|
+
if not self.head:
|
|
38
|
+
raise IndexError("Pop from empty list")
|
|
39
|
+
value = self.head.value
|
|
40
|
+
self.head = self.head.next
|
|
41
|
+
if self.head:
|
|
42
|
+
self.head.prev = None
|
|
43
|
+
else:
|
|
44
|
+
self.tail = None
|
|
45
|
+
return value
|
|
46
|
+
|
|
47
|
+
def pop_back(self):
|
|
48
|
+
"""Remove and return the node from the end of the list."""
|
|
49
|
+
if not self.tail:
|
|
50
|
+
raise IndexError("Pop from empty list")
|
|
51
|
+
value = self.tail.value
|
|
52
|
+
self.tail = self.tail.prev
|
|
53
|
+
if self.tail:
|
|
54
|
+
self.tail.next = None
|
|
55
|
+
else:
|
|
56
|
+
self.head = None
|
|
57
|
+
return value
|
|
58
|
+
|
|
59
|
+
def find(self, value):
|
|
60
|
+
"""Search for a node with the given value."""
|
|
61
|
+
current = self.head
|
|
62
|
+
while current:
|
|
63
|
+
if current.value == value:
|
|
64
|
+
return True
|
|
65
|
+
current = current.next
|
|
66
|
+
return False
|
|
67
|
+
|
|
68
|
+
def to_list(self):
|
|
69
|
+
"""Convert the linked list to a Python list."""
|
|
70
|
+
result = []
|
|
71
|
+
current = self.head
|
|
72
|
+
while current:
|
|
73
|
+
result.append(current.value)
|
|
74
|
+
current = current.next
|
|
75
|
+
return result
|
ds_toolkit/heap.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
class BinaryMinHeap:
|
|
2
|
+
"""Binary Min Heap implementation."""
|
|
3
|
+
|
|
4
|
+
def __init__(self):
|
|
5
|
+
self._heap = []
|
|
6
|
+
|
|
7
|
+
def _parent(self, index):
|
|
8
|
+
return (index - 1) // 2
|
|
9
|
+
|
|
10
|
+
def _left_child(self, index):
|
|
11
|
+
return 2 * index + 1
|
|
12
|
+
|
|
13
|
+
def _right_child(self, index):
|
|
14
|
+
return 2 * index + 2
|
|
15
|
+
|
|
16
|
+
def _swap(self, i, j):
|
|
17
|
+
self._heap[i], self._heap[j] = self._heap[j], self._heap[i]
|
|
18
|
+
|
|
19
|
+
def insert(self, value):
|
|
20
|
+
"""Insert a value into the heap."""
|
|
21
|
+
self._heap.append(value)
|
|
22
|
+
self._heapify_up(len(self._heap) - 1)
|
|
23
|
+
|
|
24
|
+
def _heapify_up(self, index):
|
|
25
|
+
"""Ensure the heap property is maintained while inserting."""
|
|
26
|
+
while index > 0 and self._heap[index] < self._heap[self._parent(index)]:
|
|
27
|
+
self._swap(index, self._parent(index))
|
|
28
|
+
index = self._parent(index)
|
|
29
|
+
|
|
30
|
+
def extract_min(self):
|
|
31
|
+
"""Remove and return the minimum value from the heap."""
|
|
32
|
+
if not self._heap:
|
|
33
|
+
raise IndexError("Extract from an empty heap")
|
|
34
|
+
if len(self._heap) == 1:
|
|
35
|
+
return self._heap.pop()
|
|
36
|
+
|
|
37
|
+
root = self._heap[0]
|
|
38
|
+
self._heap[0] = self._heap.pop()
|
|
39
|
+
self._heapify_down(0)
|
|
40
|
+
return root
|
|
41
|
+
|
|
42
|
+
def _heapify_down(self, index):
|
|
43
|
+
"""Ensure the heap property is maintained while extracting."""
|
|
44
|
+
smallest = index
|
|
45
|
+
left = self._left_child(index)
|
|
46
|
+
right = self._right_child(index)
|
|
47
|
+
|
|
48
|
+
if left < len(self._heap) and self._heap[left] < self._heap[smallest]:
|
|
49
|
+
smallest = left
|
|
50
|
+
if right < len(self._heap) and self._heap[right] < self._heap[smallest]:
|
|
51
|
+
smallest = right
|
|
52
|
+
|
|
53
|
+
if smallest != index:
|
|
54
|
+
self._swap(index, smallest)
|
|
55
|
+
self._heapify_down(smallest)
|
|
56
|
+
|
|
57
|
+
def get_min(self):
|
|
58
|
+
"""Return the minimum value without removing it."""
|
|
59
|
+
if not self._heap:
|
|
60
|
+
raise IndexError("Get from an empty heap")
|
|
61
|
+
return self._heap[0]
|
|
62
|
+
|
|
63
|
+
def size(self):
|
|
64
|
+
"""Return the size of the heap."""
|
|
65
|
+
return len(self._heap)
|
|
66
|
+
|
|
67
|
+
def is_empty(self):
|
|
68
|
+
"""Check if the heap is empty."""
|
|
69
|
+
return len(self._heap) == 0
|
|
70
|
+
|
|
71
|
+
def to_list(self):
|
|
72
|
+
"""Return the heap as a list."""
|
|
73
|
+
return self._heap[:]
|
ds_toolkit/queue.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
class Queue:
|
|
2
|
+
"""Queue implementation using a list."""
|
|
3
|
+
|
|
4
|
+
def __init__(self):
|
|
5
|
+
self._queue = []
|
|
6
|
+
|
|
7
|
+
def enqueue(self, value):
|
|
8
|
+
"""Add an element to the end of the queue."""
|
|
9
|
+
self._queue.append(value)
|
|
10
|
+
|
|
11
|
+
def dequeue(self):
|
|
12
|
+
"""Remove and return the element from the front of the queue."""
|
|
13
|
+
if self.is_empty():
|
|
14
|
+
raise IndexError("Dequeue from an empty queue")
|
|
15
|
+
return self._queue.pop(0)
|
|
16
|
+
|
|
17
|
+
def front(self):
|
|
18
|
+
"""Return the element at the front of the queue without removing it."""
|
|
19
|
+
if self.is_empty():
|
|
20
|
+
raise IndexError("Front from an empty queue")
|
|
21
|
+
return self._queue[0]
|
|
22
|
+
|
|
23
|
+
def is_empty(self):
|
|
24
|
+
"""Check if the queue is empty."""
|
|
25
|
+
return len(self._queue) == 0
|
|
26
|
+
|
|
27
|
+
def size(self):
|
|
28
|
+
"""Return the size of the queue."""
|
|
29
|
+
return len(self._queue)
|
|
30
|
+
|
|
31
|
+
def to_list(self):
|
|
32
|
+
"""Return the queue as a list."""
|
|
33
|
+
return self._queue[:]
|
|
34
|
+
|
|
35
|
+
class LinkedListQueue:
|
|
36
|
+
"""Queue implementation using a singly linked list."""
|
|
37
|
+
|
|
38
|
+
class Node:
|
|
39
|
+
"""Node class for the linked list."""
|
|
40
|
+
def __init__(self, data):
|
|
41
|
+
self.data = data
|
|
42
|
+
self.next = None
|
|
43
|
+
|
|
44
|
+
def __init__(self):
|
|
45
|
+
self.front = None
|
|
46
|
+
self.rear = None
|
|
47
|
+
self.size = 0
|
|
48
|
+
|
|
49
|
+
def is_empty(self):
|
|
50
|
+
"""Check if the queue is empty."""
|
|
51
|
+
return self.size == 0
|
|
52
|
+
|
|
53
|
+
def enqueue(self, value):
|
|
54
|
+
"""Add an element to the queue."""
|
|
55
|
+
new_node = self.Node(value)
|
|
56
|
+
if self.rear:
|
|
57
|
+
self.rear.next = new_node
|
|
58
|
+
self.rear = new_node
|
|
59
|
+
if self.front is None:
|
|
60
|
+
self.front = new_node
|
|
61
|
+
self.size += 1
|
|
62
|
+
|
|
63
|
+
def dequeue(self):
|
|
64
|
+
"""Remove and return the front element of the queue."""
|
|
65
|
+
if self.is_empty():
|
|
66
|
+
raise IndexError("Dequeue from empty queue")
|
|
67
|
+
value = self.front.data
|
|
68
|
+
self.front = self.front.next
|
|
69
|
+
if self.front is None:
|
|
70
|
+
self.rear = None
|
|
71
|
+
self.size -= 1
|
|
72
|
+
return value
|
|
73
|
+
|
|
74
|
+
def peek(self):
|
|
75
|
+
"""Return the front element without removing it."""
|
|
76
|
+
if self.is_empty():
|
|
77
|
+
raise IndexError("Peek from empty queue")
|
|
78
|
+
return self.front.data
|
|
79
|
+
|
|
80
|
+
def size(self):
|
|
81
|
+
"""Return the size of the queue."""
|
|
82
|
+
return self.size
|
|
83
|
+
|
|
84
|
+
def to_list(self):
|
|
85
|
+
"""Return the queue as a list."""
|
|
86
|
+
result = []
|
|
87
|
+
current = self.front
|
|
88
|
+
while current:
|
|
89
|
+
result.append(current.data)
|
|
90
|
+
current = current.next
|
|
91
|
+
return result
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
class SinglyLinkedListNode:
|
|
2
|
+
"""Node class for a singly linked list."""
|
|
3
|
+
def __init__(self, value):
|
|
4
|
+
self.value = value
|
|
5
|
+
self.next = None
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SinglyLinkedList:
|
|
9
|
+
"""Singly Linked List implementation."""
|
|
10
|
+
def __init__(self):
|
|
11
|
+
self.head = None
|
|
12
|
+
|
|
13
|
+
def push_front(self, value):
|
|
14
|
+
"""Insert a node at the front of the list."""
|
|
15
|
+
new_node = SinglyLinkedListNode(value)
|
|
16
|
+
new_node.next = self.head
|
|
17
|
+
self.head = new_node
|
|
18
|
+
|
|
19
|
+
def push_back(self, value):
|
|
20
|
+
"""Insert a node at the end of the list."""
|
|
21
|
+
new_node = SinglyLinkedListNode(value)
|
|
22
|
+
if not self.head:
|
|
23
|
+
self.head = new_node
|
|
24
|
+
else:
|
|
25
|
+
current = self.head
|
|
26
|
+
while current.next:
|
|
27
|
+
current = current.next
|
|
28
|
+
current.next = new_node
|
|
29
|
+
|
|
30
|
+
def pop_front(self):
|
|
31
|
+
"""Remove and return the node from the front of the list."""
|
|
32
|
+
if not self.head:
|
|
33
|
+
raise IndexError("Pop from empty list")
|
|
34
|
+
value = self.head.value
|
|
35
|
+
self.head = self.head.next
|
|
36
|
+
return value
|
|
37
|
+
|
|
38
|
+
def find(self, value):
|
|
39
|
+
"""Search for a node with the given value."""
|
|
40
|
+
current = self.head
|
|
41
|
+
while current:
|
|
42
|
+
if current.value == value:
|
|
43
|
+
return True
|
|
44
|
+
current = current.next
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
def to_list(self):
|
|
48
|
+
"""Convert the linked list to a Python list."""
|
|
49
|
+
result = []
|
|
50
|
+
current = self.head
|
|
51
|
+
while current:
|
|
52
|
+
result.append(current.value)
|
|
53
|
+
current = current.next
|
|
54
|
+
return result
|
|
55
|
+
|
|
56
|
+
def size(self):
|
|
57
|
+
"""Return the size of the list."""
|
|
58
|
+
count = 0
|
|
59
|
+
current = self.head
|
|
60
|
+
while current:
|
|
61
|
+
count += 1
|
|
62
|
+
current = current.next
|
|
63
|
+
return count
|
ds_toolkit/stack.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
class Stack:
|
|
2
|
+
"""Stack implementation using a list."""
|
|
3
|
+
|
|
4
|
+
def __init__(self):
|
|
5
|
+
self._stack = []
|
|
6
|
+
|
|
7
|
+
def push(self, value):
|
|
8
|
+
"""Push an element onto the stack."""
|
|
9
|
+
self._stack.append(value)
|
|
10
|
+
|
|
11
|
+
def pop(self):
|
|
12
|
+
"""Pop the top element from the stack."""
|
|
13
|
+
if self.is_empty():
|
|
14
|
+
raise IndexError("Pop from an empty stack")
|
|
15
|
+
return self._stack.pop()
|
|
16
|
+
|
|
17
|
+
def top(self):
|
|
18
|
+
"""Return the top element of the stack without removing it."""
|
|
19
|
+
if self.is_empty():
|
|
20
|
+
raise IndexError("Top from an empty stack")
|
|
21
|
+
return self._stack[-1]
|
|
22
|
+
|
|
23
|
+
def is_empty(self):
|
|
24
|
+
"""Check if the stack is empty."""
|
|
25
|
+
return len(self._stack) == 0
|
|
26
|
+
|
|
27
|
+
def size(self):
|
|
28
|
+
"""Return the size of the stack."""
|
|
29
|
+
return len(self._stack)
|
|
30
|
+
|
|
31
|
+
def to_list(self):
|
|
32
|
+
"""Return the stack as a list."""
|
|
33
|
+
return self._stack[:]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class LinkedListStack:
|
|
37
|
+
"""Stack implementation using a linked list."""
|
|
38
|
+
|
|
39
|
+
class Node:
|
|
40
|
+
"""A simple Node class for the linked list."""
|
|
41
|
+
def __init__(self, data):
|
|
42
|
+
self.data = data
|
|
43
|
+
self.next = None
|
|
44
|
+
|
|
45
|
+
def __init__(self):
|
|
46
|
+
self.top = None
|
|
47
|
+
self.size = 0
|
|
48
|
+
|
|
49
|
+
def is_empty(self):
|
|
50
|
+
"""Check if the stack is empty."""
|
|
51
|
+
return self.size == 0
|
|
52
|
+
|
|
53
|
+
def push(self, value):
|
|
54
|
+
"""Push an element onto the stack."""
|
|
55
|
+
new_node = self.Node(value)
|
|
56
|
+
new_node.next = self.top
|
|
57
|
+
self.top = new_node
|
|
58
|
+
self.size += 1
|
|
59
|
+
|
|
60
|
+
def pop(self):
|
|
61
|
+
"""Pop the top element from the stack."""
|
|
62
|
+
if self.is_empty():
|
|
63
|
+
raise IndexError("Pop from an empty stack")
|
|
64
|
+
popped_value = self.top.data
|
|
65
|
+
self.top = self.top.next
|
|
66
|
+
self.size -= 1
|
|
67
|
+
return popped_value
|
|
68
|
+
|
|
69
|
+
def top(self):
|
|
70
|
+
"""Return the top element of the stack without removing it."""
|
|
71
|
+
if self.is_empty():
|
|
72
|
+
raise IndexError("Top from an empty stack")
|
|
73
|
+
return self.top.data
|
|
74
|
+
|
|
75
|
+
def size(self):
|
|
76
|
+
"""Return the size of the stack."""
|
|
77
|
+
return self.size
|
|
78
|
+
|
|
79
|
+
def to_list(self):
|
|
80
|
+
"""Return the stack as a list."""
|
|
81
|
+
result = []
|
|
82
|
+
current = self.top
|
|
83
|
+
while current:
|
|
84
|
+
result.append(current.data)
|
|
85
|
+
current = current.next
|
|
86
|
+
return result
|
ds_toolkit/union_find.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
class UnionFind:
|
|
2
|
+
"""Union-Find (Disjoint Set Union) data structure with path compression and union by rank."""
|
|
3
|
+
|
|
4
|
+
def __init__(self, size):
|
|
5
|
+
"""Initialize the UnionFind data structure with `size` elements."""
|
|
6
|
+
self.parent = list(range(size)) # Each element is its own parent initially
|
|
7
|
+
self.rank = [1] * size # Rank to keep the tree flat
|
|
8
|
+
|
|
9
|
+
def find(self, x):
|
|
10
|
+
"""Find the representative (root) of the set that `x` belongs to."""
|
|
11
|
+
if self.parent[x] != x:
|
|
12
|
+
self.parent[x] = self.find(self.parent[x]) # Path compression
|
|
13
|
+
return self.parent[x]
|
|
14
|
+
|
|
15
|
+
def union(self, x, y):
|
|
16
|
+
"""Union the sets containing `x` and `y`."""
|
|
17
|
+
rootX = self.find(x)
|
|
18
|
+
rootY = self.find(y)
|
|
19
|
+
|
|
20
|
+
if rootX != rootY:
|
|
21
|
+
# Union by rank: attach the smaller tree under the larger tree
|
|
22
|
+
if self.rank[rootX] > self.rank[rootY]:
|
|
23
|
+
self.parent[rootY] = rootX
|
|
24
|
+
elif self.rank[rootX] < self.rank[rootY]:
|
|
25
|
+
self.parent[rootX] = rootY
|
|
26
|
+
else:
|
|
27
|
+
self.parent[rootY] = rootX
|
|
28
|
+
self.rank[rootX] += 1
|
|
29
|
+
|
|
30
|
+
def connected(self, x, y):
|
|
31
|
+
"""Check if elements `x` and `y` belong to the same set."""
|
|
32
|
+
return self.find(x) == self.find(y)
|
|
33
|
+
|
|
34
|
+
def to_list(self):
|
|
35
|
+
"""Return the list of parents for all elements."""
|
|
36
|
+
return self.parent
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) [2024] [Aravind]
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: toolkit_ds
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python package implementing core data structures and algorithms.
|
|
5
|
+
Home-page: https://github.com/ARAVIND281/ds_toolkit
|
|
6
|
+
Author: Aravind S
|
|
7
|
+
Author-email: aravind@inbo.tech
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.7
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
|
|
15
|
+
# DS Toolkit
|
|
16
|
+
|
|
17
|
+
DS Toolkit is a Python package providing implementations of common data structures such as Stack, Queue, Linked List, Binary Search Tree (BST), AVL Tree, and more. This toolkit helps developers easily use these data structures in their Python projects.
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- **Stack**: A simple implementation of a stack data structure using Python lists.
|
|
22
|
+
- **Queue**: A basic queue implementation with both list-based and linked list-based options.
|
|
23
|
+
- **Binary Search Tree (BST)**: An implementation of a binary search tree.
|
|
24
|
+
- **AVL Tree**: An implementation of a self-balancing binary search tree.
|
|
25
|
+
- **Union Find**: Data structure for handling disjoint sets.
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
You can install the `ds_toolkit` package directly from PyPI using pip:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install ds-toolkit
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Alternatively, if you want to install it directly from the source, clone the repository and use the following:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
git clone https://github.com/ARAVIND281/ds_toolkit.git
|
|
39
|
+
cd ds_toolkit
|
|
40
|
+
pip install .
|
|
41
|
+
```
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
### Stack
|
|
45
|
+
|
|
46
|
+
The stack is a simple Last In, First Out (LIFO) data structure. You can perform the following operations on the stack:
|
|
47
|
+
|
|
48
|
+
- **push(value)**: Adds a value to the top of the stack.
|
|
49
|
+
- **pop()**: Removes and returns the value at the top of the stack.
|
|
50
|
+
- **top()**: Returns the value at the top without removing it.
|
|
51
|
+
- **is_empty()**: Checks if the stack is empty.
|
|
52
|
+
- **size()**: Returns the number of elements in the stack.
|
|
53
|
+
- **to_list()**: Returns a list representation of the stack.
|
|
54
|
+
|
|
55
|
+
#### Example:
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from ds_toolkit.stack import Stack
|
|
59
|
+
|
|
60
|
+
stack = Stack()
|
|
61
|
+
|
|
62
|
+
# Push items onto the stack
|
|
63
|
+
stack.push(10)
|
|
64
|
+
stack.push(20)
|
|
65
|
+
|
|
66
|
+
# Pop the top item
|
|
67
|
+
print(stack.pop()) # Output: 20
|
|
68
|
+
|
|
69
|
+
# Peek at the top item
|
|
70
|
+
print(stack.top()) # Output: 10
|
|
71
|
+
|
|
72
|
+
# Check if the stack is empty
|
|
73
|
+
print(stack.is_empty()) # Output: False
|
|
74
|
+
|
|
75
|
+
# Get the size of the stack
|
|
76
|
+
print(stack.size()) # Output: 1
|
|
77
|
+
|
|
78
|
+
# Convert stack to list
|
|
79
|
+
print(stack.to_list()) # Output: [10]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Queue
|
|
83
|
+
|
|
84
|
+
The queue is a simple First In, First Out (FIFO) data structure. You can perform the following operations on the queue:
|
|
85
|
+
|
|
86
|
+
### Queue
|
|
87
|
+
|
|
88
|
+
A queue is a First In, First Out (FIFO) data structure. This means that elements are processed in the order in which they are added, with the first element added being the first one to be removed. The following operations are supported:
|
|
89
|
+
|
|
90
|
+
- **enqueue(value)**: Adds a value to the end of the queue.
|
|
91
|
+
- **dequeue()**: Removes and returns the value at the front of the queue.
|
|
92
|
+
- **front()**: Returns the value at the front of the queue without removing it.
|
|
93
|
+
- **is_empty()**: Checks if the queue is empty.
|
|
94
|
+
- **size()**: Returns the number of elements in the queue.
|
|
95
|
+
- **to_list()**: Returns the queue as a list.
|
|
96
|
+
|
|
97
|
+
#### Example:
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from ds_toolkit.queue import Queue
|
|
101
|
+
|
|
102
|
+
# Create a new Queue instance
|
|
103
|
+
queue = Queue()
|
|
104
|
+
|
|
105
|
+
# Enqueue items into the queue
|
|
106
|
+
queue.enqueue(10)
|
|
107
|
+
queue.enqueue(20)
|
|
108
|
+
queue.enqueue(30)
|
|
109
|
+
|
|
110
|
+
# Dequeue the first item (10)
|
|
111
|
+
print(queue.dequeue()) # Output: 10
|
|
112
|
+
|
|
113
|
+
# Check if the queue is empty
|
|
114
|
+
print(queue.is_empty()) # Output: False
|
|
115
|
+
|
|
116
|
+
# Get the size of the queue
|
|
117
|
+
print(queue.size()) # Output: 2
|
|
118
|
+
|
|
119
|
+
# Peek at the front item (20)
|
|
120
|
+
print(queue.front()) # Output: 20
|
|
121
|
+
|
|
122
|
+
# Convert the queue to a list
|
|
123
|
+
print(queue.to_list()) # Output: [20, 30]
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Binary Search Tree (BST)
|
|
127
|
+
|
|
128
|
+
A **Binary Search Tree (BST)** is a type of binary tree where each node has at most two children, and for each node:
|
|
129
|
+
|
|
130
|
+
- The left child has a value less than the node's value.
|
|
131
|
+
- The right child has a value greater than the node's value.
|
|
132
|
+
|
|
133
|
+
BST supports efficient search, insertion, and deletion operations, with an average time complexity of **O(log n)**.
|
|
134
|
+
|
|
135
|
+
#### Operations:
|
|
136
|
+
|
|
137
|
+
- **insert(value)**: Inserts a value into the BST.
|
|
138
|
+
- **search(value)**: Searches for a value in the BST and returns `True` if it exists, otherwise `False`.
|
|
139
|
+
- **delete(value)**: Deletes a node with the given value.
|
|
140
|
+
- **find_min()**: Returns the minimum value in the BST.
|
|
141
|
+
- **find_max()**: Returns the maximum value in the BST.
|
|
142
|
+
- **inorder_traversal()**: Returns a list of values in ascending order (left-root-right traversal).
|
|
143
|
+
- **preorder_traversal()**: Returns a list of values in preorder (root-left-right traversal).
|
|
144
|
+
- **postorder_traversal()**: Returns a list of values in postorder (left-right-root traversal).
|
|
145
|
+
- **height()**: Returns the height (max depth) of the tree.
|
|
146
|
+
|
|
147
|
+
#### Example:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
from ds_toolkit.bst import BinarySearchTree
|
|
151
|
+
|
|
152
|
+
# Create a new Binary Search Tree
|
|
153
|
+
bst = BinarySearchTree()
|
|
154
|
+
|
|
155
|
+
# Insert values into the BST
|
|
156
|
+
bst.insert(50)
|
|
157
|
+
bst.insert(30)
|
|
158
|
+
bst.insert(70)
|
|
159
|
+
bst.insert(20)
|
|
160
|
+
bst.insert(40)
|
|
161
|
+
bst.insert(60)
|
|
162
|
+
bst.insert(80)
|
|
163
|
+
|
|
164
|
+
# Search for a value
|
|
165
|
+
print(bst.search(40)) # Output: True
|
|
166
|
+
print(bst.search(25)) # Output: False
|
|
167
|
+
|
|
168
|
+
# Inorder traversal (sorted order)
|
|
169
|
+
print(bst.inorder_traversal()) # Output: [20, 30, 40, 50, 60, 70, 80]
|
|
170
|
+
|
|
171
|
+
# Find minimum and maximum values
|
|
172
|
+
print(bst.find_min()) # Output: 20
|
|
173
|
+
print(bst.find_max()) # Output: 80
|
|
174
|
+
|
|
175
|
+
# Delete a node
|
|
176
|
+
bst.delete(20) # Deletes the node with value 20
|
|
177
|
+
print(bst.inorder_traversal()) # Output: [30, 40, 50, 60, 70, 80]
|
|
178
|
+
|
|
179
|
+
# Get tree height
|
|
180
|
+
print(bst.height()) # Output: 3 (based on the tree's structure)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### AVL Tree
|
|
184
|
+
|
|
185
|
+
An **AVL Tree** is a self-balancing binary search tree (BST) where the difference in heights between the left and right subtrees (the balance factor) is at most 1 for all nodes. If the balance factor goes beyond this limit, the tree is rebalanced using rotations.
|
|
186
|
+
|
|
187
|
+
#### Operations:
|
|
188
|
+
|
|
189
|
+
- **insert(value)**: Inserts a value into the AVL tree while maintaining the balance.
|
|
190
|
+
- **delete(value)**: Deletes a node with the specified value while maintaining the balance.
|
|
191
|
+
- **search(value)**: Searches for a value in the AVL tree and returns `True` if found, otherwise `False`.
|
|
192
|
+
- **find_min()**: Returns the node with the minimum value.
|
|
193
|
+
- **find_max()**: Returns the node with the maximum value.
|
|
194
|
+
- **height()**: Returns the height (maximum depth) of the AVL tree.
|
|
195
|
+
- **inorder_traversal()**: Returns a list of values in ascending order (left-root-right traversal).
|
|
196
|
+
- **preorder_traversal()**: Returns a list of values in preorder (root-left-right traversal).
|
|
197
|
+
- **postorder_traversal()**: Returns a list of values in postorder (left-right-root traversal).
|
|
198
|
+
|
|
199
|
+
#### Example:
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
from ds_toolkit.avl import AVLTree
|
|
203
|
+
|
|
204
|
+
# Create a new AVL Tree
|
|
205
|
+
avl = AVLTree()
|
|
206
|
+
|
|
207
|
+
# Insert values into the AVL Tree
|
|
208
|
+
avl.insert(50)
|
|
209
|
+
avl.insert(30)
|
|
210
|
+
avl.insert(70)
|
|
211
|
+
avl.insert(20)
|
|
212
|
+
avl.insert(40)
|
|
213
|
+
avl.insert(60)
|
|
214
|
+
avl.insert(80)
|
|
215
|
+
|
|
216
|
+
# Search for a value
|
|
217
|
+
print(avl.search(40)) # Output: True
|
|
218
|
+
print(avl.search(25)) # Output: False
|
|
219
|
+
|
|
220
|
+
# Inorder traversal (sorted order)
|
|
221
|
+
print(avl.inorder_traversal()) # Output: [20, 30, 40, 50, 60, 70, 80]
|
|
222
|
+
|
|
223
|
+
# Find minimum and maximum values
|
|
224
|
+
print(avl.find_min()) # Output: 20
|
|
225
|
+
print(avl.find_max()) # Output: 80
|
|
226
|
+
|
|
227
|
+
# Delete a node
|
|
228
|
+
avl.delete(20) # Deletes the node with value 20
|
|
229
|
+
print(avl.inorder_traversal()) # Output: [30, 40, 50, 60, 70, 80]
|
|
230
|
+
|
|
231
|
+
# Get tree height
|
|
232
|
+
print(avl.height()) # Output: 3 (based on the tree's structure)
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Union Find (Disjoint Set Union - DSU)
|
|
236
|
+
|
|
237
|
+
The **Union Find** data structure efficiently handles the dynamic connectivity problem. It supports two primary operations:
|
|
238
|
+
1. **Union**: Merges two sets into one.
|
|
239
|
+
2. **Find**: Determines which set an element belongs to.
|
|
240
|
+
|
|
241
|
+
Union Find is commonly used for problems involving connectivity, such as determining connected components in a graph or finding the minimum spanning tree.
|
|
242
|
+
|
|
243
|
+
#### Operations:
|
|
244
|
+
|
|
245
|
+
- **find(x)**: Returns the representative (or leader) of the set containing element `x`.
|
|
246
|
+
- **union(x, y)**: Merges the sets containing `x` and `y`.
|
|
247
|
+
- **connected(x, y)**: Returns `True` if `x` and `y` are in the same set, otherwise `False`.
|
|
248
|
+
- **size(x)**: Returns the size of the set containing element `x`.
|
|
249
|
+
- **make_set(x)**: Initializes a set for a new element `x`.
|
|
250
|
+
|
|
251
|
+
#### Example:
|
|
252
|
+
|
|
253
|
+
```python
|
|
254
|
+
from ds_toolkit.union_find import UnionFind
|
|
255
|
+
|
|
256
|
+
# Create a UnionFind object
|
|
257
|
+
uf = UnionFind()
|
|
258
|
+
|
|
259
|
+
# Add elements to the UnionFind structure
|
|
260
|
+
uf.make_set(1)
|
|
261
|
+
uf.make_set(2)
|
|
262
|
+
uf.make_set(3)
|
|
263
|
+
uf.make_set(4)
|
|
264
|
+
uf.make_set(5)
|
|
265
|
+
|
|
266
|
+
# Perform union operations
|
|
267
|
+
uf.union(1, 2) # Union of 1 and 2
|
|
268
|
+
uf.union(3, 4) # Union of 3 and 4
|
|
269
|
+
uf.union(2, 3) # Union of 2 and 3 (now 1, 2, 3, and 4 are in the same set)
|
|
270
|
+
|
|
271
|
+
# Check if two elements are connected
|
|
272
|
+
print(uf.connected(1, 4)) # Output: True (since 1, 2, 3, and 4 are in the same set)
|
|
273
|
+
print(uf.connected(1, 5)) # Output: False (1 and 5 are in different sets)
|
|
274
|
+
|
|
275
|
+
# Find the representative of a set
|
|
276
|
+
print(uf.find(3)) # Output: 1 (since 1, 2, 3, and 4 are all connected, and 1 is the root)
|
|
277
|
+
|
|
278
|
+
# Check the size of a set
|
|
279
|
+
print(uf.size(1)) # Output: 4 (the set containing 1, 2, 3, and 4 has size 4)
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Author
|
|
283
|
+
|
|
284
|
+
This project is maintained by [Aravind S](https://github.com/ARAVIND281).
|
|
285
|
+
|
|
286
|
+
If you have any questions or suggestions, feel free to reach out via GitHub or through the issues section of the repository.
|
|
287
|
+
|
|
288
|
+
### About the Author
|
|
289
|
+
|
|
290
|
+
This project is developed by **Aravind S**, the founder of **Finmitr** and **Golden Crop**.
|
|
291
|
+
|
|
292
|
+
- GitHub: [@ARAVIND281](https://github.com/ARAVIND281)
|
|
293
|
+
- Email: [aravind@inbo.tech](mailto:aravind@inbo.tech)
|
|
294
|
+
- Website: [inbo.tech](https://inbo.tech)
|
|
295
|
+
|
|
296
|
+
You can connect with Aravind on [LinkedIn](https://www.linkedin.com/in/aravinds28/).
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
Thank you for exploring this project!
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
ds_toolkit/__init__.py,sha256=08Ocu5wYNMocZPGxslnNzObKOhg8PF_gUTTqQCoalBc,519
|
|
2
|
+
ds_toolkit/avl.py,sha256=UdzABvoavfJWFvGH5Tgon75HkCRKxT80I2KVyl-GKFw,5942
|
|
3
|
+
ds_toolkit/bst.py,sha256=7xjylhH2zo-5RLSKL0Qs8QMBhyrg9obFN0Hwzg4kIvk,3401
|
|
4
|
+
ds_toolkit/doubly_linked_list.py,sha256=pJBEdwUiGng0sSlblNGkZ_MnIUzbJ_i91of8W40XACc,2205
|
|
5
|
+
ds_toolkit/heap.py,sha256=S4j78Jp_gN3tEGujXCx1SJaE0nACO-YC23upiw_PC54,2229
|
|
6
|
+
ds_toolkit/queue.py,sha256=RP82eGM0VAHipXHTI5cxjl5x2IQPsWS3bJ-1zJQ3RpA,2552
|
|
7
|
+
ds_toolkit/singly_linked_list.py,sha256=JkBrP5qid_PWzn5Ddve_8IFVfqlgWc3ZGeTZqCfYkxQ,1798
|
|
8
|
+
ds_toolkit/stack.py,sha256=TSBuyFTqOH5BkHA-6vm8yH4ZmRJEyaRWyvf_SGRnnxM,2313
|
|
9
|
+
ds_toolkit/union_find.py,sha256=ltiO0b4RvtzRd_43FmJ0tVsk37zCK9injnWseryOgKQ,1392
|
|
10
|
+
toolkit_ds-0.1.0.dist-info/LICENSE,sha256=9Tgi4U7pC-bSjx9ChA2A_qii6qX2l5gQCRVgur4jF_w,1068
|
|
11
|
+
toolkit_ds-0.1.0.dist-info/METADATA,sha256=KuL28BOfWxH-rgwL-DwW5-gxx4KxWQtQjmZ7zvdF5Zo,9674
|
|
12
|
+
toolkit_ds-0.1.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
13
|
+
toolkit_ds-0.1.0.dist-info/top_level.txt,sha256=danCqjMv5Iw6sSBe0pawqg47jADt9EFm-_ztB09BeZA,11
|
|
14
|
+
toolkit_ds-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ds_toolkit
|