Compare commits

..

5 Commits
develop ... 01

Author SHA1 Message Date
kirill
b515024d07 add report 2026-05-01 23:49:53 +03:00
kirill
d232cc536d add experiment and plots 2026-04-20 18:41:33 +03:00
kirill
da3aad1c55 add bst 2026-04-16 01:01:14 +03:00
kirill
f2df567d0f add ht 2026-04-13 19:02:45 +03:00
kirill
4cb30a7747 add ll 2026-04-13 13:24:59 +03:00
13 changed files with 679 additions and 0 deletions

View File

View File

@ -0,0 +1,63 @@
def bst_insert(root, name, phone):
if root is None:
return {'name': name, 'phone': phone, 'left': None, 'right': None}
if name < root['name']:
root['left'] = bst_insert(root['left'], name, phone)
elif name > root['name']:
root['right'] = bst_insert(root['right'], name, phone)
else:
root['phone'] = phone
return root
def bst_find(root, name):
if root is None:
return None
if name == root['name']:
return root['phone']
if name < root['name']:
return bst_find(root['left'], name)
else:
return bst_find(root['right'], name)
def get_min(node):
current = node
while current['left'] is not None:
current = current['left']
return current
def bst_delete(root, name):
if root is None:
return None
if name < root['name']:
root['left'] = bst_delete(root['left'], name)
elif name > root['name']:
root['right'] = bst_delete(root['right'], name)
else:
if root['left'] is None:
return root['right']
if root['right'] is None:
return root['left']
successor = get_min(root['right'])
root['name'] = successor['name']
root['phone'] = successor['phone']
root['right'] = bst_delete(root['right'], successor['name'])
return root
def bst_list_all(root, res = None):
if res is None:
res = []
if root is not None:
bst_list_all(root['left'], res)
res.append({'name': root['name'], 'phone': root['phone']})
bst_list_all(root['right'], res)
#сортировка уже сделана
return res
#проверка
# root = None
# root = bst_insert(root, "Ivan", "111")
# root = bst_insert(root, "Anna", "222") # Уйдет влево
# root = bst_insert(root, "Zina", "333") # Уйдет вправо
# print(root)

View File

@ -0,0 +1,28 @@
from codes.LL import ll_insert, ll_find, ll_delete
def ht_insert(buckets, name, phone):
index = hash(name) % len(buckets)
current_head = buckets[index]
new_head = ll_insert(current_head, name, phone)
buckets[index] = new_head
# print("-"*100)
# print(buskets)
def ht_find(buckets, name):
index = hash(name)%len(buckets)
slot_head = buckets[index]
res_ph = ll_find(slot_head, name)
return res_ph
def ht_delete(buskets, name):
index = hash(name)%len(buskets)
buskets[index] = ll_delete(buskets[index], name)
def ht_list_all(buckets):
all_rec = []
for head in buckets:
current = head
while current is not None:
all_rec.append((current['name'], current['phone']))
current = current['next']
all_rec.sort()
return all_rec

View File

@ -0,0 +1,46 @@
def ll_insert(head, name, phone):
current = head
while current is not None:
if current['name'] == name:
current['phone'] = phone
return head
current = current['next']
return {'name': name, 'phone': phone, 'next': head}
# проверка
# my_list = None
# my_list = ll_insert(my_list, "Ivan", "555")
# my_list = ll_insert(my_list, "An", "666")
# print(my_list)
def ll_find(head, name):
current = head
while current is not None:
if current['name'] == name:
return current['phone']
current = current['next']
return None
# print(ll_find(my_list,"An"))
# buskets = [None]*10
# print(buskets)
def ll_delete(head, name):
if head is None:
return None
current = head
if head['name'] == name:
return head['next']
while current['next'] is not None:
if current['next']['name'] == name:
current['next'] = current['next']['next']
break
current = current['next']
return head
def ll_list_all(head):
res = []
current = head
while current is not None:
res.append((current['name'], current['phone']))
current = current['next']
res.sort()
return res

View File

