Skip to content

Commit 80f2037

Browse files
committed
added env, tests for lists, started stacks & queue
1 parent b67862d commit 80f2037

12 files changed

+372
-7
lines changed

Diff for: .vscode/settings.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"python.pythonPath": "C:\\Users\\Marcus\\anaconda3\\envs\\dsa_env\\python.exe"
3+
}

Diff for: README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ This repository serves as my live notes as I learn Data Structures and Algorithm
44
# References
55

66
1. https://www.codecademy.com/learn/learn-data-structures-and-algorithms-with-python
7-
7+
2. https://medium.com/@Emmanuel.A/data-structure-queue-python-9e5439d2ceea

Diff for: environment.yml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
name: dsa_env
2+
channels:
3+
- conda-forge
4+
- defaults
5+
dependencies:
6+
- pip
7+
- python=3.10
8+
- pylint
9+
- pytest
10+
- pip:
11+
- -e .

Diff for: __init__.py renamed to mydsa/__init__.py

File renamed without changes.

Diff for: mydsa/doubly_linked_list.py

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# standard imports
2+
from typing import Any
3+
4+
# local imports
5+
from mydsa.nodes import Node
6+
7+
class DoublyLinkedList:
8+
9+
def __init__(self):
10+
self._head_node: Node = None
11+
self._tail_node: Node = None
12+
13+
def add_to_head(self, new_value: Any) -> None:
14+
"""Add node to beginning of list"""
15+
new_head = Node(new_value)
16+
current_head = self._head_node
17+
18+
if current_head != None:
19+
current_head.prev_node = new_head
20+
new_head.next_node = current_head
21+
22+
self._head_node = new_head
23+
24+
if self._tail_node == None:
25+
self._tail_node = new_head
26+
27+
def add_to_tail(self, new_value: Any) -> None:
28+
"""Add node to end of the list"""
29+
new_tail = Node(new_value)
30+
current_tail = self._tail_node
31+
32+
if current_tail != None:
33+
current_tail.next_node = new_tail
34+
new_tail.prev_node = current_tail
35+
36+
self._tail_node = new_tail
37+
38+
if self._head_node == None: # if list is empty
39+
self._head_node = new_tail
40+
41+
def remove_head(self) -> Any:
42+
"""Remove head node from list"""
43+
removed_head = self._head_node
44+
45+
if removed_head == None:
46+
return None
47+
48+
self._head_node = removed_head.next_node
49+
50+
if self._head_node != None:
51+
self._head_node.prev_node = None
52+
53+
if removed_head == self._tail_node:
54+
self.remove_tail()
55+
56+
return removed_head.value
57+
58+
def remove_tail(self) -> Any:
59+
"""Remove tail node from list"""
60+
removed_tail = self._tail_node
61+
62+
if removed_tail == None:
63+
return None
64+
65+
self._tail_node = removed_tail.prev_node
66+
67+
if self._tail_node != None:
68+
self._tail_node.next_node = None
69+
70+
if removed_tail == self._head_node:
71+
self.remove_head()
72+
73+
return removed_tail.value
74+
75+
def remove_by_value(self, value_to_remove: Any) -> Any:
76+
"""Remove node that contains given value."""
77+
node_to_remove = None
78+
current_node = self._head_node
79+
80+
'''Find value to remove'''
81+
while current_node != None:
82+
if current_node.value == value_to_remove:
83+
node_to_remove = current_node
84+
break
85+
86+
current_node = current_node.next_node
87+
88+
'''restructure list'''
89+
if node_to_remove == None: # value not found
90+
return None
91+
92+
if node_to_remove == self._head_node:
93+
self.remove_head()
94+
95+
elif node_to_remove == self._tail_node:
96+
self.remove_tail()
97+
98+
else: # if node is in the middle of the list
99+
next_node = node_to_remove.next_node
100+
prev_node = node_to_remove.prev_node
101+
next_node.prev_node = prev_node
102+
prev_node.next_node = next_node
103+
104+
return node_to_remove
105+
106+
def __str__(self) -> str:
107+
string_list = []
108+
current_node = self._head_node
109+
while current_node:
110+
if current_node.value != None:
111+
string_list.append(str(current_node.value))
112+
current_node = current_node.next_node # iterate to the next node
113+
114+
return ' <-> '.join(string_list)
115+
116+
if __name__ == "__main__":
117+
doubly_list = DoublyLinkedList()
118+
print(doubly_list)
119+
doubly_list.add_to_head(1)
120+
doubly_list.add_to_tail(2)
121+
doubly_list.add_to_head(3)
122+
print(doubly_list)

