Skip to content
This repository has been archived by the owner on Apr 21, 2024. It is now read-only.

Commit

Permalink
Merge pull request #3 from MGTheTrain/feature/data-structures
Browse files Browse the repository at this point in the history
[Feature] - data structures
  • Loading branch information
MGTheTrain committed Apr 21, 2024
2 parents 8f6afd4 + 57aaa07 commit 2ac67b4
Show file tree
Hide file tree
Showing 21 changed files with 644 additions and 7 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.3.0] - 21-04-2024

### Added

- [Feature] - Basic data structures including their common operations (insertion, deletion, search) and practical applications in Python

### Updated

- Move python scripts to `design-patterns` folder

## [0.2.0] - 21-04-2024

### Added
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ run-executable:
# Target: run-python-script
.PHONY: run-python-script
run-python-script:
@python3 python/$(arg1)/$(arg2)/$(arg2).py
@python3 python/$(arg1)/$(arg2).py
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,29 @@ Curated list of design patterns, data structures, array sorting algorithms and t
| Singleton | Often used for managing resources centrally and ensuring a single instance of a class throughout the application | Single Responsibility Principle (SRP) | Creational |
| Builder | Commonly utilized for constructing complex objects step by step, improving clarity and maintainability in code that deals with object creation | Single Responsibility Principle (SRP) | Creational |

### Data structures