@ -0,0 +1,101 @@
import random
import time
import csv
import sys
sys.setrecursionlimit(20000)
from codes.LL import ll_insert, ll_find, ll_delete
from codes.HT import ht_insert, ht_delete, ht_find
from codes.BST import bst_delete, bst_find, bst_insert
# Экспериментальная часть
def generate_records(N):
records =[]
for i in range(N):
name = f"User_{i:05d}"
phone =str(random.randint(100000,999999))
records.append((name,phone))
return records
def run_test(structure_name, mode, input_data, search_names, delete_names):
print(f"Тест {structure_name} в режиме {mode}")
ins_times = []
find_times = []
del_times = []
for _ in range(5):
if structure_name == "LL":
container = None
elif structure_name == "BST":
container = None
elif structure_name == "HT":
container = [None]*150
#А
start = time.perf_counter()
for name, phone in input_data:
if structure_name == "LL":
container = ll_insert(container, name, phone)
elif structure_name == "BST":
container = bst_insert(container, name, phone)
elif structure_name == "HT":
ht_insert(container, name, phone)
ins_times.append(time.perf_counter() - start)
start = time.perf_counter()
for name in search_names:
if structure_name == "LL": ll_find(container, name)
elif structure_name == "BST": bst_find(container, name)
else: ht_find(container, name)
find_times.append(time.perf_counter() - start)
#В
start = time.perf_counter()
for name in delete_names:
if structure_name == "LL": container = ll_delete(container, name)
elif structure_name == "BST": container = bst_delete(container, name)
else: ht_delete(container, name)
del_times.append(time.perf_counter() - start)
results = []
for i in range(5):
results.append([structure_name, mode, f"Вставка (попытка {i+1})", ins_times[i]])
results.append([structure_name, mode, "Вставка СРЕДНЕЕ", sum(ins_times) / 5])
for i in range(5):
results.append([structure_name, mode, f"Поиск (попытка {i+1})", find_times[i]])
results.append([structure_name, mode, "Поиск СРЕДНЕЕ", sum(find_times) / 5])
for i in range(5):
results.append([structure_name, mode, f"Удаление (попытка {i+1})", del_times[i]])
results.append([structure_name, mode, "Удаление СРЕДНЕЕ", sum(del_times) / 5])
return results
def main_experiment():
N = 10000
data = generate_records(N)
random.shuffle(data)
data_sort = sorted(data, key = lambda x: x[0])
search_names = [r[0] for r in random.sample(data, 100)] + [f"None_{i}" for i in range(10)]
delete_names = [r[0] for r in random.sample(data, 50)]
results = [["Structure", "Mode", "Operation", "Time"]]
for mode_name, mode_data in [("shufled", data), ("sorted", data_sort)]:
results += run_test("LL", mode_name, mode_data, search_names, delete_names)
results += run_test("BST", mode_name, mode_data, search_names, delete_names)
results += run_test("HT", mode_name, mode_data, search_names, delete_names)
with open("results.csv", "w", newline = "") as f:
writer = csv.writer(f)
writer.writerows(results)
print("Результаты сохранены в файл")
if __name__ == "__main__":
main_experiment()

View File

@ -0,0 +1,59 @@
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
# Графики
def plot_all(df: pd.DataFrame):
structures = ["BST", "HT", "LL"]
operations = ["Вставка", "Поиск", "Удаление"]
op_keys = ["Вставка СРЕДНЕЕ", "Поиск СРЕДНЕЕ", "Удаление СРЕДНЕЕ"]
filenames = ["./insert_plot.png", "./search_plot.png", "./delete_plot.png"]
avg = df[df["Operation"].isin(op_keys)]
data = {
(row.Structure, row.Mode, row.Operation): row.Time
for row in avg.itertuples(index=False)
}
x = np.arange(len(structures))
width = 0.35
for op_label, op_key, filename in zip(operations, op_keys, filenames):
fig, ax = plt.subplots(figsize=(8, 5))
times_shuffled = [data.get((s, "shufled", op_key), float('nan')) for s in structures]
times_sorted = [data.get((s, "sorted", op_key), 0) for s in structures]
bars1 = ax.bar(x - width/2, times_shuffled, width, label="Случайный", color="steelblue")
bars2 = ax.bar(x + width/2, times_sorted, width, label="Отсортированный", color="red")
for bar in bars1:
height = bar.get_height()
ax.text(bar.get_x() + bar.get_width() / 2, height,
f"{height:.6f}", ha="center", va="bottom", fontsize=8)
for bar in bars2:
height = bar.get_height()
ax.text(bar.get_x() + bar.get_width() / 2, height,
f"{height:.6f}", ha="center", va="bottom", fontsize=8)
ax.set_yscale("log")
ax.set_title(op_label)
ax.set_ylabel("Время (сек)")
ax.set_xticks(x)
ax.set_xticklabels(structures)
ax.legend()
ax.grid(axis="y", linestyle="--", alpha=0.7)
plt.tight_layout()
plt.savefig(filename)
print(f"График сохранён: {filename}")
def main_plot():
results = pd.read_csv("./results.csv")
plot_all(results)
if __name__ == "__main__":
main_plot()

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,6 @@
from codes.plot import main_plot
from codes.experiment import main_experiment
if __name__ == "__main__":
main_experiment()
main_plot()

