|
2 | 2 |
|
3 | 3 |
|
4 | 4 | class Node:
|
5 |
| - """ |
6 |
| - Class to represent a single node. |
7 |
| -
|
8 |
| - Each node has following attributes |
9 |
| - * data |
10 |
| - * next_ptr |
11 |
| - """ |
12 |
| - |
13 | 5 | def __init__(self, data: Any):
|
14 | 6 | self.data = data
|
15 |
| - self.next_ptr = None |
| 7 | + self.next = None |
16 | 8 |
|
17 | 9 |
|
18 | 10 | class CircularLinkedList:
|
19 |
| - """ |
20 |
| - Class to represent the CircularLinkedList. |
21 |
| -
|
22 |
| - CircularLinkedList has following attributes. |
23 |
| - * head |
24 |
| - * length |
25 |
| - """ |
26 |
| - |
27 | 11 | def __init__(self):
|
28 | 12 | self.head = None
|
29 |
| - self.length = 0 |
| 13 | + self.tail = None |
30 | 14 |
|
31 |
| - def __len__(self) -> int: |
32 |
| - """ |
33 |
| - Dunder method to return length of the CircularLinkedList |
34 |
| - >>> cll = CircularLinkedList() |
35 |
| - >>> len(cll) |
36 |
| - 0 |
37 |
| - >>> cll.append(1) |
38 |
| - >>> len(cll) |
39 |
| - 1 |
40 |
| - >>> cll.prepend(0) |
41 |
| - >>> len(cll) |
42 |
| - 2 |
43 |
| - >>> cll.delete_front() |
44 |
| - >>> len(cll) |
45 |
| - 1 |
46 |
| - >>> cll.delete_rear() |
47 |
| - >>> len(cll) |
48 |
| - 0 |
49 |
| - """ |
50 |
| - return self.length |
51 |
| - |
52 |
| - def __str__(self) -> str: |
53 |
| - """ |
54 |
| - Dunder method to represent the string representation of the CircularLinkedList |
55 |
| - >>> cll = CircularLinkedList() |
56 |
| - >>> print(cll) |
57 |
| - Empty linked list |
58 |
| - >>> cll.append(1) |
59 |
| - >>> cll.append(2) |
60 |
| - >>> print(cll) |
61 |
| - <Node data=1> => <Node data=2> |
62 |
| - """ |
63 |
| - current_node = self.head |
64 |
| - if not current_node: |
65 |
| - return "Empty linked list" |
66 |
| - |
67 |
| - results = [current_node.data] |
68 |
| - current_node = current_node.next_ptr |
69 |
| - |
70 |
| - while current_node != self.head: |
71 |
| - results.append(current_node.data) |
72 |
| - current_node = current_node.next_ptr |
73 |
| - |
74 |
| - return " => ".join(f"<Node data={result}>" for result in results) |
75 |
| - |
76 |
| - def append(self, data: Any) -> None: |
77 |
| - """ |
78 |
| - Adds a node with given data to the end of the CircularLinkedList |
79 |
| - >>> cll = CircularLinkedList() |
80 |
| - >>> cll.append(1) |
81 |
| - >>> print(f"{len(cll)}: {cll}") |
82 |
| - 1: <Node data=1> |
83 |
| - >>> cll.append(2) |
84 |
| - >>> print(f"{len(cll)}: {cll}") |
85 |
| - 2: <Node data=1> => <Node data=2> |
86 |
| - """ |
87 |
| - current_node = self.head |
88 |
| - |
89 |
| - new_node = Node(data) |
90 |
| - new_node.next_ptr = new_node |
| 15 | + def __iter__(self): |
| 16 | + node = self.head |
| 17 | + while self.head: |
| 18 | + yield node.data |
| 19 | + node = node.next |
| 20 | + if node == self.head: |
| 21 | + break |
91 | 22 |
|
92 |
| - if current_node: |
93 |
| - while current_node.next_ptr != self.head: |
94 |
| - current_node = current_node.next_ptr |
| 23 | + def __len__(self) -> int: |
| 24 | + return len(tuple(iter(self))) |
95 | 25 |
|
96 |
| - current_node.next_ptr = new_node |
97 |
| - new_node.next_ptr = self.head |
98 |
| - else: |
99 |
| - self.head = new_node |
| 26 | + def __repr__(self): |
| 27 | + return "->".join(str(item) for item in iter(self)) |
100 | 28 |
|
101 |
| - self.length += 1 |
| 29 | + def insert_tail(self, data: Any) -> None: |
| 30 | + self.insert_nth(len(self), data) |
102 | 31 |
|
103 |
| - def prepend(self, data: Any) -> None: |
104 |
| - """ |
105 |
| - Adds a node with given data to the front of the CircularLinkedList |
106 |
| - >>> cll = CircularLinkedList() |
107 |
| - >>> cll.prepend(1) |
108 |
| - >>> cll.prepend(2) |
109 |
| - >>> print(f"{len(cll)}: {cll}") |
110 |
| - 2: <Node data=2> => <Node data=1> |
111 |
| - """ |
112 |
| - current_node = self.head |
| 32 | + def insert_head(self, data: Any) -> None: |
| 33 | + self.insert_nth(0, data) |
113 | 34 |
|
| 35 | + def insert_nth(self, index: int, data: Any) -> None: |
| 36 | + if index < 0 or index > len(self): |
| 37 | + raise IndexError("list index out of range.") |
114 | 38 | new_node = Node(data)
|
115 |
| - new_node.next_ptr = new_node |
116 |
| - |
117 |
| - if current_node: |
118 |
| - while current_node.next_ptr != self.head: |
119 |
| - current_node = current_node.next_ptr |
120 |
| - |
121 |
| - current_node.next_ptr = new_node |
122 |
| - new_node.next_ptr = self.head |
123 |
| - |
124 |
| - self.head = new_node |
125 |
| - self.length += 1 |
126 |
| - |
127 |
| - def delete_front(self) -> None: |
128 |
| - """ |
129 |
| - Removes the 1st node from the CircularLinkedList |
130 |
| - >>> cll = CircularLinkedList() |
131 |
| - >>> cll.delete_front() |
132 |
| - Traceback (most recent call last): |
133 |
| - ... |
134 |
| - IndexError: Deleting from an empty list |
135 |
| - >>> cll.append(1) |
136 |
| - >>> cll.append(2) |
137 |
| - >>> print(f"{len(cll)}: {cll}") |
138 |
| - 2: <Node data=1> => <Node data=2> |
139 |
| - >>> cll.delete_front() |
140 |
| - >>> print(f"{len(cll)}: {cll}") |
141 |
| - 1: <Node data=2> |
142 |
| - >>> cll.delete_front() |
143 |
| - >>> print(f"{len(cll)}: {cll}") |
144 |
| - 0: Empty linked list |
145 |
| - """ |
146 |
| - if not self.head: |
147 |
| - raise IndexError("Deleting from an empty list") |
148 |
| - |
149 |
| - current_node = self.head |
150 |
| - |
151 |
| - if current_node.next_ptr == current_node: |
152 |
| - self.head = None |
| 39 | + if self.head is None: |
| 40 | + new_node.next = new_node # first node points itself |
| 41 | + self.tail = self.head = new_node |
| 42 | + elif index == 0: # insert at head |
| 43 | + new_node.next = self.head |
| 44 | + self.head = self.tail.next = new_node |
153 | 45 | else:
|
154 |
| - while current_node.next_ptr != self.head: |
155 |
| - current_node = current_node.next_ptr |
156 |
| - |
157 |
| - current_node.next_ptr = self.head.next_ptr |
158 |
| - self.head = self.head.next_ptr |
159 |
| - |
160 |
| - self.length -= 1 |
161 |
| - if not self.head: |
162 |
| - assert self.length == 0 |
163 |
| - |
164 |
| - def delete_rear(self) -> None: |
165 |
| - """ |
166 |
| - Removes the last node from the CircularLinkedList |
167 |
| - >>> cll = CircularLinkedList() |
168 |
| - >>> cll.delete_rear() |
169 |
| - Traceback (most recent call last): |
170 |
| - ... |
171 |
| - IndexError: Deleting from an empty list |
172 |
| - >>> cll.append(1) |
173 |
| - >>> cll.append(2) |
174 |
| - >>> print(f"{len(cll)}: {cll}") |
175 |
| - 2: <Node data=1> => <Node data=2> |
176 |
| - >>> cll.delete_rear() |
177 |
| - >>> print(f"{len(cll)}: {cll}") |
178 |
| - 1: <Node data=1> |
179 |
| - >>> cll.delete_rear() |
180 |
| - >>> print(f"{len(cll)}: {cll}") |
181 |
| - 0: Empty linked list |
182 |
| - """ |
183 |
| - if not self.head: |
184 |
| - raise IndexError("Deleting from an empty list") |
185 |
| - |
186 |
| - temp_node, current_node = self.head, self.head |
187 |
| - |
188 |
| - if current_node.next_ptr == current_node: |
189 |
| - self.head = None |
| 46 | + temp = self.head |
| 47 | + for _ in range(index - 1): |
| 48 | + temp = temp.next |
| 49 | + new_node.next = temp.next |
| 50 | + temp.next = new_node |
| 51 | + if index == len(self) - 1: # insert at tail |
| 52 | + self.tail = new_node |
| 53 | + |
| 54 | + def delete_front(self): |
| 55 | + return self.delete_nth(0) |
| 56 | + |
| 57 | + def delete_tail(self) -> None: |
| 58 | + return self.delete_nth(len(self) - 1) |
| 59 | + |
| 60 | + def delete_nth(self, index: int = 0): |
| 61 | + if not 0 <= index < len(self): |
| 62 | + raise IndexError("list index out of range.") |
| 63 | + delete_node = self.head |
| 64 | + if self.head == self.tail: # just one node |
| 65 | + self.head = self.tail = None |
| 66 | + elif index == 0: # delete head node |
| 67 | + self.tail.next = self.tail.next.next |
| 68 | + self.head = self.head.next |
190 | 69 | else:
|
191 |
| - while current_node.next_ptr != self.head: |
192 |
| - temp_node = current_node |
193 |
| - current_node = current_node.next_ptr |
| 70 | + temp = self.head |
| 71 | + for _ in range(index - 1): |
| 72 | + temp = temp.next |
| 73 | + delete_node = temp.next |
| 74 | + temp.next = temp.next.next |
| 75 | + if index == len(self) - 1: # delete at tail |
| 76 | + self.tail = temp |
| 77 | + return delete_node.data |
| 78 | + |
| 79 | + def is_empty(self): |
| 80 | + return len(self) == 0 |
194 | 81 |
|
195 |
| - temp_node.next_ptr = current_node.next_ptr |
196 | 82 |
|
197 |
| - self.length -= 1 |
198 |
| - if not self.head: |
199 |
| - assert self.length == 0 |
| 83 | +def test_circular_linked_list() -> None: |
| 84 | + """ |
| 85 | + >>> test_circular_linked_list() |
| 86 | + """ |
| 87 | + circular_linked_list = CircularLinkedList() |
| 88 | + assert len(circular_linked_list) == 0 |
| 89 | + assert circular_linked_list.is_empty() is True |
| 90 | + assert str(circular_linked_list) == "" |
| 91 | + |
| 92 | + try: |
| 93 | + circular_linked_list.delete_front() |
| 94 | + assert False # This should not happen |
| 95 | + except IndexError: |
| 96 | + assert True # This should happen |
| 97 | + |
| 98 | + try: |
| 99 | + circular_linked_list.delete_tail() |
| 100 | + assert False # This should not happen |
| 101 | + except IndexError: |
| 102 | + assert True # This should happen |
| 103 | + |
| 104 | + try: |
| 105 | + circular_linked_list.delete_nth(-1) |
| 106 | + assert False |
| 107 | + except IndexError: |
| 108 | + assert True |
| 109 | + |
| 110 | + try: |
| 111 | + circular_linked_list.delete_nth(0) |
| 112 | + assert False |
| 113 | + except IndexError: |
| 114 | + assert True |
| 115 | + |
| 116 | + assert circular_linked_list.is_empty() is True |
| 117 | + for i in range(5): |
| 118 | + assert len(circular_linked_list) == i |
| 119 | + circular_linked_list.insert_nth(i, i + 1) |
| 120 | + assert str(circular_linked_list) == "->".join(str(i) for i in range(1, 6)) |
| 121 | + |
| 122 | + circular_linked_list.insert_tail(6) |
| 123 | + assert str(circular_linked_list) == "->".join(str(i) for i in range(1, 7)) |
| 124 | + circular_linked_list.insert_head(0) |
| 125 | + assert str(circular_linked_list) == "->".join(str(i) for i in range(0, 7)) |
| 126 | + |
| 127 | + assert circular_linked_list.delete_front() == 0 |
| 128 | + assert circular_linked_list.delete_tail() == 6 |
| 129 | + assert str(circular_linked_list) == "->".join(str(i) for i in range(1, 6)) |
| 130 | + assert circular_linked_list.delete_nth(2) == 3 |
| 131 | + |
| 132 | + circular_linked_list.insert_nth(2, 3) |
| 133 | + assert str(circular_linked_list) == "->".join(str(i) for i in range(1, 6)) |
| 134 | + |
| 135 | + assert circular_linked_list.is_empty() is False |
200 | 136 |
|
201 | 137 |
|
202 | 138 | if __name__ == "__main__":
|
|
0 commit comments