Diff for: linked_list.py renamed to mydsa/linked_list.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
# standard imports
12
from __future__ import annotations
23
from typing import Any
3-
from nodes import Node
4+
5+
# local imports
6+
from mydsa.nodes import Node
47

58

69
class LinkedList:
@@ -32,7 +35,7 @@ def __str__(self):
3235
return ' -> '.join(string_list)
3336

3437
def remove_node(self, value_to_remove: Any) -> None:
35-
"""Remove node(s) that contains given value."""
38+
"""Remove node that contains given value."""
3639
current_node = self.head_node
3740
if current_node.value == value_to_remove: # if head_node contains value to remove
3841
self._head_node = current_node.next_node

Diff for: mydsa/myqueue.py

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from nodes import Node
2+
from typing import Any
3+
4+
5+
class Queue:
6+
7+
def __init__(self):
8+
self._head: Node = None
9+
self._tail: Node = None
10+
11+
def is_empty(self) -> bool:
12+
return self._head == None
13+
14+
def peek(self) -> Any:
15+
return self._head.value
16+
17+
def enqueue(self, value) -> None:
18+
"""Add node """
19+
new_head = Node(value)
20+
current_head = self._head_node
21+
22+
if current_head != None:
23+
current_head.prev_node = new_head
24+
new_head.next_node = current_head
25+
26+
self._head_node = new_head
27+
28+
if self._tail_node == None:
29+
self._tail_node = new_head
30+
31+
def dequeue(self) -> Any:
32+
value = self._head.value
33+
self._head = self._head.next_node
34+
if self._head_node == None:
35+
self._tail_node = None
36+
37+
return value
38+
39+

Diff for: nodes.py renamed to mydsa/nodes.py

+13-4
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,20 @@
33

44

55
class Node:
6+
67
"""A node is a base data structure used to store any value and point to
78
other nodes. This data structure can be used to build other data structures such as
89
linked listed and trees.
910
"""
1011

11-
def __init__(self,
12+
def __init__(self,
1213
value: Any,
13-
next_node: Node = None):
14+
next_node: Node=None,
15+
prev_node: Node=None):
16+
1417
self._value = value
1518
self._next_node = next_node
19+
self._prev_node = prev_node
1620

1721
@property
1822
def next_node(self) -> Node:
@@ -26,6 +30,11 @@ def next_node(self, next_node: Node) -> None:
2630
def value(self) -> Any:
2731
return self._value
2832

33+
@property
34+
def prev_node(self) -> Node:
35+
return self._prev_node
2936

30-
class DoublyNode:
31-
pass
37+
@prev_node.setter
38+
def prev_node(self, prev_node: Node) -> None:
39+
self._prev_node = prev_node
40+

Diff for: mydsa/stacks.py

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from nodes import Node
2+
from typing import Any, Optional
3+
4+
class Stack:
5+
6+
def __init__(self, limit: int=1000):
7+
self._top_item = None
8+
self._size = 0
9+
self._limit = limit
10+
11+
def push(self, value: Any) -> None:
12+
if self.has_space():
13+
item = Node(value)
14+
item.next_node = self._top_item
15+
self._top_item = item
16+
self._size += 1
17+
else:
18+
print('All out of space')
19+
20+
def pop(self) -> Optional[Any]:
21+
if self._size > 0:
22+
item_to_remove = self._top_item
23+
self._top_item = item_to_remove.next_node
24+
self._size -= 1
25+
return item_to_remove.value
26+
else:
27+
print('This stack is totally empty.')

