From b26bb1d3e7c71b4e306bc13c54d71bc026b3622e Mon Sep 17 00:00:00 2001 From: danch0us Date: Wed, 20 May 2026 20:09:48 +0300 Subject: [PATCH 1/3] =?UTF-8?q?=D0=B7=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5?= =?UTF-8?q?=201?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kornevma/main.py | 146 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 kornevma/main.py diff --git a/kornevma/main.py b/kornevma/main.py new file mode 100644 index 0000000..f28ebc7 --- /dev/null +++ b/kornevma/main.py @@ -0,0 +1,146 @@ +import random +import time +import sys + +def mr(name, phone): + return {"name": name, "phone": phone} + +#llист +def ll_create_node(record): + return [record, None] + +def ll_insert(ll_head, record): + new_node = ll_create_node(record) + new_node[1] = ll_head[0] + ll_head[0] = new_node + +def ll_find(ll_head, name): + cur = ll_head[0] + while cur: + if cur[0]["name"] == name: + return cur[0] + cur = cur[1] + return None + +def ll_delete(ll_head, name): + cur = ll_head[0] + prev = None + while cur: + if cur[0]["name"] == name: + if prev: + prev[1] = cur[1] + else: + ll_head[0] = cur[1] + return True + prev = cur + cur = cur[1] + return False + +def ll_list_all(ll_head): + res = [] + cur = ll_head[0] + while cur: + res.append(cur[0]) + cur = cur[1] + return res + +#ht +def ht_hash(name, size): + return hash(name) % size +def ht_insert(table, record): + idx = ht_hash(record["name"], len(table)) + new_node = ll_create_node(record) + new_node[1] = table[idx] + table[idx] = new_node + +def ht_find(table, name): + idx = ht_hash(name, len(table)) + cur = table[idx] + while cur: + if cur[0]["name"] == name: + return cur[0] + cur = cur[1] + return None + +def ht_delete(table, name): + idx = ht_hash(name, len(table)) + cur = table[idx] + prev = None + while cur: + if cur[0]["name"] == name: + if prev: + prev[1] = cur[1] + else: + table[idx] = cur[1] + return True + prev = cur + cur = cur[1] + return False + +def ht_list_all(table): + res = [] + for head in table: + cur = head + while cur: + res.append(cur[0]) + cur = cur[1] + return res + + +# bst +def bst_create_node(record): + return [record, None, None] + +def bst_insert(root, record): + if root is None: + return bst_create_node(record) + if record["name"] < root[0]["name"]: + root[1] = bst_insert(root[1], record) + elif record["name"] > root[0]["name"]: + root[2] = bst_insert(root[2], record) + else: #мб + root[0] = record + return root + +def bst_find(root, name): + if root is None: + return None + if name == root[0]["name"]: + return root[0] + elif name < root[0]["name"]: + return bst_find(root[1], name) + else: + return bst_find(root[2], name) + +def bst_find_min(node): + while node[1] is not None: + node = node[1] + return node + +def bst_delete(root, name): + if root is None: + return None + if name < root[0]["name"]: + root[1] = bst_delete(root[1], name) + elif name > root[0]["name"]: + root[2] = bst_delete(root[2], name) + else: + + if root[1] is None: + return root[2] + elif root[2] is None: + return root[1] + else: + succ = bst_find_min(root[2]) + root[0] = succ[0] + root[2] = bst_delete(root[2], succ[0]["name"]) + return root + +def bst_list_all(root): + def inorder(node): + if node is None: + return [] + return inorder(node[1]) + [node[0]] + inorder(node[2]) + return inorder(root) +if __name__ == "__main__": + print('cba') \ No newline at end of file From af4d1c77c85bb77e048372419945e270bc0ec479 Mon Sep 17 00:00:00 2001 From: danch0us Date: Wed, 20 May 2026 20:13:04 +0300 Subject: [PATCH 2/3] =?UTF-8?q?1=20=D0=B7=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kornevma/426.md | 0 kornevma/docs/1/main.py | 349 +++++++++++++++++++++++++++++++++++++ kornevma/docs/kortinko.png | Bin 0 -> 29658 bytes kornevma/docs/results.csv | 91 ++++++++++ kornevma/main.py | 146 ---------------- 5 files changed, 440 insertions(+), 146 deletions(-) create mode 100644 kornevma/426.md create mode 100644 kornevma/docs/1/main.py create mode 100644 kornevma/docs/kortinko.png create mode 100644 kornevma/docs/results.csv delete mode 100644 kornevma/main.py diff --git a/kornevma/426.md b/kornevma/426.md new file mode 100644 index 0000000..e69de29 diff --git a/kornevma/docs/1/main.py b/kornevma/docs/1/main.py new file mode 100644 index 0000000..84fdee9 --- /dev/null +++ b/kornevma/docs/1/main.py @@ -0,0 +1,349 @@ +import random +import time +import csv +import sys + +sys.setrecursionlimit(10**8)#прикалюха 6 вылетаел + +def mk(name, phone): + return {"name": name, "phone": phone} + +def ll_create_node(record): + return [record, None] + +def ll_insert(ll_head, record): + new_node = ll_create_node(record) + new_node[1] = ll_head[0] + ll_head[0] = new_node + +def ll_find(ll_head, name): + cur = ll_head[0] + while cur: + if cur[0]["name"] == name: + return cur[0] + cur = cur[1] + return None + +def ll_delete(ll_head, name): + cur = ll_head[0] + prev = None + while cur: + if cur[0]["name"] == name: + if prev: + prev[1] = cur[1] + else: + ll_head[0] = cur[1] + return True + prev = cur + cur = cur[1] + return False + +def ll_list_all(ll_head): + res = [] + cur = ll_head[0] + while cur: + res.append(cur[0]) + cur = cur[1] + return res + +def ht_hash(name, size): + return hash(name) % size + +def ht_insert(table, record): + idx = ht_hash(record["name"], len(table)) + new_node = ll_create_node(record) + new_node[1] = table[idx] + table[idx] = new_node + +def ht_find(table, name): + idx = ht_hash(name, len(table)) + cur = table[idx] + while cur: + if cur[0]["name"] == name: + return cur[0] + cur = cur[1] + return None + +def ht_delete(table, name): + idx = ht_hash(name, len(table)) + cur = table[idx] + prev = None + while cur: + if cur[0]["name"] == name: + if prev: + prev[1] = cur[1] + else: + table[idx] = cur[1] + return True + prev = cur + cur = cur[1] + return False + +def ht_list_all(table): + res = [] + for head in table: + cur = head + while cur: + res.append(cur[0]) + cur = cur[1] + return res + +def bst_create_node(record): + return [record, None, None] + +def bst_insert(root, record): + if root is None: + return bst_create_node(record) + if record["name"] < root[0]["name"]: + root[1] = bst_insert(root[1], record) + elif record["name"] > root[0]["name"]: + root[2] = bst_insert(root[2], record) + else: + root[0] = record + return root + +def bst_find(root, name): + if root is None: + return None + if name == root[0]["name"]: + return root[0] + elif name < root[0]["name"]: + return bst_find(root[1], name) + else: + return bst_find(root[2], name) + +def bst_find_min(node): + while node[1] is not None: + node = node[1] + return node + +def bst_delete(root, name): + if root is None: + return None + if name < root[0]["name"]: + root[1] = bst_delete(root[1], name) + elif name > root[0]["name"]: + root[2] = bst_delete(root[2], name) + else: + if root[1] is None: + return root[2] + elif root[2] is None: + return root[1] + else: + succ = bst_find_min(root[2]) + root[0] = succ[0] + root[2] = bst_delete(root[2], succ[0]["name"]) + return root + +def bst_list_all(root): + def inorder(node): + if node is None: + return [] + return inorder(node[1]) + [node[0]] + inorder(node[2]) + return inorder(root) + +def tmr(func): + def wrapper(*args, **kwargs): + start = time.perf_counter() + result = func(*args, **kwargs) + elapsed = time.perf_counter() - start + return result, elapsed + return wrapper + +@tmr +def ins_ll(ll_head, records): + for rec in records: + ll_insert(ll_head, rec) + +@tmr +def fnd_ll(ll_head, names): + for name in names: + ll_find(ll_head, name) + +@tmr +def del_ll(ll_head, names): + for name in names: + ll_delete(ll_head, name) + +@tmr +def ins_ht(table, records): + for rec in records: + ht_insert(table, rec) + +@tmr +def fnd_ht(table, names): + for name in names: + ht_find(table, name) + +@tmr +def del_ht(table, names): + for name in names: + ht_delete(table, name) + +@tmr +def ins_bst(root, records): + for rec in records: + root = bst_insert(root, rec) + return root + +@tmr +def fnd_bst(root, names): + for name in names: + bst_find(root, name) + +@tmr +def del_bst(root, names): + for name in names: + root = bst_delete(root, name) + return root + +def gen(n, seed=42): + random.seed(seed) + recs = [] + for i in range(n): + name = f"user_{i:05d}" + phone = random.randint(1000000, 9999999) + recs.append(mk(name, phone)) + return recs + +def prep(recs, ecnt=100, mcnt=10): + alln = [r["name"] for r in recs] + ex = random.sample(alln, ecnt) + ms = [f"none_{i}" for i in range(mcnt)] + sn = ex + ms + dn = random.sample(alln, 50) + return sn, dn + +def prm(n): + if n < 2: return False + if n % 2 == 0: return n == 2 + d = 3 + while d * d <= n: + if n % d == 0: return False + d += 2 + return True + +def nxtprm(n): + while not prm(n): + n += 1 + return n + +def bench(n=10000, rpts=5): + recs = gen(n) + shuf = recs.copy() + random.shuffle(shuf) + srt = sorted(recs, key=lambda r: r["name"]) + + snms, dnms = prep(recs) + + htsz = nxtprm(2 * n) + + exps = [ + { + "name": "linkedlist", + "init_empty": lambda: [None], + "insert": ins_ll, + "find": fnd_ll, + "delete": del_ll, + }, + { + "name": "hashtable", + "init_empty": lambda: [None] * htsz, + "insert": ins_ht, + "find": fnd_ht, + "delete": del_ht, + }, + { + "name": "bst", + "init_empty": lambda: None, + "insert": ins_bst, + "find": fnd_bst, + "delete": del_bst, + }, + ] + + res = [] + + for e in exps: + sn = e["name"] + for mn, recs_set in [("shuffled", shuf), ("sorted", srt)]: + for rp in range(1, rpts + 1): + st = e["init_empty"]() + + if sn == "bst": + st, ti = e["insert"](st, recs_set) + else: + _, ti = e["insert"](st, recs_set) + + _, tf = e["find"](st, snms) + + if sn == "bst": + st, td = e["delete"](st, dnms) + else: + _, td = e["delete"](st, dnms) + + res.append([sn, mn, "insert", rp, ti]) + res.append([sn, mn, "find", rp, tf]) + res.append([sn, mn, "delete", rp, td]) + + with open("results.csv", "w", newline="", encoding="utf-8") as f: + w = csv.writer(f) + w.writerow(["тип", "режим", "операция", "повтор", "время"]) + w.writerows(res) + + from collections import defaultdict + agg = defaultdict(list) + for row in res: + k = (row[0], row[1], row[2]) + agg[k].append(row[4]) + print("\n5 повторов в ср:") + print(f"{'тип':<15} {'режим':<10} {'операция':<10} {'срдений':<10}") + for k, times in sorted(agg.items()): + avg = sum(times) / len(times) + print(f"{k[0]:<15} {k[1]:<10} {k[2]:<10} {avg:<10.6f}") + + return res, agg + +def plot(agg): + try: + import matplotlib.pyplot as plt + except ImportError: + print("матплотлтб скачать") + return + + sts = ["linkedlist", "hashtable", "bst"] + mds = ["shuffled", "sorted"] + ops = ["insert", "find", "delete"] + + fig, axes = plt.subplots(1, 3, figsize=(16, 5)) + fig.suptitle("сравенние", fontsize=14) + + for oi, op in enumerate(ops): + ax = axes[oi] + data = [] + for s in sts: + for m in mds: + k = (s, m, op) + avg = sum(agg[k]) / len(agg[k]) if k in agg else 0 + data.append(avg) + x = range(len(sts)) + width = 0.35 + svals = [data[i*2] for i in range(len(sts))] + svals2 = [data[i*2+1] for i in range(len(sts))] + + ax.bar([i - width/2 for i in x], svals, width, label='shuffled') + ax.bar([i + width/2 for i in x], svals2, width, label='sorted') + ax.set_xticks(x) + ax.set_xticklabels(sts) + ax.set_title(op) + ax.set_ylabel('время (sec)') + ax.legend() + + plt.tight_layout() + plt.savefig("kortinko.png") + plt.show() + print("zibka kortinko.png") + +if __name__ == "__main__": + res, agg = bench(n=10000, rpts=5) + plot(agg) \ No newline at end of file diff --git a/kornevma/docs/kortinko.png b/kornevma/docs/kortinko.png new file mode 100644 index 0000000000000000000000000000000000000000..8dc0aa1b0966ddd177a3c388da9bf20c7a78891b GIT binary patch literal 29658 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 literal 0 HcmV?d00001 diff --git a/kornevma/docs/results.csv b/kornevma/docs/results.csv new file mode 100644 index 0000000..3b254fd --- /dev/null +++ b/kornevma/docs/results.csv @@ -0,0 +1,91 @@ +тип,режим,операция,повтор,время +linkedlist,shuffled,insert,1,0.00348759995540604 +linkedlist,shuffled,find,1,0.10299369995482266 +linkedlist,shuffled,delete,1,0.06881969998357818 +linkedlist,shuffled,insert,2,0.002795599983073771 +linkedlist,shuffled,find,2,0.11420650000218302 +linkedlist,shuffled,delete,2,0.050943999958690256 +linkedlist,shuffled,insert,3,0.003374699968844652 +linkedlist,shuffled,find,3,0.09485340001992881 +linkedlist,shuffled,delete,3,0.04981170000974089 +linkedlist,shuffled,insert,4,0.002937599958386272 +linkedlist,shuffled,find,4,0.0926941999932751 +linkedlist,shuffled,delete,4,0.04564540000865236 +linkedlist,shuffled,insert,5,0.0032468000426888466 +linkedlist,shuffled,find,5,0.09445199999026954 +linkedlist,shuffled,delete,5,0.06562509998911992 +linkedlist,sorted,insert,1,0.004829699988476932 +linkedlist,sorted,find,1,0.05208860000129789 +linkedlist,sorted,delete,1,0.06792090000817552 +linkedlist,sorted,insert,2,0.0030329000437632203 +linkedlist,sorted,find,2,0.09589699999196455 +linkedlist,sorted,delete,2,0.024623799952678382 +linkedlist,sorted,insert,3,0.0023055000347085297 +linkedlist,sorted,find,3,0.05262780003249645 +linkedlist,sorted,delete,3,0.035465800028759986 +linkedlist,sorted,insert,4,0.003455400001257658 +linkedlist,sorted,find,4,0.06551479996414855 +linkedlist,sorted,delete,4,0.0368536000023596 +linkedlist,sorted,insert,5,0.0036825999850407243 +linkedlist,sorted,find,5,0.05081029998837039 +linkedlist,sorted,delete,5,0.02609110000776127 +hashtable,shuffled,insert,1,0.008456900017336011 +hashtable,shuffled,find,1,7.070001447573304e-05 +hashtable,shuffled,delete,1,3.9300008211284876e-05 +hashtable,shuffled,insert,2,0.0068731000064872205 +hashtable,shuffled,find,2,6.079999729990959e-05 +hashtable,shuffled,delete,2,3.4599972423166037e-05 +hashtable,shuffled,insert,3,0.008831500017549843 +hashtable,shuffled,find,3,6.859999848529696e-05 +hashtable,shuffled,delete,3,5.959998816251755e-05 +hashtable,shuffled,insert,4,0.009147099975962192 +hashtable,shuffled,find,4,5.989999044686556e-05 +hashtable,shuffled,delete,4,3.470003139227629e-05 +hashtable,shuffled,insert,5,0.006436199997551739 +hashtable,shuffled,find,5,4.67000063508749e-05 +hashtable,shuffled,delete,5,2.6500027161091566e-05 +hashtable,sorted,insert,1,0.0056028999970294535 +hashtable,sorted,find,1,7.159996312111616e-05 +hashtable,sorted,delete,1,3.060000017285347e-05 +hashtable,sorted,insert,2,0.006678299978375435 +hashtable,sorted,find,2,0.00012290000449866056 +hashtable,sorted,delete,2,3.4299970138818026e-05 +hashtable,sorted,insert,3,0.005322600016370416 +hashtable,sorted,find,3,4.8499961849302053e-05 +hashtable,sorted,delete,3,2.600002335384488e-05 +hashtable,sorted,insert,4,0.006450399989262223 +hashtable,sorted,find,4,7.379997987300158e-05 +hashtable,sorted,delete,4,4.780001472681761e-05 +hashtable,sorted,insert,5,0.0060063999844715 +hashtable,sorted,find,5,5.689996760338545e-05 +hashtable,sorted,delete,5,3.120000474154949e-05 +bst,shuffled,insert,1,0.05349189997650683 +bst,shuffled,find,1,0.0005713000427931547 +bst,shuffled,delete,1,0.0002283000503666699 +bst,shuffled,insert,2,0.05809580005006865 +bst,shuffled,find,2,0.00046800001291558146 +bst,shuffled,delete,2,0.00030310003785416484 +bst,shuffled,insert,3,0.05402979999780655 +bst,shuffled,find,3,0.0005937999812886119 +bst,shuffled,delete,3,0.0002316000172868371 +bst,shuffled,insert,4,0.0533465999760665 +bst,shuffled,find,4,0.000450699997600168 +bst,shuffled,delete,4,0.00031700002728030086 +bst,shuffled,insert,5,0.05407660000491887 +bst,shuffled,find,5,0.0004341000458225608 +bst,shuffled,delete,5,0.0002673999988473952 +bst,sorted,insert,1,24.01944399997592 +bst,sorted,find,1,0.2082300999900326 +bst,sorted,delete,1,0.11376300000119954 +bst,sorted,insert,2,24.020037700014655 +bst,sorted,find,2,0.21519700001226738 +bst,sorted,delete,2,0.1168955999892205 +bst,sorted,insert,3,23.98331290000351 +bst,sorted,find,3,0.20412800001213327 +bst,sorted,delete,3,0.16615210002055392 +bst,sorted,insert,4,24.231940899975598 +bst,sorted,find,4,0.2066795999999158 +bst,sorted,delete,4,0.13191749999532476 +bst,sorted,insert,5,23.72923769999761 +bst,sorted,find,5,0.22344960004556924 +bst,sorted,delete,5,0.11515349999535829 diff --git a/kornevma/main.py b/kornevma/main.py deleted file mode 100644 index f28ebc7..0000000 --- a/kornevma/main.py +++ /dev/null @@ -1,146 +0,0 @@ -import random -import time -import sys - -def mr(name, phone): - return {"name": name, "phone": phone} - -#llист -def ll_create_node(record): - return [record, None] - -def ll_insert(ll_head, record): - new_node = ll_create_node(record) - new_node[1] = ll_head[0] - ll_head[0] = new_node - -def ll_find(ll_head, name): - cur = ll_head[0] - while cur: - if cur[0]["name"] == name: - return cur[0] - cur = cur[1] - return None - -def ll_delete(ll_head, name): - cur = ll_head[0] - prev = None - while cur: - if cur[0]["name"] == name: - if prev: - prev[1] = cur[1] - else: - ll_head[0] = cur[1] - return True - prev = cur - cur = cur[1] - return False - -def ll_list_all(ll_head): - res = [] - cur = ll_head[0] - while cur: - res.append(cur[0]) - cur = cur[1] - return res - -#ht -def ht_hash(name, size): - return hash(name) % size -def ht_insert(table, record): - idx = ht_hash(record["name"], len(table)) - new_node = ll_create_node(record) - new_node[1] = table[idx] - table[idx] = new_node - -def ht_find(table, name): - idx = ht_hash(name, len(table)) - cur = table[idx] - while cur: - if cur[0]["name"] == name: - return cur[0] - cur = cur[1] - return None - -def ht_delete(table, name): - idx = ht_hash(name, len(table)) - cur = table[idx] - prev = None - while cur: - if cur[0]["name"] == name: - if prev: - prev[1] = cur[1] - else: - table[idx] = cur[1] - return True - prev = cur - cur = cur[1] - return False - -def ht_list_all(table): - res = [] - for head in table: - cur = head - while cur: - res.append(cur[0]) - cur = cur[1] - return res - - -# bst -def bst_create_node(record): - return [record, None, None] - -def bst_insert(root, record): - if root is None: - return bst_create_node(record) - if record["name"] < root[0]["name"]: - root[1] = bst_insert(root[1], record) - elif record["name"] > root[0]["name"]: - root[2] = bst_insert(root[2], record) - else: #мб - root[0] = record - return root - -def bst_find(root, name): - if root is None: - return None - if name == root[0]["name"]: - return root[0] - elif name < root[0]["name"]: - return bst_find(root[1], name) - else: - return bst_find(root[2], name) - -def bst_find_min(node): - while node[1] is not None: - node = node[1] - return node - -def bst_delete(root, name): - if root is None: - return None - if name < root[0]["name"]: - root[1] = bst_delete(root[1], name) - elif name > root[0]["name"]: - root[2] = bst_delete(root[2], name) - else: - - if root[1] is None: - return root[2] - elif root[2] is None: - return root[1] - else: - succ = bst_find_min(root[2]) - root[0] = succ[0] - root[2] = bst_delete(root[2], succ[0]["name"]) - return root - -def bst_list_all(root): - def inorder(node): - if node is None: - return [] - return inorder(node[1]) + [node[0]] + inorder(node[2]) - return inorder(root) -if __name__ == "__main__": - print('cba') \ No newline at end of file From 949252d2459db4f70a8645201e3425a66faff5f2 Mon Sep 17 00:00:00 2001 From: danch0us Date: Fri, 22 May 2026 17:19:25 +0300 Subject: [PATCH 3/3] report added --- kornevma/docs/report1.txt | 60 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 kornevma/docs/report1.txt diff --git a/kornevma/docs/report1.txt b/kornevma/docs/report1.txt new file mode 100644 index 0000000..89584f9 --- /dev/null +++ b/kornevma/docs/report1.txt @@ -0,0 +1,60 @@ +Отчёт по экспериментальному сравнению структур данных телефонного справочника +1. Условия эксперимента +Реализованы три структуры для хранения записей вида {name, phone}: + +односвязный список (вставка в голову); + +хеш-таблица с цепочками (размер таблицы ~ 2N, простое число, хеш-функция hash(name) % size); + +двоичное дерево поиска без балансировки. + +Измерялось время выполнения операций вставки N = 10 000 записей, поиска 110 имён (100 существующих + 10 несуществующих) и удаления 50 случайных записей. +Эксперименты проводились для двух вариантов порядка входных данных: + +shuffled – случайный порядок имён; + +sorted – имена, отсортированные по алфавиту. +Каждый замер повторялся 5 раз, результаты усреднены. + +Полученные средние значения (фрагмент): + +Структура Режим Вставка (сек) Поиск (сек) Удаление (сек) +LinkedList shuffled ~0.003 ~0.100 ~0.056 +LinkedList sorted ~0.003 ~0.063 ~0.038 +HashTable shuffled ~0.008 ~0.00006 ~0.00004 +HashTable sorted ~0.006 ~0.00008 ~0.00003 +BST shuffled ~0.055 ~0.0005 ~0.00027 +BST sorted ~23.997 ~0.212 ~0.129 +Точные числа см. в файле results.csv + +2. Сравнительный анализ +2.1. Влияние порядка данных на BST: деградация до O(n) +На shuffled-данных дерево строится относительно сбалансированным, и операции выполняются в среднем за O(log N). +На sorted-данных каждая следующая вставка попадает строго в правого потомка, и дерево вырождается в линейный список. Глубина рекурсии достигает N = 10 000, что приводит к: + +колоссальному росту времени вставки (с ~0.055 сек до ~24 сек); + +значительному замедлению поиска и удаления (с ~0.0005 сек до ~0.21 сек и с ~0.00027 сек до ~0.13 сек соответственно). +Причина — рекурсивные функции вынуждены обходить все N узлов, превращая логарифмическую сложность в линейную. Практический вывод: использовать несбалансированное BST можно только при гарантированно случайном порядке поступающих ключей, иначе необходимы самобалансирующиеся варианты (AVL, красно-чёрное дерево). + +2.2. Почему хеш-таблица почти не чувствительна к порядку +Хеш-функция равномерно распределяет ключи по корзинам независимо от порядка поступления. Операции вставки, поиска и удаления сводятся к вычислению хеша и проходу по очень короткой цепочке (в среднем O(1)). Время вставки в отсортированном наборе даже чуть меньше (0.006 сек против 0.008 сек), что объясняется меньшим количеством коллизий при последовательном поступлении близких имён (хотя разница несущественна). Поиск и удаление занимают доли миллисекунды и практически не зависят от размера набора в исследованном диапазоне. Хеш-таблица демонстрирует наилучшую устойчивость к любым шаблонам входных данных при условии хорошей хеш-функции и адекватного размера. + +2.3. Почему связный список всегда медленен при поиске +В односвязном списке единственный способ найти элемент — линейный проход от головы к хвосту. Среднее время поиска — O(N), то есть пропорционально количеству записей. В эксперименте при 10 000 записей и поиске 110 имён время составило около 0.1 сек, что на три порядка хуже, чем у хеш-таблицы, и на два порядка хуже, чем у сбалансированного BST. Режим sorted/shuffled влияет слабо (для списка порядок вставки не меняет структуру). Медленный поиск — фундаментальное ограничение связного списка. + +2.4. Удаление в каждой структуре +Связный список: удаление требует поиска элемента (O(N)) и изменения ссылки у предыдущего узла. Время удаления соизмеримо с поиском (0.038–0.056 сек). + +Хеш-таблица: удаление выполняется за O(1) в среднем — вычисление индекса, поиск в цепочке (очень короткой) и изменение ссылок. Время минимально (~0.00004 сек). + +BST: удаление узла с двумя потомками требует поиска минимального элемента в правом поддереве. На сбалансированных данных (shuffled) время ~0.00027 сек. На вырожденных (sorted) удаление замедляется до 0.13 сек из-за необходимости обходить длинные цепочки. + +3. Выводы и практические рекомендации +Если преобладают частые поиск и вставка, а порядок данных не важен – оптимальный выбор хеш-таблица. Она обеспечивает константное среднее время операций и нечувствительна к порядку поступления ключей. Идеально для телефонного справочника, кешей, словарей. + +Если требуется хранить данные в отсортированном виде и нужны операции типа «найти следующий/предыдущий» – подходит BST, но обязательно самобалансирующееся (например, АВЛ или красно-чёрное дерево). Несбалансированное BST применимо только при случайном порядке вставки; иначе деградация до O(n) делает его бесполезным. + +Связный список стоит использовать лишь в случаях, когда операции поиска редки, а основные действия — добавление/удаление в начале или в середине списка при известной позиции. Для телефонного справочника он практически непригоден из-за линейного времени поиска. + +Таким образом, для типового приложения с преимущественно операциями поиска и вставки (например, адресная книга в мобильном телефоне) лучшим решением является хеш-таблица. Если же функциональность требует получения отсортированного списка контактов или диапазонных запросов, разумно применять сбалансированное дерево поиска. \ No newline at end of file