308 lines
9.0 KiB
Python
308 lines
9.0 KiB
Python
|
|
import random
|
||
|
|
import time
|
||
|
|
import csv
|
||
|
|
import os
|
||
|
|
import matplotlib.pyplot as plt
|
||
|
|
import numpy as np
|
||
|
|
|
||
|
|
from sys import setrecursionlimit
|
||
|
|
|
||
|
|
setrecursionlimit(20000)
|
||
|
|
|
||
|
|
|
||
|
|
def ll_insert(head, name, phone):
|
||
|
|
new_node = {'name': name, 'phone': phone, 'next': None}
|
||
|
|
if head is None:
|
||
|
|
return new_node
|
||
|
|
current = head
|
||
|
|
while current:
|
||
|
|
if current['name'] == name:
|
||
|
|
current['phone'] = phone
|
||
|
|
return head
|
||
|
|
if current['next'] is None:
|
||
|
|
break
|
||
|
|
current = current['next']
|
||
|
|
current['next'] = new_node
|
||
|
|
return head
|
||
|
|
|
||
|
|
|
||
|
|
def ll_find(head, name):
|
||
|
|
current = head
|
||
|
|
while current:
|
||
|
|
if current['name'] == name:
|
||
|
|
return current['phone']
|
||
|
|
current = current['next']
|
||
|
|
return None
|
||
|
|
|
||
|
|
|
||
|
|
def ll_delete(head, name):
|
||
|
|
if head is None:
|
||
|
|
return None
|
||
|
|
if head['name'] == name:
|
||
|
|
return head['next']
|
||
|
|
prev = head
|
||
|
|
current = head['next']
|
||
|
|
while current:
|
||
|
|
if current['name'] == name:
|
||
|
|
prev['next'] = current['next']
|
||
|
|
return head
|
||
|
|
prev = current
|
||
|
|
current = current['next']
|
||
|
|
return head
|
||
|
|
|
||
|
|
|
||
|
|
def ll_list_all(head):
|
||
|
|
records = []
|
||
|
|
current = head
|
||
|
|
while current:
|
||
|
|
records.append((current['name'], current['phone']))
|
||
|
|
current = current['next']
|
||
|
|
records.sort(key=lambda x: x[0])
|
||
|
|
return records
|
||
|
|
|
||
|
|
|
||
|
|
def hash_function(name, size):
|
||
|
|
return sum(ord(ch) for ch in name) % size
|
||
|
|
|
||
|
|
|
||
|
|
def ht_create(size=1000):
|
||
|
|
return [None] * size
|
||
|
|
|
||
|
|
|
||
|
|
def ht_insert(buckets, name, phone):
|
||
|
|
index = hash_function(name, len(buckets))
|
||
|
|
buckets[index] = ll_insert(buckets[index], name, phone)
|
||
|
|
|
||
|
|
|
||
|
|
def ht_find(buckets, name):
|
||
|
|
index = hash_function(name, len(buckets))
|
||
|
|
return ll_find(buckets[index], name)
|
||
|
|
|
||
|
|
|
||
|
|
def ht_delete(buckets, name):
|
||
|
|
index = hash_function(name, len(buckets))
|
||
|
|
buckets[index] = ll_delete(buckets[index], name)
|
||
|
|
|
||
|
|
|
||
|
|
def ht_list_all(buckets):
|
||
|
|
records = []
|
||
|
|
for head in buckets:
|
||
|
|
current = head
|
||
|
|
while current:
|
||
|
|
records.append((current['name'], current['phone']))
|
||
|
|
current = current['next']
|
||
|
|
records.sort(key=lambda x: x[0])
|
||
|
|
return records
|
||
|
|
|
||
|
|
|
||
|
|
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']
|
||
|
|
elif name < root['name']:
|
||
|
|
return bst_find(root['left'], name)
|
||
|
|
else:
|
||
|
|
return bst_find(root['right'], name)
|
||
|
|
|
||
|
|
|
||
|
|
def bst_min_node(node):
|
||
|
|
current = node
|
||
|
|
while current and current['left']:
|
||
|
|
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']
|
||
|
|
elif root['right'] is None:
|
||
|
|
return root['left']
|
||
|
|
temp = bst_min_node(root['right'])
|
||
|
|
root['name'] = temp['name']
|
||
|
|
root['phone'] = temp['phone']
|
||
|
|
root['right'] = bst_delete(root['right'], temp['name'])
|
||
|
|
return root
|
||
|
|
|
||
|
|
|
||
|
|
def bst_list_all(root, result=None):
|
||
|
|
if result is None:
|
||
|
|
result = []
|
||
|
|
if root:
|
||
|
|
bst_list_all(root['left'], result)
|
||
|
|
result.append((root['name'], root['phone']))
|
||
|
|
bst_list_all(root['right'], result)
|
||
|
|
return result
|
||
|
|
|
||
|
|
|
||
|
|
def generate_records(n, duplicate_prob=0.1):
|
||
|
|
records = []
|
||
|
|
for i in range(n):
|
||
|
|
if random.random() < duplicate_prob and i > 0:
|
||
|
|
name = records[random.randint(0, i - 1)][0]
|
||
|
|
else:
|
||
|
|
name = f"User_{random.randint(0, n * 2)}"
|
||
|
|
phone = f"+7-999-{random.randint(1000000, 9999999)}"
|
||
|
|
records.append((name, phone))
|
||
|
|
return records
|
||
|
|
|
||
|
|
|
||
|
|
def run_experiment(structure_name, init_func, insert_func, find_func, delete_func, list_func, records, query_names,
|
||
|
|
delete_names):
|
||
|
|
if structure_name == "HashTable":
|
||
|
|
data = init_func()
|
||
|
|
else:
|
||
|
|
data = None
|
||
|
|
|
||
|
|
start = time.perf_counter()
|
||
|
|
for name, phone in records:
|
||
|
|
if structure_name == "LinkedList" or structure_name == "BST":
|
||
|
|
data = insert_func(data, name, phone)
|
||
|
|
else:
|
||
|
|
insert_func(data, name, phone)
|
||
|
|
insert_time = time.perf_counter() - start
|
||
|
|
|
||
|
|
start = time.perf_counter()
|
||
|
|
for name in query_names:
|
||
|
|
find_func(data, name)
|
||
|
|
find_time = time.perf_counter() - start
|
||
|
|
|
||
|
|
start = time.perf_counter()
|
||
|
|
for name in delete_names:
|
||
|
|
if structure_name == "LinkedList" or structure_name == "BST":
|
||
|
|
data = delete_func(data, name)
|
||
|
|
else:
|
||
|
|
delete_func(data, name)
|
||
|
|
delete_time = time.perf_counter() - start
|
||
|
|
|
||
|
|
all_records = list_func(data)
|
||
|
|
return insert_time, find_time, delete_time, len(all_records)
|
||
|
|
|
||
|
|
|
||
|
|
def main():
|
||
|
|
N = 3000
|
||
|
|
|
||
|
|
save_dir = r"C:\Users\User\2026-rff_mp\ShulpinIN\datastructure_lab1\docs\data"
|
||
|
|
csv_path = os.path.join(save_dir, "results.csv")
|
||
|
|
graph_path = os.path.join(save_dir, "performance_comparison.png")
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
records_original = generate_records(N, duplicate_prob=0.05)
|
||
|
|
records_shuffled = records_original.copy()
|
||
|
|
random.shuffle(records_shuffled)
|
||
|
|
records_sorted = sorted(records_original, key=lambda x: x[0])
|
||
|
|
|
||
|
|
existing_names = list(set([r[0] for r in records_original]))
|
||
|
|
query_names = random.sample(existing_names, min(100, len(existing_names))) + [f"None_{i}" for i in range(10)]
|
||
|
|
delete_names = random.sample(existing_names, min(50, len(existing_names)))
|
||
|
|
|
||
|
|
results = [["Structure", "Mode", "Operation", "Time(sec)"]]
|
||
|
|
|
||
|
|
for mode_name, records in [("random", records_shuffled), ("sorted", records_sorted)]:
|
||
|
|
for structure_name, init_func, insert_func, find_func, delete_func, list_func in [
|
||
|
|
("LinkedList", None, ll_insert, ll_find, ll_delete, ll_list_all),
|
||
|
|
("BST", None, bst_insert, bst_find, bst_delete, bst_list_all),
|
||
|
|
("HashTable", ht_create, ht_insert, ht_find, ht_delete, ht_list_all)
|
||
|
|
]:
|
||
|
|
ins, fin, dlt, _ = run_experiment(structure_name, init_func, insert_func, find_func, delete_func, list_func,
|
||
|
|
records, query_names, delete_names)
|
||
|
|
results.append([structure_name, mode_name, "insert", ins])
|
||
|
|
results.append([structure_name, mode_name, "search_110", fin])
|
||
|
|
results.append([structure_name, mode_name, "delete_50", dlt])
|
||
|
|
|
||
|
|
|
||
|
|
with open(csv_path, "w", newline="", encoding='utf-8') as f:
|
||
|
|
writer = csv.writer(f)
|
||
|
|
writer.writerows(results)
|
||
|
|
|
||
|
|
print(f"Results saved to {csv_path}")
|
||
|
|
|
||
|
|
|
||
|
|
structures = ["LinkedList", "HashTable", "BST"]
|
||
|
|
|
||
|
|
random_insert = []
|
||
|
|
random_search = []
|
||
|
|
random_delete = []
|
||
|
|
sorted_insert = []
|
||
|
|
sorted_search = []
|
||
|
|
sorted_delete = []
|
||
|
|
|
||
|
|
for row in results[1:]:
|
||
|
|
structure, mode, operation, time_val = row
|
||
|
|
if mode == "random" and operation == "insert":
|
||
|
|
random_insert.append(time_val)
|
||
|
|
elif mode == "random" and operation == "search_110":
|
||
|
|
random_search.append(time_val)
|
||
|
|
elif mode == "random" and operation == "delete_50":
|
||
|
|
random_delete.append(time_val)
|
||
|
|
elif mode == "sorted" and operation == "insert":
|
||
|
|
sorted_insert.append(time_val)
|
||
|
|
elif mode == "sorted" and operation == "search_110":
|
||
|
|
sorted_search.append(time_val)
|
||
|
|
elif mode == "sorted" and operation == "delete_50":
|
||
|
|
sorted_delete.append(time_val)
|
||
|
|
|
||
|
|
# Построение и сохранение графика
|
||
|
|
fig, axes = plt.subplots(1, 3, figsize=(18, 6))
|
||
|
|
x = np.arange(len(structures))
|
||
|
|
width = 0.35
|
||
|
|
|
||
|
|
axes[0].bar(x - width / 2, random_insert, width, label="Random", color="steelblue")
|
||
|
|
axes[0].bar(x + width / 2, sorted_insert, width, label="Sorted", color="coral")
|
||
|
|
axes[0].set_xticks(x)
|
||
|
|
axes[0].set_xticklabels(structures)
|
||
|
|
axes[0].set_ylabel("Time (sec)")
|
||
|
|
axes[0].set_title("Insert")
|
||
|
|
axes[0].legend()
|
||
|
|
axes[0].grid(True)
|
||
|
|
|
||
|
|
axes[1].bar(x - width / 2, random_search, width, label="Random", color="steelblue")
|
||
|
|
axes[1].bar(x + width / 2, sorted_search, width, label="Sorted", color="coral")
|
||
|
|
axes[1].set_xticks(x)
|
||
|
|
axes[1].set_xticklabels(structures)
|
||
|
|
axes[1].set_ylabel("Time (sec)")
|
||
|
|
axes[1].set_title("Search")
|
||
|
|
axes[1].legend()
|
||
|
|
axes[1].grid(True)
|
||
|
|
|
||
|
|
axes[2].bar(x - width / 2, random_delete, width, label="Random", color="steelblue")
|
||
|
|
axes[2].bar(x + width / 2, sorted_delete, width, label="Sorted", color="coral")
|
||
|
|
axes[2].set_xticks(x)
|
||
|
|
axes[2].set_xticklabels(structures)
|
||
|
|
axes[2].set_ylabel("Time (sec)")
|
||
|
|
axes[2].set_title("Delete")
|
||
|
|
axes[2].legend()
|
||
|
|
axes[2].grid(True)
|
||
|
|
|
||
|
|
plt.tight_layout()
|
||
|
|
|
||
|
|
|
||
|
|
plt.savefig(graph_path, dpi=300)
|
||
|
|
print(f"Graph saved to {graph_path}")
|
||
|
|
|
||
|
|
|
||
|
|
plt.show()
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|