diff --git a/.idea/csv-plugin.xml b/.idea/csv-plugin.xml index b8a2aa1..658ebc5 100644 --- a/.idea/csv-plugin.xml +++ b/.idea/csv-plugin.xml @@ -24,6 +24,13 @@ + + + + + + diff --git a/.idea/misc.xml b/.idea/misc.xml index a6fa933..cc266c9 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/.idea/netlinks.iml b/.idea/netlinks.iml index 049c994..80274ce 100644 --- a/.idea/netlinks.iml +++ b/.idea/netlinks.iml @@ -4,7 +4,7 @@ - + diff --git a/Algorithms/SearchingAlgorithms/Fig-11.jpg b/Algorithms/SearchingAlgorithms/Fig-11.jpg new file mode 100644 index 0000000..607db7b Binary files /dev/null and b/Algorithms/SearchingAlgorithms/Fig-11.jpg differ diff --git a/Algorithms/SearchingAlgorithms/README.md b/Algorithms/SearchingAlgorithms/README.md new file mode 100644 index 0000000..5d35cc0 --- /dev/null +++ b/Algorithms/SearchingAlgorithms/README.md @@ -0,0 +1,152 @@ +# Searching and Path Finding Algorithms + +## Searching Lists + +The list seaching algorithms can be used with `Lists`, `LinkedLists`, and `DoublyLinkedLists` + +### Linear Search + +```python +from structlinks.algorithms.searching_algorithms import linear_search + +# initalize a list +lst = [1, 100, 4, -1, 5] + +print(linear_search(lst, -1)) +# Output: True + +print(linear_search(lst, 0)) +# Output: False +``` + +### Binary Search + +Note that for binary search the list to be searched , should be `sorted` + +```python +from structlinks.algorithms.searching_algorithms import binary_search + +# initalize a sorted list +lst = [-1, 1, 4, 5, 100] + +print(binary_search(lst, -1)) +# Output: True + +print(binary_search(lst, 0)) +# Output: False +``` + +## Path Finding in Graphs + +### Algorithms For Un-Weighted Graphs + +#### Breadth First Search + +```python +from structlinks.structures import Graph +from structlinks.algorithms.searching_algorithms import breadth_first_search + +graph = Graph() + +graph.add_vertices([1, 2, 3, 5, 7]) +graph.add_edges([(1, 2), (2, 3), (3, 1), (3, 5)]) + +path = breadth_first_search(graph, origin = 1, target = 5) + +print(path) +# Output: [(1, 3), (3, 5)] # <---- Return the edges of the path (1 -> 3 -> 5) + +non_existing_path = breadth_first_search(graph, origin = 1, target = 7) # <-- 1 and 7 are not connected + +print(non_existing_path) +# Output: None +``` + +#### Depth First Search + +```python +from structlinks.structures import Graph +from structlinks.algorithms.searching_algorithms import depth_first_search + +graph = Graph() + +graph.add_vertices([1, 2, 3, 5]) +graph.add_edges([(1, 2), (2, 3), (3, 1), (3, 5)]) + +path = depth_first_search(graph, origin = 1, target = 5) + +print(path) +# Output: [(1, 3), (3, 5)] # <---- Return the edges of the path (1 -> 3 -> 5) + +non_existing_path = depth_first_search(graph, origin = 1, target = 7) # <-- 1 and 7 are not connected + +print(non_existing_path) +# Output: None +``` + +### Algorithms For Weighted Graphs + +```python +from structlinks.structures import Graph + +graph = Graph() + +graph.add_vertices([1, 2, 3, 5, 6, 7]) + +graph.add_edges([(0, 1, {'weight': 4}), + (2, 1, {'weight': 8}), + (2, 3, {'weight': 7}), + (3, 4, {'weight': 9}), + (4, 5, {'weight': 10}), + (5, 6, {'weight': 2}), + (6, 7, {'weight': 1}), + (7, 0, {'weight': 8}), + (7, 1, {'weight': 11}), + (7, 8, {'weight': 7}), + (6, 8, {'weight': 6}), + (8, 2, {'weight': 2}), + (2, 5, {'weight': 4}), + (3, 5, {'weight': 14})]) + +``` + +##### This is how the graph looks: + +![Graph](https://github.com/eeshannarula29/structlinks/blob/gh-pages/Fig-11.jpg) + +#### Dijkstra's Search Algorithm For Single Path + +This is Dijkstra's Search Algorithm for getting a path from an `origin` to a specific `target` + +```python +from structlinks.algorithms.searching_algorithms import dijkstra_search_target + +path = dijkstra_search_target(graph, origin = 0, target: 4, metric: 'weight') + +print(path) +# Output: [(0, 7), (7, 6), (6, 5), (5, 4)] # 0 -> 7 -> 6 -> 5 -> 4 +``` + +#### Dijkstra's Search Algorithm + +This is Dijkstra's Search Algorithm for getting a path from an `origin` to all the other vertices. + +```python +from structlinks.algorithms.searching_algorithms import dijkstra_search_target + +paths = dijkstra_search_all(graph, origin = 0, metric: 'weight') + +print(paths) +# Output: +{ + 1: [(0, 1)], + 2: [(0, 1), (1, 2)], + 3: [(0, 1), (1, 2), (2, 3)], + 4: [(0, 7), (7, 6), (6, 5), (5, 4)], + 5: [(0, 7), (7, 6), (6, 5)], + 6: [(0, 7), (7, 6)], + 7: [(0, 7)], + 8: [(0, 1), (1, 2), (2, 8)] +} + +``` diff --git a/SearchingAlgorithms.py b/Algorithms/SearchingAlgorithms/SearchingAlgorithms.py similarity index 96% rename from SearchingAlgorithms.py rename to Algorithms/SearchingAlgorithms/SearchingAlgorithms.py index 89f583d..0ad1272 100644 --- a/SearchingAlgorithms.py +++ b/Algorithms/SearchingAlgorithms/SearchingAlgorithms.py @@ -11,11 +11,11 @@ import math -from structlinks.Graph import Graph -from structlinks.Queue import Queue -from structlinks.Stack import Stack -from structlinks.LinkedList import LinkedList -from structlinks.DoublyLinkedList import DoublyLinkedList +from structlinks.DataStructures.LinkedList.LinkedList import LinkedList +from structlinks.DataStructures.DoublyLinkedList.DoublyLinkedList import DoublyLinkedList +from structlinks.DataStructures.Graph.Graph import Graph +from structlinks.DataStructures.Queue.Queue import * +from structlinks.DataStructures.Stack.Stack import Stack def linear_search(lst: Union[list, LinkedList, DoublyLinkedList], item: Any) -> bool: diff --git a/Algorithms/SearchingAlgorithms/__init__.py b/Algorithms/SearchingAlgorithms/__init__.py new file mode 100644 index 0000000..915a6f2 --- /dev/null +++ b/Algorithms/SearchingAlgorithms/__init__.py @@ -0,0 +1 @@ +import structlinks.Algorithms.SearchingAlgorithms.SearchingAlgorithms as SearchingAlgorithms \ No newline at end of file diff --git a/Algorithms/SortingAlgorithms/README.md b/Algorithms/SortingAlgorithms/README.md new file mode 100644 index 0000000..d1fe443 --- /dev/null +++ b/Algorithms/SortingAlgorithms/README.md @@ -0,0 +1,158 @@ +# Sorting Algorithms + +All the sorting algorithms in this module work with `lists`, `LinkedLists` and `DoublyLinkedLists` + +## MergeSort-InPlace + +Use an inplace mergesort algorithm to return a sorted list. +This version is inferior (in terms of running time) to the non-mutating implementation of mergesort. + +```python +from structlinks.algorithms.sorting_algorithms import mergesort + +# initialize a list +lst = [1, 100, 50, 20, 4] +# make a sorted list +return_value = mergesort(lst) + +print(lst) +# Output: +# [1, 4, 20, 50, 100] + +print(return_value) +# Output: +# None +``` + +## MergeSort-NonMutating + +Use mergesort algorithm to return a sorted list. + +```python +from structlinks.algorithms.sorting_algorithms import no_mut_mergesort + +# initialize a list +lst = [1, 100, 50, 20, 4] +# make a sorted list +sorted_lst = no_mut_mergesort(lst) + +print(lst) +# Output: +# [1, 100, 50, 20, 4] + +print(sorted_lst) +# Output: +# [1, 4, 20, 50, 100] +``` + +## QuickSort-InPlace + +Use quicksort algorithm to return a sorted list. This is a _mutating_ method: it modifies the input instead of returning an output. + +```python +from structlinks.algorithms.sorting_algorithms import quicksort + +# initialize a list +lst = [1, 100, 50, 20, 4] +# make a sorted list +return_value = quicksort(lst) + +print(lst) +# Output: +# [1, 4, 20, 50, 100] + +print(return_value) +# Output: +# None +``` + +## QuickSort-NonMutating + +Use quicksort algorithm to return a sorted list. This is a _non-mutating_ method: the input list will be preserved. +Note that the runtime of this version is technically inferior to the mutating version of quicksort, above. + +```python +from structlinks.algorithms.sorting_algorithms import no_mut_quicksort + +# initialize a list +lst = [1, 100, 50, 20, 4] +# make a sorted list +sorted_lst = no_mut_quicksort(lst) + +print(lst) +# Output: +# [1, 100, 50, 20, 4] + +print(sorted_lst) +# Output: +# [1, 4, 20, 50, 100] +``` + +## SelectionSort + +Use the selection sort algorithm to return a sorted list. This is a mutating method that changes the input list. + +```python +from structlinks.algorithms.sorting_algorithms import selection_sort + +# initialize a list +lst = [1, 100, 50, 20, 4] +# make a sorted list +return_value = selection_sort(lst) + +print(lst) +# Output: +# [1, 4, 20, 50, 100] + +print(return_value) +# Output: +# None +``` + +## InsertionSort + +Use the insertion sort algorithm to return a sorted list. Like selection sort, this is a mutating algorithm that modifies it's input + +```python +from structlinks.algorithms.sorting_algorithms import insertion_sort + +# initialize a list +lst = [1, 100, 50, 20, 4] +# make a sorted list +return_value = insertion_sort(lst) + +print(lst) +# Output: +# [1, 4, 20, 50, 100] + +print(return_value) +# Output: +# None +``` + +# Sorting Algorithms: The Key Parameter + +Each sorting algorithm accepts an optional `key` parameter: pass in a function to adjust the weighting scheme (or control the values of) of the elements in the list. + +For example, if we wanted to sort the list from largest to smallest (rather than smallest to largest, as is default), we can: + +```python +from structlinks.algorithms.sorting_algorithms import insertion_sort + +# Define a function that reverses the weighting of integers +def invert(x: int) -> int: + return -x + +# initialize a list +lst = [1, 100, 50, 20, 4] +# Sort the list, passing in the function as a key +return_value = insertion_sort(lst, key=invert) + +print(lst) +# Output: +# [100, 50, 20, 4, 1] + +print(sorted_lst) +# Output: +# None +``` diff --git a/SortingAlgorithms.py b/Algorithms/SortingAlgorithms/SortingAlgorithms.py similarity index 98% rename from SortingAlgorithms.py rename to Algorithms/SortingAlgorithms/SortingAlgorithms.py index dc33539..7e1f03f 100644 --- a/SortingAlgorithms.py +++ b/Algorithms/SortingAlgorithms/SortingAlgorithms.py @@ -14,8 +14,8 @@ has a default value for key, (lambda x: x) which does not modify it's input """ from typing import Any, Callable, Union -from structlinks.DoublyLinkedList import DoublyLinkedList -from structlinks.LinkedList import LinkedList +from structlinks.DataStructures.LinkedList.LinkedList import LinkedList +from structlinks.DataStructures.DoublyLinkedList.DoublyLinkedList import DoublyLinkedList ######################################## diff --git a/Algorithms/SortingAlgorithms/__init__.py b/Algorithms/SortingAlgorithms/__init__.py new file mode 100644 index 0000000..8fbbb18 --- /dev/null +++ b/Algorithms/SortingAlgorithms/__init__.py @@ -0,0 +1 @@ +import structlinks.Algorithms.SortingAlgorithms.SortingAlgorithms as SortingAlgorithms \ No newline at end of file diff --git a/Algorithms/__init__.py b/Algorithms/__init__.py new file mode 100644 index 0000000..2debced --- /dev/null +++ b/Algorithms/__init__.py @@ -0,0 +1,2 @@ +import structlinks.Algorithms.SearchingAlgorithms.SearchingAlgorithms as searching_algorithms +import structlinks.Algorithms.SortingAlgorithms.SortingAlgorithms as sorting_algorithms \ No newline at end of file diff --git a/BinaryTree.py b/DataStructures/BinarySearchTree/BinarySearchTree.py similarity index 100% rename from BinaryTree.py rename to DataStructures/BinarySearchTree/BinarySearchTree.py diff --git a/DataStructures/BinarySearchTree/README.md b/DataStructures/BinarySearchTree/README.md new file mode 100644 index 0000000..66f7a24 --- /dev/null +++ b/DataStructures/BinarySearchTree/README.md @@ -0,0 +1,392 @@ +# BinarySearchTrees + +## Initialize a BST + +BST can be initialized in two ways, first by a list, which makes a balanced binary search tree or +by constructing a tree from scratch. + +### From list + +```python +from structlinks.structures import BinarySearchTree + +bst = BinarySearchTree.create_tree([1, 2, 4, 10, 20, 30, 3]) + +print(bst) + +# Output: +# _4___ +# / \ +# 2 20_ +# / \ / \ +# 1 3 10 30 +``` + +### From Scratch + +```python +from structlinks.structures import BinarySearchTree + +bst = BinarySearchTree(7) + +left = BinarySearchTree(3) +left.set_left_to(BinarySearchTree(2)) +left.set_right_to(BinarySearchTree(5)) + +right = BinarySearchTree(11) +right.set_left_to(BinarySearchTree(9)) +right.set_right_to(BinarySearchTree(13)) + +bst.set_left_to(left) +bst.set_right_to(right) + +print(bst) + +# Output: +# _7__ +# / \ +# 3 11_ +# / \ / \ +# 2 5 9 13 +``` + +## Printing BST + +### Branched Form + +```python +from structlinks.structures import BinarySearchTree + +bst = BinarySearchTree.create_tree([1, 2, 4, 10, 20, 30, 3]) + +bst.display() + +# Output: +# _4___ +# / \ +# 2 20_ +# / \ / \ +# 1 3 10 30 + +# or instead + +print(bst) + +# Output: +# _4___ +# / \ +# 2 20_ +# / \ / \ +# 1 3 10 30 +``` + +### Indented Form + +```python +from structlinks.structures import BinarySearchTree + +bst = BinarySearchTree.create_tree([1, 2, 4, 10, 20, 30, 3]) + +bst.display(indented = True) + + # Output: + # |->4 + # |->2 + # |->1 + # |->3 + # |->20 + # |->10 + # |->30 +``` + +## Properties + +```python +from structlinks.structures import BinarySearchTree + +bst = BinarySearchTree.create_tree([1, 2, 4, 10, 20]) + +print(bst) + +# Output: +# 4___ +# / \ +# 2 20 +# / / +# 1 10 + +print(bst.root) # root value +# Output: +# 4 + +print(bst.left) # copy of left child +# Output: +# 2 +# / +# 1 + +print(bst.right) # copy of right child +# Output: +# 20 +# / +# 10 + +print(bst.height) # height of the tree +# Output: +# 3 + +print(bst.is_balanced) # Weather tree is balanced +# Output: +# True +``` + +## Insert Item in BST + +```python +from structlinks.structures import BinarySearchTree + +bst = BinarySearchTree.create_tree([1, 2, 4, 10, 20]) + +print(bst) + +# Output: +# 4___ +# / \ +# 2 20 +# / / +# 1 10 + +bst.insert(3) + +print(bst) + +# Output: +# _4___ +# / \ +# 2 20 +# / \ / +# 1 3 10 +``` + +## Remove Item from BST + +```python +from structlinks.structures import BinarySearchTree + +bst = BinarySearchTree.create_tree([1, 2, 3, 4, 5]) + +print(bst) + +# Output: +# 3_ +# / \ +# 2 5 +# / / +# 1 4 + +bst.remove(2) + +print(bst) + +# Output: +# 3_ +# / \ +# 1 5 +# / +# 4 +``` + +## Convert to List + +```python +from structlinks.structures import BinarySearchTree + +bst = BinarySearchTree.create_tree([2, 1, 3, 5, 4]) + +print(bst.to_list()) + +# Output: +# [1, 2, 3, 4, 5] +``` + +## Item in BST + +```python +from structlinks.structures import BinarySearchTree + +bst = BinarySearchTree.create_tree([2, 1, 3, 3, 5, 4]) + +print(3 in bst) +# Output: +# True + +print(100 in bst) +# Output: +# False +``` + +## Invert the BST + +```python +from structlinks.structures import BinarySearchTree + +bst = BinarySearchTree.create_tree([1, 2, 3, 4, 5]) + +print(bst) + +# Output: +# 3_ +# / \ +# 2 5 +# / / +# 1 4 + +bst.invert() + +print(bst) + +# Output: +# _3 +# / \ +# 5 2 +# \ \ +# 4 1 +``` + +## Balance the BST + +```python +from structlinks.structures import BinarySearchTree + +bst = BinarySearchTree(5) +right = BinarySearchTree(6) +right.set_right_to(BinarySearchTree(7)) +bst.set_right_to(right) + +print(bst) + +# Output: +# 5 +# \ +# 6 +# \ +# 7 + + +bst.balance() + +print(bst) + +# Output: +# 6 +# / \ +# 5 7 +``` + +## Mapping a BST + +### Mutate existing tree + +```python +from structlinks.structures import BinarySearchTree + +bst = BinarySearchTree.create_tree([1, 2, 3, 4, 5]) + +print(bst) + +# Output: +# 3_ +# / \ +# 2 5 +# / / +# 1 4 + +bst.apply(lambda x: x ** 2) # <---- mapping x to x^2 + +print(bst) + +# Output: +# 9___ +# / \ +# 4 25 +# / / +# 1 16 +``` + +### Creating new Mapped BST + +```python +from structlinks.structures import BinarySearchTree + +bst = BinarySearchTree.create_tree([1, 2, 3, 4, 5]) + +print(bst) + +# Output: +# 3_ +# / \ +# 2 5 +# / / +# 1 4 + +mapped = bst.map(lambda x: x ** 2) # <---- mapping x to x^2 + +print(mapped) + +# Output: +# 9___ +# / \ +# 4 25 +# / / +# 1 16 + +print(bst) # bst did not change + +# Output: +# 3_ +# / \ +# 2 5 +# / / +# 1 4 +``` + +## Some other methods + +```python +from structlinks.structures import BinarySearchTree + +bst = BinarySearchTree.create_tree([1, 2, 3, 4, 5]) + +print(bst) + +# Output: +# 3_ +# / \ +# 2 5 +# / / +# 1 4 + +# Maximum item in the tree +print(bst.maximum()) +# Output: +# 5 + +# Minimum item in the tree +print(bst.mimimum()) +# Output: +# 1 + +# Copy BST +print(bst.copy()) +# Output: +# 3_ +# / \ +# 2 5 +# / / +# 1 4 + +# abs, floor, and ceil +abs_bst = bst.abs() # <- map abs to all element of bst +floor_bst = bst.floor() # <- map floor to all element of bst +ceil_bst = bst.ceil() # <- map ceil to all element of bst +``` diff --git a/DataStructures/BinarySearchTree/__init__.py b/DataStructures/BinarySearchTree/__init__.py new file mode 100644 index 0000000..39431a0 --- /dev/null +++ b/DataStructures/BinarySearchTree/__init__.py @@ -0,0 +1 @@ +from structlinks.DataStructures.BinarySearchTree.BinarySearchTree import BinarySearchTree \ No newline at end of file diff --git a/DoublyLinkedList.py b/DataStructures/DoublyLinkedList/DoublyLinkedList.py similarity index 100% rename from DoublyLinkedList.py rename to DataStructures/DoublyLinkedList/DoublyLinkedList.py diff --git a/DataStructures/DoublyLinkedList/README.md b/DataStructures/DoublyLinkedList/README.md new file mode 100644 index 0000000..836bf06 --- /dev/null +++ b/DataStructures/DoublyLinkedList/README.md @@ -0,0 +1,143 @@ +# DoublyLinkedLists + +## Initializing a Doubly Linked list + +You can initialize a Doubly linked list by simply creating a Doubly linked list object +and optionally pass in a list object witch would contain the initial list +items. + +```python +from structlinks.structures import DoublyLinkedList + +# create an empty linked list +lst = DoublyLinkedList() +# create a linked list with initial values +lst = DoublyLinkedList([1, 10, -3, 5]) +``` + +## Basic Operations + +The idea for all of the basic operations is similar to that of Linked lists, which were taken +from the University of Toronto's CSC111 course. + +```python +from structlinks.structures import DoublyLinkedList + +lst = DoublyLinkedList([1, 10, 3, 5]) + +# print the list +print(lst) # this will print : [1 <--> 10 <--> 3 <--> 5] + +# Length of the list +length = len(lst) # length = 4 + +# Append items +lst.append(2) # After the call, lst = [1 <--> 10 <--> 3 <--> 5 <--> 2] + +# Insert items +lst.insert(0, 200) # After the call, lst = [200 <--> 1 <--> 10 <--> 3 <--> 5 <--> 2] + +# Get & Set item by index +item = lst[1] # here item = 10 +lst[2] = 100 # set element at index 2 to be 100, so lst = [200 <--> 1 <--> 100 <--> 3 <--> 5 <--> 2] + +# Slicing the list +part = lst[2:] # here part = [100 <--> 3 <--> 5 <--> 2] + +# Removing element +# 1) by index +popped = lst.pop(0) # here popped = 200, and lst = [1 <--> 100 <--> 3 <--> 5 <--> 2] +# 2) by item value +lst.remove(1) # now list lst = [100 <--> 3 <--> 5 <--> 2] + +# Checking for Element +cond1 = 2 in lst # here cond1 is True as 2 is in lst +cond2 = 1 in lst # here cond2 is False as 1 is not in lst + +# Adding to Doubly Linked Lists +lst1 = DoublyLinkedList([1, 10, 3, 5]) +lst2 = DoublyLinkedList([2, 100, 4, 0]) +lst3 = lst1 + lst2 # lst3 = [1 <--> 10 <--> 3 <--> 5 <--> 2 <--> 100 <--> 4 <--> 0] + +# Count of an item +count = lst.count(2) # count = 1 as 2 appears only once + +# Index of an item (return -1 if element not in lst) +index = lst.index(100) # index = 0 as 100 is at index 0 + +# Equating to lists +cond3 = lst1 == lst2 # cond3 = False as lst1 and lst2 have different elements + +# Sort the list +lst.sort() # lst will be mutated to [2 <--> 3 <--> 5 <--> 100] + +# Extend the list +lst.extend(lst2) # lst will be mutated to [2 <--> 3 <--> 5 <--> 100 <--> 2 <--> 100 <--> 4 <--> 0] + +# Copy the list +lst4 = lst.copy() # lst4 is a copy of lst +``` + +## Additional Operations + +### Inverting a Doubly Linked List + +```python +from structlinks.structures import DoublyLinkedList + +lst = DoublyLinkedList([1, 10, 3, 5]) + +print(lst) +# Output: +# [1 <--> 10 <--> 3 <--> 5] + +lst.invert() # mutate lst + +print(lst) +# Output: +# [5 <--> 3 <--> 10 <--> 1] +``` + +### Mapping functions to a Doubly Linked List + +```python +from structlinks.structures import DoublyLinkedList + +lst = DoublyLinkedList([1, 10, 3, 5]) + +# Map function f(x) = x^2 +new_lst = lst.map(lambda x: x ** 2) + +print(new_lst) +# Output: +# [25 <--> 9 <--> 100 <--> 1] +``` + +### Inbuilt Mapping functions + +```python +from structlinks.structures import DoublyLinkedList + +lst = DoublyLinkedList([1.1, 10.5, -3.7, 5.2]) + +#abs +abs_lst = lst.abs() + +print(abs_lst) +# Output: +# [1.1 <--> 10.5 <--> 3.7 <--> 5.2] + +# floor +floor_lst = lst.floor() + +print(floor_lst) +# Output: +# [1.0 <--> 10.0 <--> 3.0 <--> 5.0] + +# ceil +ceil_lst = lst.ceil() + +print(ceil_lst) +# Output: +# [2.0 <--> 11.0 <--> 4.0 <--> 6.0] +``` diff --git a/DataStructures/DoublyLinkedList/__init__.py b/DataStructures/DoublyLinkedList/__init__.py new file mode 100644 index 0000000..5135407 --- /dev/null +++ b/DataStructures/DoublyLinkedList/__init__.py @@ -0,0 +1 @@ +from structlinks.DataStructures.DoublyLinkedList.DoublyLinkedList import * \ No newline at end of file diff --git a/Graph.py b/DataStructures/Graph/Graph.py similarity index 100% rename from Graph.py rename to DataStructures/Graph/Graph.py diff --git a/DataStructures/Graph/README.md b/DataStructures/Graph/README.md new file mode 100644 index 0000000..b2860f8 --- /dev/null +++ b/DataStructures/Graph/README.md @@ -0,0 +1,356 @@ +# Graphs + +## Initialize a Graph + +```python +from structlinks.structures import Graph + +graph = Graph() +``` + +## Adding Vertices + +### Adding a Single Vertex + +A vertex can we added simply by calling `graph.add_vertex`. The function takes in the value of the vertex. +Additional `Attributes` of the vertex can be passed in as a dictionary. + +```python +from structlinks.structures import Graph + +graph = Graph() + +# Adding a vertex +graph.add_vertex(2) + +# Add vertex with attributes +graph.add_vertex(3, attributes={'type': int, 'is active': True}) +``` + +A error would **not** be raised if a vertex value is added again, instead the attributes +of the vertex would be updated by the attributes of the new call. + +### Adding Multiple Vertices + +Multiple vertices could be added using the `graph.add_vertices` function, which takes in a list of elements. To +add vertices with attributes the format of the tuple should be `[ ... (value, attribute dictionary) ... ]`. A list +contain both elements with no attributes and elements with attributes. + +```python +from structlinks.structures import Graph + +graph = Graph() + +graph.add_vertices([2, (3, {'type': int, 'is active': True}), 5, ('string', {'type': str})]) + +print(graph.vertices) +# Output: +# {2, 3, 5, 'string'} +``` + +Similar to `add_vertex` if a vertex is added with existing value, the attributes of the vertex would be updated and +no error would be raised. + +Note that a vertex can be anything, as shown in the above example. It could be a number, string, dictionary or even +a graph object itself. + +## Adding Edges + +### Adding a Single Edge + +An edge can be added using the `add_edge` function. Similar to vertices, edges can have attributes which we can pass in as +an attribute. Note that if a vertex does not exists then the function does **not** raise an error, instead it creates +a new vertex with that value, and adds an edge. Note That if an edge already exists, then similar to vertices, the `attributes` +of the edge would be update if `add_edge` is called. + +```python +from structlinks.structures import Graph + +graph = Graph() + +# Add vertices +graph.add_vertices([2, 1, 3, 5]) + +# Add edge +graph.add_edge(2, 1) + +# Add edge with attributes +graph.add_edge(1, 3, attributes={'weight': 100}) + +# Add edge between non existing vertex +graph.add_edge(3, 100) + +print(graph.edges) +# Output: +# {(2, 1), (1, 3), (3, 100)} +``` + +Note that `100` was not a vertex, but the function created a new vertex with value `100`, and add an edge between `3` and `100`. + +### Adding multiple Edges + +Multiple edges can be added using `add_edges` function. The function takes in a list of tuples, where each tuple represents +an edge. The format of the tuple could `[... (item1, item2) ...]` , and we can additionally add attributes to the edge +which would look like `[... (item1, item2, attributes dict) ...]`. Note that similar to `add edge`, if a vertex does not exists then the function does **not** raise an error, instead it creates +a new vertex with that value, and adds an edge. + +```python +from structlinks.structures import Graph + +graph = Graph() + +# Add vertices +graph.add_vertices([2, 1, 3, 5]) + +# Add edges +graph.add_edges([(2, 1), (1, 3, {'weight': 100}), (5, 25)]) + +print(graph.edges) +# Output: +# {(2, 1), (1, 3), (5, 25)} +``` + +## Clear the Graph + +A graph can be cleared using the `clear` function. This would remove all the vertices and edges of the graph. + +```python +graph.clear() +``` + +## Examining elements of a graph + +We can read and examine the graph with some helpful function. As you have seen in a few examples above +`graph.vertices` and `graph.edges` are two properties used to get the unique set of vertices and edges. + +To get the number of vertices and edges you can call `graph.vertices_count()` and `graph.edges_count()`. + +Other functions are `graph.get_neighbours`, `graph.adjacent`, `graph.get_degree` + +```python +print(graph.vertices) +# Output: {2, 1, 3, 5} + +print(graph.edges) +# Output: {(2, 1), (1, 3)} + +print(graph[1].get_neighbours()) # Same as graph.get_neighbours(1) +# Output: {2, 3} + +print(graph[2].adjacent(1)) # Same as graph.adjacent(2, 1) or graph.adjacent(1, 2) +# Output: True + +print(graph[5].degree) # Same as graph.get_degree(5) +# Output: 0 + +# print degree for multiple elements +print(graph.get_degree({1, 3})) +# Output: {1: 2, 3: 1} +``` + +There are some other functions specific to only vertices like `check_connected`, `get_connected`, and `in_cycle`. + +```python +graph[1].check_connected(5) # check if 1 and 5 are connected +# Output: False + +graph[1].get_connected() # Return all the vertices connected to graph +# Output: {1, 2, 3} + +graph[1].in_cycle() # check if vertex is in a cycle +# Output: False + +# You can also check if the graph is connected or not +print(graph.is_connected) # check if all vertices in the graph are connected to each other +# Output: False +``` + +## Path Finding + +All the algorithms for finding the shortest path are located in the `searching_algorithms` [module](https://eeshannarula29.github.io/structlinks/searching), +however one can find all the paths from a vertex to another vertex with `get_all_paths_to` function. + +```python +from structlinks.structures import Graph + +graph = Graph() + +graph.add_edges([(1, 2), (2, 3), (4, 5), (5, 6), (6, 1), (1, 3)]) + +paths = g[2].get_all_paths_to(3) # <--- Get all the paths between 2 and 3 + +print(paths) +# Output: {(2, 3), (2, 1, 3)} +``` + +## Remove Vertices and Edges + +Removing vertices and edges is similar to adding them. The functions used are `graph.remove_vertex`, `graph.remove_vertices`, +`graph.remove_edge` and `graph.remove_edges` + +```python +print(graph.vertices) +# Output: {2, 1, 3, 5, 6, 7} + +print(graph.edges) +# Output: {(2, 1), (1, 3), (3, 5), (1, 7), (7, 6), (6, 1)} + +# Remove a vertex +graph.remove_vertex(2) # <--------------- Removing single vertex + +# After removing: +print(graph.vertices) +# Output: {1, 3, 5, 6, 7} + +print(graph.edges) +# Output: {(1, 3), (3, 5), (1, 7), (7, 6), (6, 1)} + +graph.remove_vertices([5, 3]) # <-------- Remove multiple vertices + +# After removing: +print(graph.vertices) +# Output: {1, 6, 7} + +print(graph.edges) +# Output: {(1, 7), (7, 6), (6, 1)} + +graph.remove_edge(1, 7) # <-------------- Remove a single edge + +# After removing: +print(graph.vertices) +# Output: {1, 6, 7} + +print(graph.edges) +# Output: {(7, 6), (6, 1)} + +graph.remove_edges([(7, 6), (6, 1)]) # <-- Remove multiple edges + +# After removing: +print(graph.vertices) +# Output: {1, 6, 7} + +print(graph.edges) +# Output: {} +``` + +## Get/Set the Attributes + +```python +from structlinks.structures import Graph + +graph = Graph() + +# Add vertices +graph.add_vertices([1, (2, {'type': int}), 3, ('string', {'type': str})]) + +# Add edges +graph.add_edges([(1, 3, {'weight': 10}), (2, 1)]) +``` + +### Vertex Attributes + +```python +print(graph[2].attr) +# Output: {'type': int} + +print(graph['string'].attr['type']) +# Output: str + +graph[1].attr.update({'type': int}) # Same as graph[1].attr['type'] = int +``` + +### Edge Attributes + +```python +print(graph[3][1]) # Represents an edge between 1 and 3 +# Output: {'weight': 10} + +print(graph[1][2]) # Represents an edge between 1 and 2 +# Output: {} + +print(graph[1][3]['weight']) # Return the weight attribute of the edge +# Output: 10 + +graph[1][2]['type'] = 'numbers' # Same as graph[1][2].update({'type': 'numbers'}) +``` + +### Add Global Attributes to All the Vertices of the Graph + +```python +graph.add_global_attributes({'weight': 1}) # add this attribute to all the vertices +``` + +## Graph Generator Functions + +```python +from structlinks.structures import Graph + +graph = Graph() + +# Add vertices +graph.add_vertices([1, (2, {'type': int}), 3, ('string', {'type': str})]) + +# Add edges +graph.add_edges([(1, 3, {'weight': 10}), ('string', 1, {'weight': 1})]) +``` + +### Sub-Graph: Filter by Vertex Values + +```python +filtered = graph.sub_graph_by_value(lambda x: isinstance(x, int)) # <-- only keep int vertices + +print(filtered.vertices) +# Output: {1, 2, 3} + +print(filtered.edges) +# Output: {(1, 3)} +``` + +### Sub-Graph: Filter by Attribute Values + +```python +filtered = graph.sub_graph_by_attribute(attribute='type') # only keep vertices with attribute 'type' + +print(filtered.vertices) +# Output: {2} + +print(filtered.edges) +# Output: {} +``` + +### Union + +```python +other_graph = Graph() + +other_graph.add_edges([(1, 7), (9, 0), (3, 10)]) + +union = graph.union(other_graph) + +print(union.vertices) +# Output: {1, 2, 3, 'string', 7, 9, 0, 10} + +print(union.edges) +# Output: {(1, 3), (1, 'string'), (1, 7), (9, 0), (3, 10)} +``` + +## Printing Graphs + +```python +from structlinks.structures import Graph + +graph = Graph() + +graph.add_edges([(1, 2), (2, 3), (4, 5), (5, 6), (6, 1)]) + +print(graph) +# Output: +# { +# 1: {2, 6} +# 2: {1, 3} +# 3: {2} +# 4: {5} +# 5: {4, 6} +# 6: {1, 5} +# } +``` diff --git a/DataStructures/Graph/__init__.py b/DataStructures/Graph/__init__.py new file mode 100644 index 0000000..461c1e3 --- /dev/null +++ b/DataStructures/Graph/__init__.py @@ -0,0 +1 @@ +from structlinks.DataStructures.Graph.Graph import * \ No newline at end of file diff --git a/LinkedList.py b/DataStructures/LinkedList/LinkedList.py similarity index 100% rename from LinkedList.py rename to DataStructures/LinkedList/LinkedList.py diff --git a/DataStructures/LinkedList/README.md b/DataStructures/LinkedList/README.md new file mode 100644 index 0000000..bf46480 --- /dev/null +++ b/DataStructures/LinkedList/README.md @@ -0,0 +1,142 @@ +# LinkedLists + +## Initializing a Linked list + +You can initialize a linked list by simply creating a linked list object +and optionally pass in a list object witch would contain the initial list +items. + +```python +from structlinks.structures import LinkedList + +# create an empty linked list +lst = LinkedList() +# create a linked list with initial values +lst = LinkedList([1, 10, -3, 5]) +``` + +## Basic Operations + +All of the basic operations are taken from the University of Toronto's CSC111 course. + +```python +from structlinks.structures import LinkedList + +lst = LinkedList([1, 10, 3, 5]) + +# print the list +print(lst) # this will print : [1 -> 10 -> 3 -> 5] + +# Length of the list +length = len(lst) # length = 4 + +# Append items +lst.append(2) # After the call, lst = [1 -> 10 -> 3 -> 5 -> 2] + +# Insert items +lst.insert(0, 200) # After the call, lst = [200 -> 1 -> 10 -> 3 -> 5 -> 2] + +# Get & Set item by index +item = lst[1] # here item = 10 +lst[2] = 100 # set element at index 2 to be 100, so lst = [200 -> 1 -> 100 -> 3 -> 5 -> 2] + +# Slicing the list +part = lst[2:] # here part = [100 -> 3 -> 5 -> 2] + +# Removing element +# 1) by index +popped = lst.pop(0) # here popped = 200, and lst = [1 -> 100 -> 3 -> 5 -> 2] +# 2) by item value +lst.remove(1) # now list lst = [100 -> 3 -> 5 -> 2] + +# Checking for Element +cond1 = 2 in lst # here cond1 is True as 2 is in lst +cond2 = 1 in lst # here cond2 is False as 1 is not in lst + +# Adding to Linked Lists +lst1 = LinkedList([1, 10, 3, 5]) +lst2 = LinkedList([2, 100, 4, 0]) +lst3 = lst1 + lst2 # lst3 = [1 -> 10 -> 3 -> 5 -> 2 -> 100 -> 4 -> 0] + +# Count of an item +count = lst.count(2) # count = 1 as 2 appears only once + +# Index of an item (return -1 if element not in lst) +index = lst.index(100) # index = 0 as 100 is at index 0 + +# Equating to lists +cond3 = lst1 == lst2 # cond3 = False as lst1 and lst2 have different elements + +# Sort the list +lst.sort() # lst will be mutated to [2 -> 3 -> 5 -> 100] + +# Extend the list +lst.extend(lst2) # lst will be mutated to [2 -> 3 -> 5 -> 100 -> 2 -> 100 -> 4 -> 0] + +# Copy the list +lst4 = lst.copy() # lst4 is a copy of lst +``` + +## Additional Operations + +### Inverting a Linked List + +```python +from structlinks.structures import LinkedList + +lst = LinkedList([1, 10, 3, 5]) + +print(lst) +# Output: +# [1 -> 10 -> 3 -> 5] + +lst.invert() # mutate lst + +print(lst) +# Output: +# [5 -> 3 -> 10 -> 1] +``` + +### Mapping functions to a Linked List + +```python +from structlinks.structures import LinkedList + +lst = LinkedList([1, 10, 3, 5]) + +# Map function f(x) = x^2 +new_lst = lst.map(lambda x: x ** 2) + +print(new_lst) +# Output: +# [25 -> 9 -> 100 -> 1] +``` + +### Inbuilt Mapping functions + +```python +from structlinks.structures import LinkedList + +lst = LinkedList([1.1, 10.5, -3.7, 5.2]) + +#abs +abs_lst = lst.abs() + +print(abs_lst) +# Output: +# [1.1 -> 10.5 -> 3.7 -> 5.2] + +# floor +floor_lst = lst.floor() + +print(floor_lst) +# Output: +# [1.0 -> 10.0 -> 3.0 -> 5.0] + +# ceil +ceil_lst = lst.ceil() + +print(ceil_lst) +# Output: +# [2.0 -> 11.0 -> 4.0 -> 6.0] +``` diff --git a/DataStructures/LinkedList/__init__.py b/DataStructures/LinkedList/__init__.py new file mode 100644 index 0000000..6ac0810 --- /dev/null +++ b/DataStructures/LinkedList/__init__.py @@ -0,0 +1 @@ +from structlinks.DataStructures.LinkedList.LinkedList import * \ No newline at end of file diff --git a/Matrix.py b/DataStructures/Matrix/Matrix.py similarity index 100% rename from Matrix.py rename to DataStructures/Matrix/Matrix.py diff --git a/DataStructures/Matrix/README.md b/DataStructures/Matrix/README.md new file mode 100644 index 0000000..630e629 --- /dev/null +++ b/DataStructures/Matrix/README.md @@ -0,0 +1,243 @@ +# Matrix + +## Initialise Matrix + +```python +from structlinks.structures import Matrix + +matrix = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + +print(matrix) +# Output: +#[[1, 2, 3] +# [4, 5, 6] +# [7, 8, 9]] +``` + +## Shape of the Matrix + +```python +from structlinks.structures import Matrix + +matrix = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + +print(matrix.shape) +# Output: +# (3, 3) +``` + +## Get/Set Items + +```python +from structlinks.structures import Matrix + +matrix = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + +print(matrix[1][1] == 5) +# Output: True + +print(matrix[1] == [4, 5, 6]) +# Output: True + +matrix[1][1] = 100 + +print(matrix) +# Output: +#[[1, 2, 3] +# [4, 100, 6] +# [7, 8, 9]] +``` + +## Other Properties + +```python +from structlinks.structures import Matrix + +matrix = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + +# check whether the matrix is a square matrix +print(matrix.is_square) +# Output: True + +# check whether the matrix is a vector +print(matrix.is_vector) +# Output: False + +# check whether the matrix is invertable +print(matrix.is_invertible) +# Output: False +``` + +## Add Constant + +```python +from structlinks.structures import Matrix + +matrix = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + +new_matrix = matrix.add_scalar(10) + +print(new_matrix) +# Output: +#[[11, 12, 13] +# [14, 15, 16] +# [17, 18, 19]] +``` + +## Multiply Constant + +```python +from structlinks.structures import Matrix + +matrix = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + +new_matrix = matrix.multiply_scalar(10) + +print(new_matrix) +# Output: +#[[10, 20, 30] +# [40, 50, 60] +# [70, 80, 90]] +``` + +## Map Function + +```python +from structlinks.structures import Matrix + +matrix = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + +new_matrix = matrix.map(lambda x: x ** 2) + +print(new_matrix) +# Output: +#[[1, 4, 9] +# [16, 25, 36] +# [49, 64, 81]] +``` + +## Create Zero, One, and Identity Matrix + +```python +from structlinks.structures import Matrix + +zeros = Matrix.zeros(shape = (2, 1)) + +print(zeros) +# Output: +# [[0], +# [0]] + +ones = Matrix.ones(shape = (1, 2)) + +print(ones) +# Output: +# [[1, 1]] + +indentity = Matrix.identity(order = 2) + +print(indentity) +# Output: +# [[1, 0], +# [0, 1]] +``` + +## Add Matrices + +```python +from structlinks.structures import Matrix + +m1 = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) +m2 = Matrix([[10, 11, 12], [13, 14, 15], [16, 17, 18]]) + +m3 = m1.add_matrix(m2) + +print(m3) +# Output: +# [[11, 13, 15], +# [17, 19, 21], +# [23, 25, 27]] +``` + +## Matrix Multiplication (Cross Product) + +```python +from structlinks.structures import Matrix + +m1 = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) +m2 = Matrix([[10, 11, 12], [13, 14, 15], [16, 17, 18]]) + +m3 = m1.multiply_matrix(m2) + +print(m3) +# Output: +# [[84, 90, 96] +# [201, 216, 231] +# [318, 342, 366]] +``` + +## Dot Product + +```python +from structlinks.structures import Matrix + +m1 = Matrix([[1, 2, 3]]) +m2 = Matrix([[4, 5, 6]]) + +m3 = m1.dot_multiply(m2) + +print(m3) +# Output: +# [[32]] +``` + +## Transpose Matrix + +```python +from structlinks.structures import Matrix + +matrix = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + +print(matrix) +# Output: +#[[1, 2, 3] +# [4, 5, 6] +# [7, 8, 9]] + +transpose = matrix.transpose() + +print(transpose) +# Output: +#[[1, 4, 7] +# [2, 5, 8] +# [3, 6, 9]] +``` + +## Determinant + +```python +from structlinks.structures import Matrix + +matrix = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + +det = matrix.determinant() + +print(det) +# Output: 0 +``` + +## Inverse + +```python +from structlinks.structures import Matrix + +matrix = Matrix([[3, 1, -1], [2, -2, 0], [1, 2, -1]]) + +inverse = matrix.inverse() + +print(inverse) +# Output: +#[[1.0, -0.5, -1.0] +# [1.0, -1.0, -1.0] +# [3.0, -2.5, -4.0]] +``` diff --git a/DataStructures/Matrix/__init__.py b/DataStructures/Matrix/__init__.py new file mode 100644 index 0000000..d63b044 --- /dev/null +++ b/DataStructures/Matrix/__init__.py @@ -0,0 +1 @@ +from structlinks.DataStructures.Matrix.Matrix import * \ No newline at end of file diff --git a/Queue.py b/DataStructures/Queue/Queue.py similarity index 96% rename from Queue.py rename to DataStructures/Queue/Queue.py index 990b58b..081f71a 100644 --- a/Queue.py +++ b/DataStructures/Queue/Queue.py @@ -5,8 +5,8 @@ """ from __future__ import annotations -from structlinks.DoublyLinkedList import * -from structlinks.SortingAlgorithms import quicksort +from structlinks.DataStructures.DoublyLinkedList.DoublyLinkedList import * +from structlinks.Algorithms.SortingAlgorithms.SortingAlgorithms import * from typing import * diff --git a/DataStructures/Queue/README.md b/DataStructures/Queue/README.md new file mode 100644 index 0000000..058dfee --- /dev/null +++ b/DataStructures/Queue/README.md @@ -0,0 +1,212 @@ +# Queues + +## Initialize a Normal Queue + +```python +from structlinks.structures import Queue + +# initialize empty queue +queue = Queue() + +# initialize queue with list +queue_with_list = Queue([1, 2, 3]) + +print(queue) +# Output: +# [1 -> 2 -> 3] +``` + +## Initialize a Priority Queue + +```python +from structlinks.structures import Queue + +priority_func = lambda x: len(x) + +queue = Queue(['hi', 'hello', 'hey'], metric = priority_func) + +print(queue) +# Output: +# [hi -> hey -> hello] +``` + +## Initialize Queue / Priority Queue with Limit + +Add a limit to the queue to limit the number of elements in a queue, This can be done by adding limit +attribute while initializing a queue. + +```python +from structlinks.structures import Queue + +queue = Queue(['hi', 'hello', 'hey'], limit = 5) +``` + +## Interchange between Queue and Priority Queue + +Queue and Priority Queue are interchangeable and can be converted from one to another + +### Queue --> Priority Queue + +```python +from structlinks.structures import Queue + +# Initialize a queue +queue = Queue(['hi', 'hello', 'hey']) + +# define a priority function +priority_func = lambda x: len(x) + +# convert Queue to Priority Queue +queue.change_metric(priority_func) +``` + +### Priority Queue --> Queue + +```python +from structlinks.structures import Queue + +# define a priority function +priority_func = lambda x: len(x) + +# Initialize a Priority Queue +queue = Queue(['hi', 'hello', 'hey'], metric = priority_func) + +# convert Priority Queue to Queue +queue.change_metric(None) +``` + +## Change Limit + +```python +from structlinks.structures import Queue + +# Initialize a queue with limit +queue = Queue(['hi', 'hello', 'hey'], limit = 3) + +queue.change_limit(4) # <---- changes the limit to 4 + +queue.change_limit(None) # <---- removes the limit +``` + +## Check for Empty / Filled Queue + +```python +from structlinks.structures import Queue + +# Initialize a queue with limit +queue = Queue(['hi', 'hello', 'hey'], limit = 3) + +print(queue.is_filled) +# Output: +# True + +print(queue.is_empty) +# Output: +# False +``` + +## Enqueue / Push Elements + +The function used to enqueue/push element from queue, takes constant time. If the limit of the queue has been obtained +the `QueueLimitReachedError` will be raised. + +```python +from structlinks.structures import Queue + +# Initialize a queue +queue = Queue([100, 200, 300]) + +print(queue) +# Output: +# [100 -> 200 -> 300] + +queue.enqueue(50) # <--- push 50 into the queue + +print(queue) +# Output: +# [50 -> 100 -> 200 -> 300] +``` + +## Dequeue / Pop Elements + +The function used to Dequeue/Pop element, takes constant time. If the queue is empty then +the `EmptyQueueError` will be raised. + +```python +from structlinks.structures import Queue + +# Initialize a queue +queue = Queue([100, 200, 300]) + +print(queue) +# Output: +# [100 -> 200 -> 300] + +element = queue.dequeue() # <--- pop element + +print(element) +# Output: +# 300 + +print(queue) +# Output: +# [100 -> 200] +``` + +## Extend Queue + +```python +from structlinks.structures import Queue + +q1 = Queue([100, 200, 300]) +q2 = Queue([400, 500, 600]) + +q2.extend(q1) # <--- Extend q2 by q1 + +print(q2) +# Output: +# [100 -> 200 -> 300 -> 400 -> 500 -> 600] +``` + +## Map a Queue + +```python +from structlinks.structures import Queue + +# Initialize a queue +queue = Queue([1, 2, 3]) + +# create a mapping function +mapping_function = lambda x: x ** 2 + +# map the function to queue +queue.map(mapping_function) + +print(queue) +# Output: +# [1 -> 4 -> 9] +``` + +## Printing Custom Queues + +Queues can be printed to get specific properties of a elements in the queue. + +```python +from structlinks.structures import Queue +from dataclasses import dataclass + +@dataclass +class Person: + name: str + +david = Person('David') +mario = Person('Mario') + +people_queue = Queue([david, mario]) + +people_queue.display(lambda person: person.name) +# Output: +# Entry ---------------> Exit +# David -> Mario +# --------------------------- +``` diff --git a/DataStructures/Queue/__init__.py b/DataStructures/Queue/__init__.py new file mode 100644 index 0000000..b4bd3fe --- /dev/null +++ b/DataStructures/Queue/__init__.py @@ -0,0 +1,2 @@ +from structlinks.DataStructures.Queue.Queue import * + diff --git a/DataStructures/Stack/README.md b/DataStructures/Stack/README.md new file mode 100644 index 0000000..c87983a --- /dev/null +++ b/DataStructures/Stack/README.md @@ -0,0 +1,191 @@ +# Stacks + +## Initialize a stack + +```python +from structlinks.structures import Stack + +# initialize empty stack +stack = Stack() + +# initialize stack with a list +stack_with_list = Stack([1, 2, 3, 4, 5]) + + +print(stack_with_list) +# Output: + +# | 5 | +# | 4 | +# | 3 | +# | 2 | +# | 1 | +# |____________________| + +``` + +## Push Elements + +```python +from structlinks.structures import Stack + +stack = Stack([1, 2]) + +print(stack) +# Output: + +# | 2 | +# | 1 | +# |____________________| + +stack.push(3) + +print(stack) +# Output: + +# | 3 | +# | 2 | +# | 1 | +# |____________________| +``` + +## Push multiple Elements + +```python +from structlinks.structures import Stack + +stack = Stack([1, 2]) + +print(stack) +# Output: + +# | 2 | +# | 1 | +# |____________________| + +stack.push_multiple([3, 4, 5]) + +print(stack) +# Output: + +# | 5 | +# | 4 | +# | 3 | +# | 2 | +# | 1 | +# |____________________| +``` + +## Pop Elements + +```python +from structlinks.structures import Stack + +stack = Stack([1, 2]) + +print(stack) +# Output: + +# | 2 | +# | 1 | +# |____________________| + +stack.pop() + +print(stack) +# Output: + +# | 1 | +# |____________________| +``` + +## Extend and Add Stacks + +```python +from structlinks.structures import Stack + +stack1 = Stack([1, 2]) +stack2 = Stack([3, 4]) + +# adding two stacks +stack3 = stack1 + stack2 + +print(stack3) +# Output: + +# | 4 | +# | 3 | +# | 2 | +# | 1 | +# |____________________| + +# extend stack1 +stack1.extend(stack2) + +print(stack1) +# Output: + +# | 4 | +# | 3 | +# | 2 | +# | 1 | +# |____________________| +``` + +## Map Stack to a function + +```python +from structlinks.structures import Stack + +stack = Stack([1, 2]) + +print(stack) +# Output: + +# | 2 | +# | 1 | +# |____________________| + +# map to f(x) = x^2 +mapped = stack.map(lambda x: x ** 2) + +print(mapped) +# Output: + +# | 4 | +# | 1 | +# |____________________| + +print(stack) # <---- stack did not get mutated +# Output: + +# | 2 | +# | 1 | +# |____________________| +``` + +## Invert a Stack + +```python +from structlinks.structures import Stack + +stack = Stack([1, 2, 3]) + +print(stack) +# Output: + +# | 3 | +# | 2 | +# | 1 | +# |____________________| + +stack.invert() + +print(stack) +# Output: + +# | 1 | +# | 2 | +# | 3 | +# |____________________| +``` diff --git a/Stack.py b/DataStructures/Stack/Stack.py similarity index 100% rename from Stack.py rename to DataStructures/Stack/Stack.py diff --git a/DataStructures/Stack/__init__.py b/DataStructures/Stack/__init__.py new file mode 100644 index 0000000..fc869e8 --- /dev/null +++ b/DataStructures/Stack/__init__.py @@ -0,0 +1 @@ +from structlinks.DataStructures.Stack.Stack import * \ No newline at end of file diff --git a/DataStructures/__init__.py b/DataStructures/__init__.py new file mode 100644 index 0000000..e28f9eb --- /dev/null +++ b/DataStructures/__init__.py @@ -0,0 +1,6 @@ +from structlinks.DataStructures.BinarySearchTree.BinarySearchTree import BinarySearchTree +from structlinks.DataStructures.DoublyLinkedList.DoublyLinkedList import DoublyLinkedList +from structlinks.DataStructures.Graph.Graph import Graph +from structlinks.DataStructures.LinkedList.LinkedList import LinkedList +from structlinks.DataStructures.Matrix.Matrix import Matrix +from structlinks.DataStructures.Queue.Queue import Queue diff --git a/__init__.py b/__init__.py index a0c805c..6c9ffe0 100644 --- a/__init__.py +++ b/__init__.py @@ -1,10 +1,2 @@ -from structlinks.LinkedList import * -from structlinks.DoublyLinkedList import * -from structlinks.Graph import * -from structlinks.Matrix import * -from structlinks.Stack import * -from structlinks.Queue import * -import structlinks.SortingAlgorithms as sorting_algorithms -import structlinks.SearchingAlgorithms as searching_algorithms - - +import structlinks.DataStructures as structures +import structlinks.Algorithms as algorithms