**جستجوی درختی** از جمله پرکاربردترین استفاده از یک درخت است. درخت یک مدل مناسب برای نمایش بسیاری از مفاهیم، پدیده‌ها و رابطهٔ بین آن‌هاست. مثلاً درخت فامیلی، سلسله مراتب اداری، عبارات ریاضی و بسیاری از بازی‌ها با درخت مدل می‌شوند. در واقع درخت‌ها، گراف‌های خاصی هستند که در مورد ویژگی‌های آن‌ها نتایج نظری زیادی وجود دارد.

درخت‌ها بسته به نوع نیاز، تعداد برگ‌های مختلفی دارند. الگوریتم جستجو مختص به تعداد برگ‌های درخت می‌باشد که عبارتند از:درخت جستجوی سه‌تایی ،درخت جستجوی دودویی خود-متوازن ،درخت جستجوی دودویی متوازن وزن‌دار ،درخت جستجوی تعمیم‌یافته، درخت جستجو بندانگشتی و... در اینجا سه نوع جستجوی رایج را در درخت دودویی و درخت با تعداد برگ نامشخص و ترای (همان درخت پیشوندی است که برای جستجوی رشته‌ای مناسب است) بررسی می‌کنیم.

درخت دودویی جستجو
-----------------

*درخت دودویی* درخت مرتبی است که هر عنصر آن حداکثر دارای دو فرزند چپ و راست باشد. اگر یک گره فقط یک فرزند داشته باشد، باید مشخص شود که فرزند چپ است یا راست.
درخت عبارت و درخت تصمیم نمونه‌هایی از کاربردهای درخت دودویی هستند.

یک درخت دودویی ساده با ۹ گره و ارتفاع ۳، در این درخت گره شماره ۲ ریشه است. این درخت غیرمتوازن و نامرتب است.
معروف به د.د. ج می‌باشد.
معمولاً عملیات زیر بر روی یک درخت جستجوی دودویی تعریف می‌شود:

1. ایجاد یک درخت جستجوی خالی
2. آزمایش خالی بودن درخت
3. درج کردن یک کلید جدید در درخت، بدون برهم‌خوردن خاصیت درخت
4. جستجو کردن و یافتن یک کلید خاص در درخت
5. حذف کردن یک کلید از درخت، با حفظ خاصیت درخت
6. پیمایش درخت جستجوی دودویی، به طوری تمام گره‌ها دقیقاً یک بار مورد دسترسی قرارگیرند.

برای پیدا کردن گرهی با یک کلید خاص مانند key در درخت، ابتدا باید از ریشه درخت شروع کنیم. اگر ریشه تهی باشد، درخت فاقد هر عنصری بوده و جستجو ناموفق خواهد بود. در غیر اینصورت، key را با مقدار گره ریشه مقایسه می‌کنیم، اگر برابر بودند، جستجو موفق است و گره ریشه همان گره موردنظر است. در غیر این صورت، دو حالت پیش خواهدآمد:

1. key از گره ریشه کوچکتر است. در این‌حالت، هیچ عنصری در زیردرخت سمت راست وجود ندارد که مقدار کلید آن برابر با key باشد؛ بنابراین جستجو باید در زیردرخت سمت چپ ادامه‌یابد.
2. key بزرگتر از گره ریشه است. در این حالت، هیچ عنصری در زیردرخت سمت چپ وجود ندارد که مقدار کلید آن برابر با key باشد؛ بنابراین جستجو باید در زیردرخت سمت راست ادامه‌یابد.

سپس بسته به حالت یک و دو، زیردرخت سمت چپ یا زیردرخت سمت راست را به روش بازگشتی و با استفاده از الگوریتم بالا جستجو می‌کنیم. عمل جستجو در درخت جستجوی دودویی، از مرتبه {\displaystyle O(h)} است که در آن h ارتفاع درخت است. چرا که حداکثر باید به میزان عمق درخت، به طرف پایین حرکت کنیم.
در این الگوریتم، هر راس ۳ اشاره‌گر دارد:

