-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlinked_list.py
198 lines (166 loc) · 5.87 KB
/
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
import timeit
from typing import Optional
class Node:
def __init__(self, value: int, next_node: Optional["Node"] = None) -> None:
self.__value = value
self.__next = next_node
@property
def value(self) -> int:
return self.__value
@property
def next(self) -> Optional["Node"]:
return self.__next
@next.setter
def next(self, elem: Optional["Node"]) -> None:
self.__next = elem
class LinkedList:
def __init__(self) -> None:
self.head = None
self.nodes_counter = 0
def __str__(self) -> str:
return " -> ".join([str(node) for node in self])
def __iter__(self) -> int:
current = self.head
while current:
yield current.value
current = current.next
def __len__(self) -> int:
return self.nodes_counter
def append(self, value_to_append: int) -> None:
"""The function appends a new node at the end of the linked list"""
new_node = Node(value_to_append)
if not self.head:
self.head = new_node
self.nodes_counter += 1
return
current = self.head
while current.next:
current = current.next
current.next = new_node
self.nodes_counter += 1
def insert(self, prev_position, value_to_insert: int) -> None:
"""The function inserts a new node after the given position number in linked list"""
current = self.find(prev_position)
new_node = Node(value_to_insert)
new_node.next = current.next
current.next = new_node
self.nodes_counter += 1
def find(self, position: int) -> Node:
"""The function search a node by specified position number in the linked list"""
if position > (len(self) - 1):
raise ValueError(
"The specified position ({}) is out of the current linked list length".format(
position
)
)
current = self.head
pointer = 0
while current:
if pointer == position:
return current
current = current.next
pointer += 1
def remove(self, position: int) -> None:
"""The function removes a node at the specified position of the linked list"""
if not self.head:
raise ValueError("The linked list is empty -> nothing to remove")
if position == 0:
self.head = self.head.next
self.nodes_counter -= 1
return
current = self.find(position)
prev = self.find(position - 1)
current = current.next
prev.next = current
self.nodes_counter -= 1
def swap(self, position1: int, position2: int) -> None:
"""The function swaps two nodes at the specified positions in the linked list"""
node1 = self.find(position1)
node2 = self.find(position2)
prev_node1 = self.find(position1 - 1)
prev_node2 = self.find(position2 - 1)
if prev_node1:
prev_node1.next = node2
else:
self.head = node2
if prev_node2:
prev_node2.next = node1
else:
self.head = node1
current = node1.next
node1.next = node2.next
node2.next = current
def bubble_sort(linked_list: LinkedList) -> None:
"""The function sorts a given linked list using Bubble sort method"""
for j in range(len(linked_list) - 1):
for i in range(len(linked_list) - 1 - j):
if linked_list.find(i).value > linked_list.find(i + 1).value:
linked_list.swap(i, i + 1)
def merge_lists(head1: Node, head2: Node) -> Optional["Node"]:
"""The function combines two linked lists, while sorting the elements in ascending order"""
if not head1:
return head2
if not head2:
return head1
temp_head = tail = Node(314159)
while head1 and head2:
if head1.value <= head2.value:
tail.next = head1
head1 = head1.next
else:
tail.next = head2
head2 = head2.next
tail = tail.next
tail.next = head1 or head2
return temp_head.next
def list_separator(head: Node) -> tuple[Node, Node | None]:
"""The function takes head of linked list, and splits the list into two"""
slow = head
fast = head.next
while fast:
fast = fast.next
if fast:
slow = slow.next
fast = fast.next
head1 = head
head2 = slow.next
slow.next = None
return head1, head2
def merge_sort_step(head: Node) -> Node:
"""
The function takes a head of a linked list and recursively calls the separator,
dividing the list into sub-lists until only one element remains in each, then sorts them by merging.
Will return a head of sorted list.
"""
if not head or not head.next:
return head
a, b = list_separator(head)
a = merge_sort_step(a)
b = merge_sort_step(b)
return merge_lists(a, b)
def merge_sort(linked_list: LinkedList) -> LinkedList:
"""The function initiates merge sorting algorithm"""
linked_list.head = merge_sort_step(linked_list.head)
return linked_list
def make_linked_list(data: list) -> LinkedList:
"""The function creates a linked list"""
ll = LinkedList()
for el in data:
ll.append(el)
return ll
def runtime_test(sort_method, data: list) -> None:
arr_for_test = [make_linked_list(data) for i in range(1000000)]
print(
"Average {.__name__} execution time: {: f} sec.".format(
sort_method,
timeit.timeit(lambda: sort_method(arr_for_test.pop()), number=1000000)
/ 1000000,
)
)
if __name__ == "__main__":
elements = []
with open("input.txt", "r") as f:
for item in f.readlines():
elements.append(int(item))
runtime_test(bubble_sort, elements)
runtime_test(merge_sort, elements)