جستجوی درختی کیست؟ توضیح بده.
جستجوی درختی از جمله پرکاربردترین استفاده از یک درخت است. درخت یک مدل مناسب برای نمایش بسیاری از مفاهیم، پدیده‌ها و رابطهٔ بین آن‌هاست. مثلاً درخت فامیلی، سلسله مراتب اداری، عبارات ریاضی و بسیاری از بازی‌ها با درخت مدل می‌شوند. در واقع درخت‌ها، گراف‌های خاصی هستند که در مورد ویژگی‌های آن‌ها نتایج نظری زیادی وجود دارد.درخت‌ها بسته به نوع نیاز، تعداد برگ‌های مختلفی دارند. الگوریتم جستجو مختص به تعداد برگ‌های درخت می‌باشد که عبارتند از:درخت جستجوی سه‌تایی ،درخت جستجوی دودویی خود-متوازن ،درخت جستجوی دودویی متوازن وزن‌دار ،درخت جستجوی تعمیم‌یافته، درخت جستجو بندانگشتی و... در اینجا سه نوع جستجوی رایج را در درخت دودویی و درخت با تعداد برگ نامشخص و ترای (همان درخت پیشوندی است که برای جستجوی رشته‌ای مناسب است) بررسی می‌کنیم.درخت دودویی جستجو."درخت دودویی" درخت مرتبی است که هر عنصر آن حداکثر دارای دو فرزند چپ و راست باشد. اگر یک گره فقط یک فرزند داشته باشد، باید مشخص شود که فرزند چپ است یا راست.درخت عبارت و درخت تصمیم نمونه‌هایی از کاربردهای درخت دودویی هستند.معروف به د.د. ج می‌باشد.معمولاً عملیات زیر بر روی یک درخت جستجوی دودویی تعریف می‌شود:برای پیدا کردن گرهی با یک کلید خاص مانند key در درخت، ابتدا باید از ریشه درخت شروع کنیم. اگر ریشه تهی باشد، درخت فاقد هر عنصری بوده و جستجو ناموفق خواهد بود. در غیر اینصورت، key را با مقدار گره ریشه مقایسه می‌کنیم، اگر برابر بودند، جستجو موفق است و گره ریشه همان گره موردنظر است. در غیر این صورت، دو حالت پیش خواهدآمد:سپس بسته به حالت یک و دو، زیردرخت سمت چپ یا زیردرخت سمت راست را به روش بازگشتی و با استفاده از الگوریتم بالا جستجو می‌کنیم. عمل جستجو در درخت جستجوی دودویی، از مرتبه formula_1 است که در آن h ارتفاع درخت است. چرا که حداکثر باید به میزان عمق درخت، به طرف پایین حرکت کنیم.در این الگوریتم، هر راس ۳ اشاره‌گر دارد:class TreeNode: def __init__(self, label): self.left_child = None self.right_child = None self.label = label self.parent = Nonedef search_in_BST(node, key): if node is None or node.label == key: return node if node.label &lt; key: return search_in_BST(node.leftChild, key) else: return search_in_BST(node.rightChild, key)جستجو در درخت با تعداد برگ نامشخص.ممکن است الگوریتمی نیاز داشته‌باشیم که در آن تعداد فرزندان هر راس نامشخص باشد. در اینصورت به درختی نیاز داریم که در آن فرزندان هر راس رابه تعداد دلخواه بتوان‌افزود.در این الگوریتم، هر راس ۳ اشاره‌گر دارد:و برای جستجو، ابتدا برچسب خود راس را چک می‌کنیم اگر برابر با key نبود، برچسب فرزند چپ آن را بررسی می‌کنیم و اگر برابر نبود به سراغ رئوس سمت راست فرزند چپ می‌رویم و تا جایی که به مقدار None نرسیدیم، کار را ادامه می‌دهیم و الگوریتم را به صورت بازگشتی ادامه می‌دهیم.به دلیل اینکه حداکثر همه رئوس برای یکبار جستجو طی می‌شوند، الگوریتم از مرتبه formula_2 می‌باشد.class TreeNode: def __init__(self, label): self.label = label self.parent = None self.left_child = None self.right_sibling = None def find_in_subtree(tree, key, node): if node.label == key: return node child = node.left_child while child is not None: result = tree.find_in_subtree(key, child) if result is not None: return result child = child.right_sibling return Noneجستجو در درخت پیشوندی.در علم رایانه ترای یا درخت پیشوندی یک داده ساختار درختی است که برای آرایه‌های شرکت پذیری استفاده می‌شود که کلیدهای آن معمولاً رشته می‌باشند. برخلاف یک درخت دودویی جستجو در این درخت هیچ گرهی، کلیدی را که توسط آن گره مشخص می‌شود ذخیره نمی‌کند؛ در عوض، موقعیت آن در درخت نشان‌دهنده کلید مربوط به آن است. تمام فرزندان یک گره پیشوند مشترکی دارند که این پیشوند در گره مربوطه ذخیره می‌شود. گره ریشه نیز یک رشته خالی است. معمولاً همه گره‌ها مشخص‌کننده کلیدها نیستند. فقط برگ‌ها و بعضی از گره‌های داخلی با کلیدها مرتبط می‌شوند. گره‌های حاوی کلید به نحوی علامت‌گذاری می‌شوند تا تمام کلیدها مشخص شوند.البته یک ترای الزاماً شامل رشته‌های کاراکتری نمی‌باشد، بلکه حتی برای جایگشت‌های عددی و مواردی از این قبیل هم می‌توان از ترای استفاده کرد و از آن در جستجوی لغت نامه‌ها نیز استفاده می‌شود.برای جستجوی رشته یا عبارت در ترای، از ریشه درخت شروع به مقایسه می‌کنیم. در این الگوریتم، یال‌های درخت برچسب دارند و اگر یال ابتدایی برابر با مقدار ابتدای رشته باشد، سایر یال‌های متصل به یال اولیه را بررسی می‌کنیم و به صورت بازگشتی ادامه می‌دهیم تا به انتهای یک رشته که با mark مشخص شده، برسیم.class Node: def __init__(self): self.mark = False self.edges = []class Edge: def __init__(self, start, end, label): self.start = start self.end = end self.label = labelclass Trie: def __init__(self): self.root = Node() def find(self, string): return trie_find(self.root, string, 0)اگر تنها جستجوی کل عبارت مدنظر باشد، داریم:def trie_find(node, string, idx = 0): if idx == len(string): return node.mark found = False for edge in node.edges: if edge.label == string[idx]: found = True return trie_find(edge.end, string, idx + 1) if not found: return Falseاگر پیشوندهای عبارت، مدنظر باشد، داریم:def trie_insert_mark_all(node, string, idx): node.mark = True if idx == len(string): return found = False for edge in node.edges: if edge.label == string[idx]: found = True trie_insert_mark_all(edge.end, string, idx + 1) # increasing idx to proceed to the next character if not found: new_node = Node() node.edges.append(Edge(node, new_node, string[idx])) trie_insert_mark_all(new_node, string, idx + 1) # increasing idx to proceed to the next character