1. اشاره‌گر به پدردرخت دودویی جستجو
2. اشاره‌گر به فرزند چپ
3. اشاره‌گر به فرزند راست

```
class TreeNode:
 def \_\_init\_\_(self, label):
 
 self.left\_child = None
 self.right\_child = None
 self.label = label
 self.parent = None

def search\_in\_BST(node, key):
 if node is None or node.label == key:
 return node
 if node.label < key:
 return search\_in\_BST(node.leftChild, key)
 else:
 return search\_in\_BST(node.rightChild, key)

```

جستجو در درخت با تعداد برگ نامشخص
---------------------------------

ممکن است الگوریتمی نیاز داشته‌باشیم که در آن تعداد فرزندان هر راس نامشخص باشد. در اینصورت به درختی نیاز داریم که در آن فرزندان هر راس رابه تعداد دلخواه بتوان‌افزود.
در این الگوریتم

، هر راس ۳ اشاره‌گر دارد:

1. اشاره‌گر به پدر
2. اشاره‌گر به فرزند چپ
3. اشاره‌گر به راس بعدی در سمت راست خود راس

و برای جستجو، ابتدا برچسب خود راس را چک می‌کنیم اگر برابر با key نبود، برچسب فرزند چپ آن را بررسی می‌کنیم و اگر برابر نبود به سراغ رئوس سمت راست فرزند چپ می‌رویم و تا جایی که به مقدار None نرسیدیم، کار را ادامه می‌دهیم و الگوریتم را به صورت بازگشتی ادامه می‌دهیم.
به دلیل اینکه حداکثر همه رئوس برای یکبار جستجو طی می‌شوند، الگوریتم از مرتبه {\displaystyle O(n)} می‌باشد.

```
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

```

جستجو در درخت پیشوندی
---------------------

یک ترای با کلیدهای in , i, ten, ted, tea, to, A و inn
در علم رایانه ترای یا درخت پیشوندی یک داده ساختار درختی است که برای آرایه‌های شرکت پذیری استفاده می‌شود که کلیدهای آن معمولاً رشته می‌باشند. برخلاف یک درخت دودویی جستجو در این درخت هیچ گرهی، کلیدی را که توسط آن گره مشخص می‌شود ذخیره نمی‌کند؛ در عوض، موقعیت آن در درخت نشان‌دهنده کلید مربوط به آن است. تمام فرزندان یک گره پیشوند مشترکی دارند که این پیشوند در گره مربوطه ذخیره می‌شود. گره ریشه نیز یک رشته خالی است. معمولاً همه گره‌ها مشخص‌کننده کلیدها نیستند. فقط برگ‌ها و بعضی از گره‌های داخلی با کلیدها مرتبط می‌شوند. گره‌های حاوی کلید به نحوی علامت‌گذاری می‌شوند تا تمام کلیدها مشخص شوند.
البته یک ترای الزاماً شامل رشته‌های کاراکتری نمی‌باشد، بلکه حتی برای جایگشت‌های عددی و مواردی از این قبیل هم می‌توان از ترای استفاده کرد و از آن در جستجوی لغت نامه‌ها نیز استفاده می‌شود.

برای جستجوی رشته یا عبارت در ترای، از ریشه درخت شروع به مقایسه می‌کنیم. در این الگوریتم، یال‌های درخت برچسب دارند و اگر یال ابتدایی برابر با مقدار ابتدای رشته باشد، سایر یال‌های متصل به یال اولیه را بررسی می‌کنیم و به صورت بازگشتی ادامه می‌دهیم تا به انتهای یک رشته که با 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 = label

class 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

```

منابع
-----

1. 1 2 3  قدسی، محمد (۱۳۸۸)، *داده ساختارها و مبانی الگوریتم‌ها*، تهران: انتشارات فاطمی
2. ↑  Times Roman and Mathtime Pro 2 by the authors (۲۰۰۹)، *Introduction to Algorythms*، Amherst: Massachusetts Institute of Technonology
3. ↑  "The Trie Data Structure".