-
Notifications
You must be signed in to change notification settings - Fork 39
/
circular_linked_list.py
144 lines (115 loc) · 4.25 KB
/
circular_linked_list.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
from __future__ import annotations
from collections.abc import Iterator
from typing import Any
class Node:
def __init__(self, data: Any):
self.data: Any = data
self.next: Node | None = None
class CircularLinkedList:
def __init__(self):
self.head = None
self.tail = None
def __iter__(self) -> Iterator[Any]:
node = self.head
while self.head:
yield node.data
node = node.next
if node == self.head:
break
def __len__(self) -> int:
return len(tuple(iter(self)))
def __repr__(self):
return "->".join(str(item) for item in iter(self))
def insert_tail(self, data: Any) -> None:
self.insert_nth(len(self), data)
def insert_head(self, data: Any) -> None:
self.insert_nth(0, data)
def insert_nth(self, index: int, data: Any) -> None:
if index < 0 or index > len(self):
raise IndexError("list index out of range.")
new_node = Node(data)
if self.head is None:
new_node.next = new_node # first node points itself
self.tail = self.head = new_node
elif index == 0: # insert at head
new_node.next = self.head
self.head = self.tail.next = new_node
else:
temp = self.head
for _ in range(index - 1):
temp = temp.next
new_node.next = temp.next
temp.next = new_node
if index == len(self) - 1: # insert at tail
self.tail = new_node
def delete_front(self):
return self.delete_nth(0)
def delete_tail(self) -> Any:
return self.delete_nth(len(self) - 1)
def delete_nth(self, index: int = 0) -> Any:
if not 0 <= index < len(self):
raise IndexError("list index out of range.")
delete_node = self.head
if self.head == self.tail: # just one node
self.head = self.tail = None
elif index == 0: # delete head node
self.tail.next = self.tail.next.next
self.head = self.head.next
else:
temp = self.head
for _ in range(index - 1):
temp = temp.next
delete_node = temp.next
temp.next = temp.next.next
if index == len(self) - 1: # delete at tail
self.tail = temp
return delete_node.data
def is_empty(self) -> bool:
return len(self) == 0
def test_circular_linked_list() -> None:
"""
>>> test_circular_linked_list()
"""
circular_linked_list = CircularLinkedList()
assert len(circular_linked_list) == 0
assert circular_linked_list.is_empty() is True
assert str(circular_linked_list) == ""
try:
circular_linked_list.delete_front()
raise AssertionError() # This should not happen
except IndexError:
assert True # This should happen
try:
circular_linked_list.delete_tail()
raise AssertionError() # This should not happen
except IndexError:
assert True # This should happen
try:
circular_linked_list.delete_nth(-1)
raise AssertionError()
except IndexError:
assert True
try:
circular_linked_list.delete_nth(0)
raise AssertionError()
except IndexError:
assert True
assert circular_linked_list.is_empty() is True
for i in range(5):
assert len(circular_linked_list) == i
circular_linked_list.insert_nth(i, i + 1)
assert str(circular_linked_list) == "->".join(str(i) for i in range(1, 6))
circular_linked_list.insert_tail(6)
assert str(circular_linked_list) == "->".join(str(i) for i in range(1, 7))
circular_linked_list.insert_head(0)
assert str(circular_linked_list) == "->".join(str(i) for i in range(0, 7))
assert circular_linked_list.delete_front() == 0
assert circular_linked_list.delete_tail() == 6
assert str(circular_linked_list) == "->".join(str(i) for i in range(1, 6))
assert circular_linked_list.delete_nth(2) == 3
circular_linked_list.insert_nth(2, 3)
assert str(circular_linked_list) == "->".join(str(i) for i in range(1, 6))
assert circular_linked_list.is_empty() is False
if __name__ == "__main__":
import doctest
doctest.testmod()