View File

@ -0,0 +1,109 @@
Structure,Mode,Operation,Time
LL,shufled,Вставка (попытка 1),2.4339829910004482
LL,shufled,Вставка (попытка 2),2.4039403810002113
LL,shufled,Вставка (попытка 3),2.458547864999673
LL,shufled,Вставка (попытка 4),2.413923469999645
LL,shufled,Вставка (попытка 5),2.4440171969999938
LL,shufled,Вставка СРЕДНЕЕ,2.430882380799994
LL,shufled,Поиск (попытка 1),0.031343970999841986
LL,shufled,Поиск (попытка 2),0.03304426499926194
LL,shufled,Поиск (попытка 3),0.03239262500028417
LL,shufled,Поиск (попытка 4),0.03132577799988212
LL,shufled,Поиск (попытка 5),0.03151892799996858
LL,shufled,Поиск СРЕДНЕЕ,0.03192511339984776
LL,shufled,Удаление (попытка 1),0.01929760500024713
LL,shufled,Удаление (попытка 2),0.019222485999307537
LL,shufled,Удаление (попытка 3),0.019446178000180225
LL,shufled,Удаление (попытка 4),0.019156967000526492
LL,shufled,Удаление (попытка 5),0.019182482000360324
LL,shufled,Удаление СРЕДНЕЕ,0.01926114360012434
BST,shufled,Вставка (попытка 1),0.02289151100012532
BST,shufled,Вставка (попытка 2),0.0229584500002602
BST,shufled,Вставка (попытка 3),0.02520326100056991
BST,shufled,Вставка (попытка 4),0.03390770399983012
BST,shufled,Вставка (попытка 5),0.022019966999323515
BST,shufled,Вставка СРЕДНЕЕ,0.025396178600021812
BST,shufled,Поиск (попытка 1),0.000189066000530147
BST,shufled,Поиск (попытка 2),0.00023037999926600605
BST,shufled,Поиск (попытка 3),0.00018933499995910097
BST,shufled,Поиск (попытка 4),0.0001907400001073256
BST,shufled,Поиск (попытка 5),0.00024124399988068035
BST,shufled,Поиск СРЕДНЕЕ,0.000208152999948652
BST,shufled,Удаление (попытка 1),0.00011542900028871372
BST,shufled,Удаление (попытка 2),0.0001963419999810867
BST,shufled,Удаление (попытка 3),0.00011738299963326426
BST,shufled,Удаление (попытка 4),0.00011437200009822845
BST,shufled,Удаление (попытка 5),0.00015602500025124755
BST,shufled,Удаление СРЕДНЕЕ,0.00013991020005050815
HT,shufled,Вставка (попытка 1),0.0291972099994382
HT,shufled,Вставка (попытка 2),0.02882972299994435
HT,shufled,Вставка (попытка 3),0.028241992999937793
HT,shufled,Вставка (попытка 4),0.02896075100034068
HT,shufled,Вставка (попытка 5),0.029606111000248347
HT,shufled,Вставка СРЕДНЕЕ,0.028967157599981874
HT,shufled,Поиск (попытка 1),0.0014172729997881106
HT,shufled,Поиск (попытка 2),0.0003571010001905961
HT,shufled,Поиск (попытка 3),0.0004458979992705281
HT,shufled,Поиск (попытка 4),0.0005671679991792189
HT,shufled,Поиск (попытка 5),0.0004517190000115079
HT,shufled,Поиск СРЕДНЕЕ,0.0006478317996879923
HT,shufled,Удаление (попытка 1),0.000430808000601246
HT,shufled,Удаление (попытка 2),0.000223173999984283
HT,shufled,Удаление (попытка 3),0.0001869629995780997
HT,shufled,Удаление (попытка 4),0.00021918600032222457
HT,shufled,Удаление (попытка 5),0.00026948999948217534
HT,shufled,Удаление СРЕДНЕЕ,0.0002659241999936057
LL,sorted,Вставка (попытка 1),2.546284423999168
LL,sorted,Вставка (попытка 2),2.527255480000349
LL,sorted,Вставка (попытка 3),2.4814426879993334
LL,sorted,Вставка (попытка 4),2.501784361999853
LL,sorted,Вставка (попытка 5),2.480424575000143
LL,sorted,Вставка СРЕДНЕЕ,2.507438305799769
LL,sorted,Поиск (попытка 1),0.030172253000273486
LL,sorted,Поиск (попытка 2),0.030333151999911934
LL,sorted,Поиск (попытка 3),0.030028871000467916
LL,sorted,Поиск (попытка 4),0.032277871000587766
LL,sorted,Поиск (попытка 5),0.030364938999809965
LL,sorted,Поиск СРЕДНЕЕ,0.030635417200210215
LL,sorted,Удаление (попытка 1),0.02085002199964947
LL,sorted,Удаление (попытка 2),0.021086609999656503
LL,sorted,Удаление (попытка 3),0.02087502099948324
LL,sorted,Удаление (попытка 4),0.021417887000097835
LL,sorted,Удаление (попытка 5),0.020822566000788356
LL,sorted,Удаление СРЕДНЕЕ,0.02101042119993508
BST,sorted,Вставка (попытка 1),11.841748112999994
BST,sorted,Вставка (попытка 2),11.75695861999975
BST,sorted,Вставка (попытка 3),11.649890955999581
BST,sorted,Вставка (попытка 4),11.535229737999543
BST,sorted,Вставка (попытка 5),11.569677194999713
BST,sorted,Вставка СРЕДНЕЕ,11.670700924399716
BST,sorted,Поиск (попытка 1),0.08916782800042711
BST,sorted,Поиск (попытка 2),0.09862517000055959
BST,sorted,Поиск (попытка 3),0.08865728399996442
BST,sorted,Поиск (попытка 4),0.08748506499978248
BST,sorted,Поиск (попытка 5),0.08942283200030943
BST,sorted,Поиск СРЕДНЕЕ,0.09067163580020861
BST,sorted,Удаление (попытка 1),0.046499003000462835
BST,sorted,Удаление (попытка 2),0.04566383200017299
BST,sorted,Удаление (попытка 3),0.04534191499988083
BST,sorted,Удаление (попытка 4),0.04480698100087466
BST,sorted,Удаление (попытка 5),0.045543646000623994
BST,sorted,Удаление СРЕДНЕЕ,0.04557107540040306
HT,sorted,Вставка (попытка 1),0.03348540300066816
HT,sorted,Вставка (попытка 2),0.03959386299993639
HT,sorted,Вставка (попытка 3),0.026414189000206534
HT,sorted,Вставка (попытка 4),0.028397822999977507
HT,sorted,Вставка (попытка 5),0.0283703630002492
HT,sorted,Вставка СРЕДНЕЕ,0.031252328200207555
HT,sorted,Поиск (попытка 1),0.00092922399926465
HT,sorted,Поиск (попытка 2),0.0005907800004933961
HT,sorted,Поиск (попытка 3),0.00031636099993193056
HT,sorted,Поиск (попытка 4),0.00034901999970315956
HT,sorted,Поиск (попытка 5),0.0003578830001060851
HT,sorted,Поиск СРЕДНЕЕ,0.0005086535998998443
HT,sorted,Удаление (попытка 1),0.0003968310002164799
HT,sorted,Удаление (попытка 2),0.0009146930005954346
HT,sorted,Удаление (попытка 3),0.000194480000573094
HT,sorted,Удаление (попытка 4),0.00020164000034128549
HT,sorted,Удаление (попытка 5),0.00023484999928768957
HT,sorted,Удаление СРЕДНЕЕ,0.0003884988002027967
1 Structure Mode Operation Time
2 LL shufled Вставка (попытка 1) 2.4339829910004482
3 LL shufled Вставка (попытка 2) 2.4039403810002113
4 LL shufled Вставка (попытка 3) 2.458547864999673
5 LL shufled Вставка (попытка 4) 2.413923469999645
6 LL shufled Вставка (попытка 5) 2.4440171969999938
7 LL shufled Вставка СРЕДНЕЕ 2.430882380799994
8 LL shufled Поиск (попытка 1) 0.031343970999841986
9 LL shufled Поиск (попытка 2) 0.03304426499926194
10 LL shufled Поиск (попытка 3) 0.03239262500028417
11 LL shufled Поиск (попытка 4) 0.03132577799988212
12 LL shufled Поиск (попытка 5) 0.03151892799996858
13 LL shufled Поиск СРЕДНЕЕ 0.03192511339984776
14 LL shufled Удаление (попытка 1) 0.01929760500024713
15 LL shufled Удаление (попытка 2) 0.019222485999307537
16 LL shufled Удаление (попытка 3) 0.019446178000180225
17 LL shufled Удаление (попытка 4) 0.019156967000526492
18 LL shufled Удаление (попытка 5) 0.019182482000360324
19 LL shufled Удаление СРЕДНЕЕ 0.01926114360012434
20 BST shufled Вставка (попытка 1) 0.02289151100012532
21 BST shufled Вставка (попытка 2) 0.0229584500002602
22 BST shufled Вставка (попытка 3) 0.02520326100056991
23 BST shufled Вставка (попытка 4) 0.03390770399983012
24 BST shufled Вставка (попытка 5) 0.022019966999323515
25 BST shufled Вставка СРЕДНЕЕ 0.025396178600021812
26 BST shufled Поиск (попытка 1) 0.000189066000530147
27 BST shufled Поиск (попытка 2) 0.00023037999926600605
28 BST shufled Поиск (попытка 3) 0.00018933499995910097
29 BST shufled Поиск (попытка 4) 0.0001907400001073256
30 BST shufled Поиск (попытка 5) 0.00024124399988068035
31 BST shufled Поиск СРЕДНЕЕ 0.000208152999948652
32 BST shufled Удаление (попытка 1) 0.00011542900028871372
33 BST shufled Удаление (попытка 2) 0.0001963419999810867
34 BST shufled Удаление (попытка 3) 0.00011738299963326426
35 BST shufled Удаление (попытка 4) 0.00011437200009822845
36 BST shufled Удаление (попытка 5) 0.00015602500025124755
37 BST shufled Удаление СРЕДНЕЕ 0.00013991020005050815
38 HT shufled Вставка (попытка 1) 0.0291972099994382
39 HT shufled Вставка (попытка 2) 0.02882972299994435
40 HT shufled Вставка (попытка 3) 0.028241992999937793
41 HT shufled Вставка (попытка 4) 0.02896075100034068
42 HT shufled Вставка (попытка 5) 0.029606111000248347
43 HT shufled Вставка СРЕДНЕЕ 0.028967157599981874
44 HT shufled Поиск (попытка 1) 0.0014172729997881106
45 HT shufled Поиск (попытка 2) 0.0003571010001905961
46 HT shufled Поиск (попытка 3) 0.0004458979992705281
47 HT shufled Поиск (попытка 4) 0.0005671679991792189
48 HT shufled Поиск (попытка 5) 0.0004517190000115079
49 HT shufled Поиск СРЕДНЕЕ 0.0006478317996879923
50 HT shufled Удаление (попытка 1) 0.000430808000601246
51 HT shufled Удаление (попытка 2) 0.000223173999984283
52 HT shufled Удаление (попытка 3) 0.0001869629995780997
53 HT shufled Удаление (попытка 4) 0.00021918600032222457
54 HT shufled Удаление (попытка 5) 0.00026948999948217534
55 HT shufled Удаление СРЕДНЕЕ 0.0002659241999936057
56 LL sorted Вставка (попытка 1) 2.546284423999168
57 LL sorted Вставка (попытка 2) 2.527255480000349
58 LL sorted Вставка (попытка 3) 2.4814426879993334
59 LL sorted Вставка (попытка 4) 2.501784361999853
60 LL sorted Вставка (попытка 5) 2.480424575000143
61 LL sorted Вставка СРЕДНЕЕ 2.507438305799769
62 LL sorted Поиск (попытка 1) 0.030172253000273486
63 LL sorted Поиск (попытка 2) 0.030333151999911934
64 LL sorted Поиск (попытка 3) 0.030028871000467916
65 LL sorted Поиск (попытка 4) 0.032277871000587766
66 LL sorted Поиск (попытка 5) 0.030364938999809965
67 LL sorted Поиск СРЕДНЕЕ 0.030635417200210215
68 LL sorted Удаление (попытка 1) 0.02085002199964947
69 LL sorted Удаление (попытка 2) 0.021086609999656503
70 LL sorted Удаление (попытка 3) 0.02087502099948324
71 LL sorted Удаление (попытка 4) 0.021417887000097835
72 LL sorted Удаление (попытка 5) 0.020822566000788356
73 LL sorted Удаление СРЕДНЕЕ 0.02101042119993508
74 BST sorted Вставка (попытка 1) 11.841748112999994
75 BST sorted Вставка (попытка 2) 11.75695861999975
76 BST sorted Вставка (попытка 3) 11.649890955999581
77 BST sorted Вставка (попытка 4) 11.535229737999543
78 BST sorted Вставка (попытка 5) 11.569677194999713
79 BST sorted Вставка СРЕДНЕЕ 11.670700924399716
80 BST sorted Поиск (попытка 1) 0.08916782800042711
81 BST sorted Поиск (попытка 2) 0.09862517000055959
82 BST sorted Поиск (попытка 3) 0.08865728399996442
83 BST sorted Поиск (попытка 4) 0.08748506499978248
84 BST sorted Поиск (попытка 5) 0.08942283200030943
85 BST sorted Поиск СРЕДНЕЕ 0.09067163580020861
86 BST sorted Удаление (попытка 1) 0.046499003000462835
87 BST sorted Удаление (попытка 2) 0.04566383200017299
88 BST sorted Удаление (попытка 3) 0.04534191499988083
89 BST sorted Удаление (попытка 4) 0.04480698100087466
90 BST sorted Удаление (попытка 5) 0.045543646000623994
91 BST sorted Удаление СРЕДНЕЕ 0.04557107540040306
92 HT sorted Вставка (попытка 1) 0.03348540300066816
93 HT sorted Вставка (попытка 2) 0.03959386299993639
94 HT sorted Вставка (попытка 3) 0.026414189000206534
95 HT sorted Вставка (попытка 4) 0.028397822999977507
96 HT sorted Вставка (попытка 5) 0.0283703630002492
97 HT sorted Вставка СРЕДНЕЕ 0.031252328200207555
98 HT sorted Поиск (попытка 1) 0.00092922399926465
99 HT sorted Поиск (попытка 2) 0.0005907800004933961
100 HT sorted Поиск (попытка 3) 0.00031636099993193056
101 HT sorted Поиск (попытка 4) 0.00034901999970315956
102 HT sorted Поиск (попытка 5) 0.0003578830001060851
103 HT sorted Поиск СРЕДНЕЕ 0.0005086535998998443
104 HT sorted Удаление (попытка 1) 0.0003968310002164799
105 HT sorted Удаление (попытка 2) 0.0009146930005954346
106 HT sorted Удаление (попытка 3) 0.000194480000573094
107 HT sorted Удаление (попытка 4) 0.00020164000034128549
108 HT sorted Удаление (попытка 5) 0.00023484999928768957
109 HT sorted Удаление СРЕДНЕЕ 0.0003884988002027967

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