Diff for: setup.py

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import os
2+
from setuptools import setup
3+
4+
# Utility function to read the README file.
5+
# Used for the long_description. It's nice, because now 1) we have a top level
6+
# README file and 2) it's easier to type in the README file than to put a raw
7+
# string in below ...
8+
9+
10+
def read(fname: str) -> str:
11+
"""Reads README file
12+
Args:
13+
fname (str): path to readme file
14+
Returns:
15+
str: contents in readme
16+
"""
17+
full_path = os.path.join(os.path.dirname(__file__), fname)
18+
with open(full_path, encoding="utf-8") as file:
19+
return file.read()
20+
21+
22+
setup(
23+
name="DSA-Lessons",
24+
version="0.0.1",
25+
author="Marcus Allen",
26+
author_email="[email protected]",
27+
url="https://github.com/marcus-24/DSA_Journey",
28+
long_description=read('README.md'),
29+
py_modules=[]
30+
)

Diff for: tests/test_doubly_linked_list.py

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# standard imports
2+
import pytest
3+
import sys
4+
5+
# local imports
6+
from mydsa.doubly_linked_list import DoublyLinkedList
7+
8+
@pytest.fixture
9+
def sample_list() -> DoublyLinkedList:
10+
mylist = DoublyLinkedList()
11+
for idx in range(3):
12+
mylist.add_to_head(idx)
13+
14+
return mylist
15+
16+
def test_empty_list():
17+
mylist = DoublyLinkedList()
18+
assert str(mylist) == ''
19+
20+
def test_add_single_value_to_head():
21+
mylist = DoublyLinkedList()
22+
mylist.add_to_head(1)
23+
assert str(mylist) == '1'
24+
25+
def test_add_multiple_values_to_head():
26+
mylist = DoublyLinkedList()
27+
for idx in range(3):
28+
mylist.add_to_head(idx)
29+
assert str(mylist) == '2 <-> 1 <-> 0'
30+
31+
def test_add_single_value_to_tail():
32+
mylist = DoublyLinkedList()
33+
mylist.add_to_tail(1)
34+
assert str(mylist) == '1'
35+
36+
def test_add_multiple_values_to_tail():
37+
mylist = DoublyLinkedList()
38+
for idx in range(3):
39+
mylist.add_to_tail(idx)
40+
assert str(mylist) == '0 <-> 1 <-> 2'
41+
42+
def test_remove_single_head(sample_list: DoublyLinkedList):
43+
sample_list.remove_head()
44+
assert str(sample_list) == '1 <-> 0'
45+
46+
def test_remove_two_heads(sample_list: DoublyLinkedList):
47+
for _ in range(2):
48+
sample_list.remove_head()
49+
50+
assert str(sample_list) == '0'
51+
52+
def test_remove_all_heads(sample_list: DoublyLinkedList):
53+
for _ in range(3):
54+
sample_list.remove_head()
55+
56+
assert str(sample_list) == ''
57+
58+
def test_remove_single_tail(sample_list: DoublyLinkedList):
59+
sample_list.remove_tail()
60+
assert str(sample_list) == '2 <-> 1'
61+
62+
def test_remove_two_tails(sample_list: DoublyLinkedList):
63+
for _ in range(2):
64+
sample_list.remove_tail()
65+
assert str(sample_list) == '2'
66+
67+
def test_remove_middle_value(sample_list: DoublyLinkedList):
68+
sample_list.remove_by_value(1)
69+
assert str(sample_list) == '2 <-> 0'
70+
71+
if __name__ == "__main__":
72+
pytest.main(sys.argv)

0 commit comments

Comments
 (0)