Skip to content

Commit

Permalink
Merge pull request #91 from BrianLusina/feat/circular-linked-list
Browse files Browse the repository at this point in the history
Circular linked list
  • Loading branch information
BrianLusina authored May 23, 2024
2 parents 8b83c03 + 7b1c5e2 commit bcb2e16
Show file tree
Hide file tree
Showing 10 changed files with 463 additions and 42 deletions.
5 changes: 5 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@
* Huffman
* [Decoding](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/huffman/decoding.py)
* [Encoding](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/huffman/encoding.py)
* Josephus Circle
* [Test Josephus Circle](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/josephus_circle/test_josephus_circle.py)
* Memoization
* [Fibonacci](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/memoization/fibonacci.py)
* [Petethebaker](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/petethebaker.py)
Expand Down Expand Up @@ -163,6 +165,9 @@
* Hashset
* [Test My Hashset](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/hashset/test_my_hashset.py)
* Linked Lists
* Circular
* [Node](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/linked_lists/circular/node.py)
* [Test Circular Linked List](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/linked_lists/circular/test_circular_linked_list.py)
* Doubly Linked List
* [Test Doubly Linked List](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/linked_lists/doubly_linked_list/test_doubly_linked_list.py)
* [Test Doubly Linked List Move Tail To Head](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/linked_lists/doubly_linked_list/test_doubly_linked_list_move_tail_to_head.py)
Expand Down
19 changes: 19 additions & 0 deletions algorithms/josephus_circle/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Josephus Circle

Children often play a counting-out game to randomly select one person from the group by singing a rhyme. The purpose is
to select one person, either as a straightforward winner, or as someone who is eliminated.

Josephus problem is related to this concept. In this problem, people are standing in one circle waiting to be executed.
Following points list the specifications of Josephus problem:

The counting out begins at a specified point in a circle and continues around the circle in a fixed direction.

In each step, a certain number of people are skipped and the next person is executed.

For example, if we have 𝑛 people, and 𝑘-1 people are skipped every time, it means that the
𝑘th person is executed. Here, 𝑘 is the step-size.

## Reference

- https://en.wikipedia.org/wiki/Josephus_problem
-
18 changes: 18 additions & 0 deletions algorithms/josephus_circle/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from datastructures.linked_lists.circular import CircularLinkedList
from datastructures.linked_lists.circular.node import CircularNode


def josephus_circle(circular_list: CircularLinkedList, step: int) -> CircularNode:
current = circular_list.head

length = len(circular_list)
while length > 1:
count = 1
while count != step:
current = current.next
count += 1
circular_list.delete_node(current)
current = current.next
length -= 1

return current
22 changes: 22 additions & 0 deletions algorithms/josephus_circle/test_josephus_circle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import unittest
from datastructures.linked_lists.circular import CircularLinkedList
from datastructures.linked_lists.circular.node import CircularNode
from . import josephus_circle


class JosephusCircularTestCase(unittest.TestCase):
def test_1(self):
"""should run through the circle of 1-2-3-4 with a step of 2 remaining with 1"""
data = [1, 2, 3, 4]
circular_linked_list = CircularLinkedList()
for d in data:
circular_linked_list.append(d)

step = 2
expected = CircularNode(1)
actual = josephus_circle(circular_linked_list, step)
self.assertEqual(expected, actual)


if __name__ == '__main__':
unittest.main()
68 changes: 35 additions & 33 deletions datastructures/linked_lists/__init__.py
Original file line number Diff line number Diff line change
@@ -1,69 +1,70 @@
# coding=utf-8
from typing import Any, Union, Optional, Generic, TypeVar
from abc import ABCMeta, abstractmethod
from typing import Any, Union, Optional

from datastructures.linked_lists.exceptions import EmptyLinkedList

T = TypeVar("T")

class Node:

class Node(Generic[T]):
"""
Node object in the Linked List
"""

__metaclass__ = ABCMeta

def __init__(self, data=Optional[Any], next_=None, key=None):
def __init__(self, data: Optional[T] = None, next_: Optional['Node[Generic[T]]'] = None, key: Any = None):
self.data = data
self.next: Optional[Node] = next_
self.key = key
self.next = next_
# if no key is provided, the hash of the data becomes the key
self.key = key or hash(data)

def __str__(self):
return f"Node({self.data})"
def __str__(self) -> str:
return f"Node(data={self.data}, key={self.key})"

def __repr__(self):
return f"Node({self.data})"
def __repr__(self) -> str:
return f"Node(data={self.data}, key={self.key})"

def __eq__(self, other: "Node"):
return self.data == other.data
def __eq__(self, other: "Node") -> bool:
return self.key == other.key


class LinkedList:
class LinkedList(Generic[T]):
"""
The most basic LinkedList from which other types of Linked List will be subclassed
"""

__metaclass__ = ABCMeta
head: Optional[Node] = None

def __init__(self):
self.head = None
def __init__(self, head: Optional[Node[Generic[T]]] = None):
self.head: Optional[Node[Generic[T]]] = head

def __iter__(self):
head = self.head
current = self.head

if head:
yield head.data
if current:
yield current.data

if head.next:
node = head.next
if current.next:
node = current.next
while node:
yield node.data
node = node.next

@abstractmethod
def __str__(self):
raise NotImplementedError("Not Yet implemented")
return "->".join([str(item) for item in self])

@abstractmethod
def __repr__(self):
raise NotImplementedError("Not Yet implemented")
return "->".join([str(item) for item in self])

def __len__(self):
def __len__(self) -> int:
"""
Implements the len() for a linked list. This counts the number of nodes in a Linked List
This uses an iterative method to find the length of the LinkedList
:return: Number of nodes
:rtype: int
Returns:
int: Number of nodes
"""
return len(tuple(iter(self)))

Expand Down Expand Up @@ -281,23 +282,24 @@ def delete_node_at_position(self, position: int):
# rest of the implementation is at the relevant subclasses

@abstractmethod
def delete_node_by_data(self, data: Any):
def delete_node_by_key(self, key: Any):
"""
traverses the LinkedList until we find the data in a Node that matches and deletes that node. This uses the same
approach as self.delete_node(node: Node) but instead of using the node to traverse the linked list, we use the
data attribute of a node. Note that if there are duplicate Nodes in the LinkedList with the same data attributes
only, the first Node is deleted.
:param data: Data of Node element to be deleted
Args:
key Any: Key of Node element to be deleted
"""
raise NotImplementedError("Not yet implemented")

@abstractmethod
def delete_nodes_by_data(self, data: Any):
def delete_nodes_by_key(self, key: Any):
"""
traverses the LinkedList until we find the data in a Node that matches and deletes those nodes. This uses the
same approach as self.delete_node(node: Node) but instead of using the node to traverse the linked list,
we use the data attribute of a node.
:param data: Data of Node element to be deleted
traverses the LinkedList until we find the key in a Node that matches and deletes those nodes. This uses the
same approach as self.delete_node_by_key(key) but instead deletes multiple nodes with the same key
Args:
key Any: Key of Node elements to be deleted
"""
raise NotImplementedError("Not yet implemented")

Expand Down
Loading

0 comments on commit bcb2e16

Please sign in to comment.