267
svetlakovkyu/docs/report.md Normal file
View File

@ -0,0 +1,267 @@
# Задание 1: Структуры данных
>Выполнил: Светлаков Кирилл
>
>Студент 426 группы
## Цель работы
Реализовать три различные структуры данных «с нуля», применить их для хранения записей телефонного справочника и экспериментально сравнить производительность основных операций. Необходимо собственными руками написать код, чтобы понять внутреннее устройство связного списка, хеш-таблицы и двоичного дерева поиска, а также осознать их сильные и слабые стороны на практике.
---
## 1. Реализация структур данных
### 1.1 Связный список (Linked List)
```python
def ll_insert(head, name, phone):
current = head
while current is not None:
if current['name'] == name:
current['phone'] = phone
return head
current = current['next']
return {'name': name, 'phone': phone, 'next': head}
def ll_find(head, name):
current = head
while current is not None:
if current['name'] == name:
return current['phone']
current = current['next']
return None
def ll_delete(head, name):
if head is None:
return None
current = head
if head['name'] == name:
return head['next']
while current['next'] is not None:
if current['next']['name'] == name:
current['next'] = current['next']['next']
break
current = current['next']
return head
def ll_list_all(head):
res = []
current = head
while current is not None:
res.append((current['name'], current['phone']))
current = current['next']
res.sort()
return res
```
---
### 1.2 Хеш-таблица (Hash Table)
```python
def ht_insert(buckets, name, phone):
index = hash(name) % len(buckets)
current_head = buckets[index]
new_head = ll_insert(current_head, name, phone)
buckets[index] = new_head
def ht_find(buckets, name):
index = hash(name)%len(buckets)
slot_head = buckets[index]
res_ph = ll_find(slot_head, name)
return res_ph
def ht_delete(buskets, name):
index = hash(name)%len(buskets)
buskets[index] = ll_delete(buskets[index], name)
def ht_list_all(buckets):
all_rec = []
for head in buckets:
current = head
while current is not None:
all_rec.append((current['name'], current['phone']))
current = current['next']
all_rec.sort()
return all_rec
```
---
### 1.3 Двоичное дерево поиска (BST)
```python
def bst_insert(root, name, phone):
if root is None:
return {'name': name, 'phone': phone, 'left': None, 'right': None}
if name < root['name']:
root['left'] = bst_insert(root['left'], name, phone)
elif name > root['name']:
root['right'] = bst_insert(root['right'], name, phone)
else:
root['phone'] = phone
return root
def bst_find(root, name):
if root is None:
return None
if name == root['name']:
return root['phone']
if name < root['name']:
return bst_find(root['left'], name)
else:
return bst_find(root['right'], name)
def get_min(node):
current = node
while current['left'] is not None:
current = current['left']
return current
def bst_delete(root, name):
if root is None:
return None
if name < root['name']:
root['left'] = bst_delete(root['left'], name)
elif name > root['name']:
root['right'] = bst_delete(root['right'], name)
else:
if root['left'] is None:
return root['right']
if root['right'] is None:
return root['left']
successor = get_min(root['right'])
root['name'] = successor['name']
root['phone'] = successor['phone']
root['right'] = bst_delete(root['right'], successor['name'])
return root
def bst_list_all(root, res = None):
if res is None:
res = []
if root is not None:
bst_list_all(root['left'], res)
res.append({'name': root['name'], 'phone': root['phone']})
bst_list_all(root['right'], res)
#сортировка уже сделана
return res
```
---
## 2. Эксперимент
Для экспериментального сравнения были сгенерированы 10 000 записей вида `(User_XXXXX, номерелефона)`.
Каждый тест проводился в двух режимах входных данных:
- Случайный (shuffled) - записи перемешаны в произвольном порядке
- Отсортированный (sorted) - записи отсортированы по имени
Для каждой структуры и режима измерялось время выполнения трёх операций:
- Вставка - 10 000 элементов
- Поиск - 110 запросов (100 существующих + 10 несуществующих)
- Удаление - 50 элементов
Каждый замер повторялся 5 раз, итоговое значение - среднее арифметическое.
В файл `results.py` записывались в следующем виде
| Structure | Mode | Operation | Time |
|---|---|---|---|
| Название структуры: LL, BST, HT | Режим данных: shufled, sorted | Операция и номер попытки или среднее | Время выполнения в секундах |
| LL | shufled | Вставка (попытка 1) | 2.730272 |
| LL | shufled | Вставка (попытка 2) | 2.675253 |
| LL | shufled | Вставка (попытка 3) | 2.628982 |
| LL | shufled | Вставка (попытка 4) | 2.673355 |
| LL | shufled | Вставка (попытка 5) | 2.636129 |
| LL | shufled | Вставка СРЕДНЕЕ | 2.668798 |
| ... | ... | ... | ... |
---
## 3. Результаты
### 3.1 Вставка
![График 1](insert_plot.png)
### 3.2 Поиск
![График 2](search_plot.png)
### 3.3 Удаление
![График 3](delete_plot.png)
---
## 4. Анализ результатов
### 4.1 Влияние порядка данных на BST: деградация до O(n)
При вставке случайных данных BST ведёт себя как сбалансированное дерево: каждый новый ключ с равной вероятностью уходит влево или вправо, глубина дерева составляет ~log₂(10000) ≈ 13. Операция вставки 10 000 записей заняла 0.025 сек.
При вставке отсортированных данных каждый новый ключ оказывается больше предыдущего и всегда уходит вправо. Дерево вырождается в линейный список глубиной 10 000. Каждая следующая вставка проходит на один шаг дальше, итоговая сложность становится O(1 + 2 + ... + n) = O(n²). Именно поэтому вставка отсортированных данных заняла 13.16 сек - более чем в 500 раз медленнее случайных.
Это фундаментальный недостаток, реализованного BST.
---
### 4.2 Нечувствительность хеш-таблицы к порядку данных
Хеш-таблица вычисляет позицию элемента через `hash(name) % len(buckets)`. Функция `hash()` в Python зависит только от значения ключа, но никак не от порядка вставки. Независимо от того, отсортированы данные или перемешаны, каждый ключ попадает в тот же бакет с той же скоростью.
Это подтверждается полученными результатами: время вставки для случайных данных - 0.0281 сек, для отсортированных - 0.0286 сек, что практически одинаково.
В графике для поиска и удаления, моожно заметить примено такой же результат - время работы программы для отсортированных и не для неотсортированных данных одинаково.
---
### 4.3 Медленный поиск в связном списке
Связный список не имеет структуры, позволяющей перейти к нужному элементу. При поиске приходится последовательно проходить узел за узлом от головы до нужного элемента - это линейный поиск O(n).
При N = 10 000 и 110 запросах среднее время поиска составило 0.03 сек, в ~150 раз медленнее поиска в BST и в ~75 раз медленнее поиска в HT на случайных данных.
Порядок данных на LL практически не влияет: в любом случае нужно проходить примерно половину списка для найденных и весь список для несуществующих записей.
---
### 4.4 Удаление в каждой структуре
Связный список: удаление требует сначала найти удаляемый элемент - O(n). Затем достаточно переключить одну ссылку у предшественника. Итог: O(n) за счёт поиска. При 50 удалениях время составило ~0.02 сек.
Хеш-таблица: вычисляем бакет за O(1), затем удаляем элемент из короткой цепочки. Итог: O(1) амортизированно. При 50 удалениях время составило 0.00003 сек для случайных и 0.00004 для отсортированых данных, что значительно быстрее связного списка, но медленее BST(для неотсортированных данных).
BST (случайные данные): находим узел за O(log n). Если у него два потомка - находим минимум правого поддерева, копируем его значение и рекурсивно удаляем его. Итог: O(log n). При 50 удалениях время составило 0.00012 сек.
BST (отсортированные данные): дерево вырождено в список, глубина O(n). Каждое удаление - O(n), 50 удалений - 0.060 сек, что медленнее даже связного списка.
---
## 5. Выводы
Из результатов эксперимента, можно сделать вывод, что нет универсальной структуры данных, и под конкретную задачу надо выбирать определенную.
Хеш-таблица - лучший выбор, если нужны быстрые вставка, поиск и удаление и порядок хранения данных не важен. Идеальна для кешей, словарей, телефонных справочников, где операции выполняются в O(1). Не подходит для задач, требующих обхода данных в отсортированном порядке.
BST - лучший выбор, когда важен порядок данных: обход дерева in-order даёт отсортированную последовательность за O(n), можно быстро найти минимум/максимум или диапазон ключей. Подходит для задач типа «найти все записи от A до B». Критически важно использовать только на случайных или специально перемешанных данных.
Связный список - подходит для задач, где данные постоянно добавляются и удаляются с известной позиции (начало/конец списка), а поиск по значению происходит редко. В телефонном справочнике с 10 000 записей является наихудшим вариантом из трёх.
| Задача | Лучшая структура |
|---|---|
| Частые вставки и удаления по ключу | Хеш-таблица |
| Частый поиск по ключу | Хеш-таблица |
| Обход данных в отсортированном порядке | BST |
| Поиск по диапазону ключей | BST |
| Встава/удаление в начало/конец | Связный список |
| Данные приходят отсортированными, нужен быстрый поиск | Хеш-таблица |