| Data Structure | Common Use Cases | C++ (STL) | Python |
|-------------------|-----------------------------------------------------------------------------------------------------------------------|------------------------------------------------------|------------------------------------------------------|
| Binary Tree | - Binary Search Trees (BSTs) for dictionary and associative array implementations. | `std::map`, `std::set`, `std::multimap`, `std::multiset` | `dict`, `set` |
| | - Sorting and searching algorithms like binary search. | `std::map`, `std::set`, `std::multimap`, `std::multiset`, `std::binary_search`, `std::lower_bound`, `std::upper_bound` | `sortedcontainers.SortedDict`, `sortedcontainers.SortedSet` |
| | - Expression trees for representing mathematical expressions. | `std::set`, `std::multiset` (for unique and duplicate keys) | Not directly available in the standard library, but libraries like `sortedcontainers` or `bintrees` can be used |
| | - Huffman coding for data compression. | - | - |
| Doubly Linked List| - LRU (Least Recently Used) cache implementation. | `std::list` | `collections.deque` |
| | - Undo functionality in text editors or software applications. | `std::list` | - |
| | - Scheduling algorithms like round-robin scheduling. | `std::list` | - |
| | - Implementing deque (double-ended queue) data structure. | `std::deque` | `collections.deque` |
| Dynamic Array | - Resizable array for storing elements. | `std::vector` | `list` |
| | - Implementing lists in programming languages (e.g., Python's list, C++'s std::vector, Java's ArrayList). | `std::vector` | `list` |
| | - Dynamic programming algorithms with varying input sizes. | `std::vector` | `list` |
| Graph | - Social network analysis representing users as nodes and relationships as edges. | Custom implementations or libraries like Boost.Graph | `networkx` |
| | - Network routing algorithms to find shortest paths. | Custom implementations or libraries like Boost.Graph | `networkx` |
| | - Modeling dependencies between tasks or processes in project management. | Custom implementations or libraries like Boost.Graph | `networkx` |
| Hash Table | - Associative arrays, dictionaries, or maps implementations. | `std::unordered_map`, `std::unordered_set`, `std::unordered_multimap`, `std::unordered_multiset` | `dict`, `set` |
| | - Efficient lookup, insertion, and deletion operations. | `std::unordered_map`, `std::unordered_set`, `std::unordered_multimap`, `std::unordered_multiset` | `dict`, `set` |
| | - Implementing symbol tables in compilers or interpreters. | - | - |
| | - Handling collisions using techniques like chaining or open addressing. | - | - |


## References

Expand All @@ -33,7 +56,7 @@ Curated list of design patterns, data structures, array sorting algorithms and t
- [x] Coding design patterns for design principles (e.g. SOLID) and their use cases in C++
- [x] Coding design patterns for design principles (e.g. SOLID) and their use cases in Python
- [ ] Efficient array sorting algorithms in terms of complexity in Python
- [ ] Basic data structures including their common operations (insertion, deletion, search) and associated complexities along with practical applications in Python
- [x] Basic data structures including their common operations (insertion, deletion, search) and practical applications in Python

## Getting started

Expand Down
9 changes: 5 additions & 4 deletions cpp/design-patterns/decorator/decorator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,14 @@ int main() {

std::cout << "\n";

std::shared_ptr<Component> anotherDecorated2 = std::make_shared<AnotherConcreteDecorator2>(anotherDecorated);
anotherDecorated2->operation();

std::cout << "\n";

std::shared_ptr<Component> anotherDecorated = std::make_shared<AnotherConcreteDecorator>(decorated);
anotherDecorated->operation();

std::cout << "\n";

std::shared_ptr<Component> anotherDecorated2 = std::make_shared<AnotherConcreteDecorator2>(anotherDecorated);
anotherDecorated2->operation();

return 0;
}
124 changes: 124 additions & 0 deletions python/data-structures/binary-tree.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
class TreeNode:
def __init__(self, data):
"""
TreeNode class represents each element in the binary tree.
"""
self.data = data # Data stored in the node
self.left = None # Reference to the left child node
self.right = None # Reference to the right child node

class BinaryTree:
def __init__(self):
"""
BinaryTree class represents the binary tree data structure.
"""
self.root = None # Reference to the root node
self.size = 0 # Number of elements in the tree

def __len__(self):
"""
Returns the number of elements in the tree.
"""
return self.size

def insert(self, data):
"""
Inserts an element into the binary tree.
"""
if self.root is None:
self.root = TreeNode(data)
else:
self._insert_recursively(self.root, data)
self.size += 1

def _insert_recursively(self, node, data):
"""
Helper method to recursively insert an element into the binary tree.
"""
if data < node.data:
if node.left is None:
node.left = TreeNode(data)
else:
self._insert_recursively(node.left, data)
elif data > node.data:
if node.right is None:
node.right = TreeNode(data)
else:
self._insert_recursively(node.right, data)

def _find_node(self, node, data):
"""
Helper method to find a node with the given data in the tree.
"""
if node is None or node.data == data:
return node
if data < node.data:
return self._find_node(node.left, data)
return self._find_node(node.right, data)

def search(self, data):
"""
Searches for an element in the binary tree.
"""
node = self._find_node(self.root, data)
return node is not None

def _find_min(self, node):
"""
Helper method to find the minimum element in the binary tree.
"""
while node.left is not None:
node = node.left
return node

def _delete_node(self, node, data):
"""
Helper method to delete a node with the given data from the binary tree.
"""
if node is None:
return node

if data < node.data:
node.left = self._delete_node(node.left, data)
elif data > node.data:
node.right = self._delete_node(node.right, data)
else:
if node.left is None:
return node.right
elif node.right is None:
return node.left
temp = self._find_min(node.right)
node.data = temp.data
node.right = self._delete_node(node.right, temp.data)
return node

def remove(self, data):
"""
Removes an element from the binary tree.
"""
if not self.search(data):
raise ValueError("Element not found in the tree")
self.root = self._delete_node(self.root, data)
self.size -= 1

def main():
"""
Main entry point.
"""
bst = BinaryTree()
bst.insert(5)
bst.insert(3)
bst.insert(7)
bst.insert(2)
bst.insert(4)
bst.insert(6)
bst.insert(8)

print("Size of the binary tree: ", len(bst))
print("Is 4 present in the tree? ", bst.search(4))
bst.remove(4)
print("Is 4 present in the tree after removal? ", bst.search(4))
print("Size of the binary tree after removal: ", len(bst))

if __name__ == "__main__":
main()
119 changes: 119 additions & 0 deletions python/data-structures/doubly-linked-list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
class Node:
def __init__(self, data):
"""
Node class represents each element in the doubly linked list.
"""
self.data = data # Data stored in the node
self.prev = None # Reference to the previous node
self.next = None # Reference to the next node

class DoublyLinkedList:
def __init__(self):
"""
DoublyLinkedList class represents the doubly linked list data structure.
"""
self.head = None # Reference to the head node
self.tail = None # Reference to the tail node
self.size = 0 # Number of elements in the list

def __len__(self):
"""
Returns the number of elements in the list.
"""
return self.size

def __getitem__(self, index):
"""
Returns the element at the specified index.
"""
if not 0 <= index < self.size:
raise IndexError('Index out of bounds')
current = self.head
for _ in range(index):
current = current.next
return current.data

def insert_at(self, index, item):
"""
Inserts an element at the specified index.
"""
if not 0 <= index <= self.size:
raise IndexError('Index out of bounds')
new_node = Node(item)
if index == 0:
# Insertion at the beginning
new_node.next = self.head
if self.head:
self.head.prev = new_node
self.head = new_node
if self.tail is None:
self.tail = new_node
elif index == self.size:
# Insertion at the end
self.tail.next = new_node
new_node.prev = self.tail
self.tail = new_node
else:
# Insertion at a specific index
current = self.head
for _ in range(index - 1):
current = current.next
new_node.next = current.next
current.next.prev = new_node
current.next = new_node
new_node.prev = current
self.size += 1

def remove_at(self, index):
"""
Removes the element at the specified index.
"""
if not 0 <= index < self.size:
raise IndexError('Index out of bounds')
if index == 0:
# Removal from the beginning
if self.size == 1:
# Special case: list has only one element
self.head = None
self.tail = None
else:
self.head = self.head.next
self.head.prev = None
elif index == self.size - 1:
# Removal from the end
self.tail = self.tail.prev
self.tail.next = None
else:
# Removal from a specific index
current = self.head
for _ in range(index):
current = current.next
current.prev.next = current.next
current.next.prev = current.prev
self.size -= 1

def main():
"""
Main entry point.
"""
dll = DoublyLinkedList()
dll.insert_at(0, 1)
dll.insert_at(1, 2)
dll.insert_at(2, 3) # insertion between
dll.insert_at(2, 5) # insertion between
dll.insert_at(2, 7) # insertion between
print("Length of the doubly linked list: ", len(dll))
print("Element at index 0: ", dll[0])
print("Element at index 1: ", dll[1])
print("Element at index 2: ", dll[2])
print("Element at index 3: ", dll[3])
print("Element at index 4: ", dll[4])
dll.remove_at(1)
dll.remove_at(3)
print("Length of the doubly linked list after removal: ", len(dll))
print("Element at index 0: ", dll[0])
print("Element at index 1: ", dll[1])
print("Element at index 1: ", dll[2])

if __name__ == "__main__":
main()
Loading

0 comments on commit 2ac67b4

Please sign in to comment.