forked from UNN/2026-rff_mp
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 82e988c965 | |||
| 58daf860ed | |||
| fe9ce65eb2 | |||
| 3a251f06c7 |
0
kalinovskiymi/428
Normal file
0
kalinovskiymi/428
Normal file
|
|
@ -1,63 +0,0 @@
|
||||||
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)
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
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
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
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
|
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
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()
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
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.
|
Before Width: | Height: | Size: 21 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 22 KiB |
|
|
@ -1,6 +0,0 @@
|
||||||
from codes.plot import main_plot
|
|
||||||
from codes.experiment import main_experiment
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main_experiment()
|
|
||||||
main_plot()
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
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
|
|
||||||
|
Binary file not shown.
|
Before Width: | Height: | Size: 22 KiB |
|
|
@ -1,267 +0,0 @@
|
||||||
# Задание 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 Вставка
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
### 3.2 Поиск
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
### 3.3 Удаление
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 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 |
|
|
||||||
| Встава/удаление в начало/конец | Связный список |
|
|
||||||
| Данные приходят отсортированными, нужен быстрый поиск | Хеш-таблица |
|
|
||||||
Loading…
Reference in New Issue
Block a user