درخت جستجوی دودویی
در علوم رایانه، درخت جستجوی دودویی که گاهی اوقات درخت دودویی مرتب هم نامیده می‌شود، یک ساختار داده است و نوعی درخت دودویی است.
یک درخت باینری نوعی ساختارداده برای ذخیره کردن داده است مانند عددهاییی که مرتب شده هستند. درخت جست و جوی دودویی این امکان را فراهم میکند که جست و جوی یک عدد، اضافه کردن آن و حذف کردن آن با سرعت بیشتری انجام شود. همچنین این امکان را فراهم میکند که مجموعه های پویا و جداول جست و جو را پیاده سازی کنیم.
ترتیب گره ها در دخت جست و جوی دودویی به صورتی است که در هر مقایسه نیمی از درخت باقی مانده بررسی نمیشود. بنابراین زمان جست و جوی درخت متناسب با لگاریتم تعداد عددهای ذخیره شده در درخت است. این زمان بسیار کمتر از زمان جست و جوی خطی در یک آرایه مرتب نشده است اما کندتر از انجام عملیات درهم سازی است.
انواع مختلفی از درخت های جست و جوی دودویی مورد بررسی و مطالعه قرار گرفته اند.
درخت جستجوی دودویی، نوعی درخت دودویی است که ممکن است تهی باشد، اگر تهی نباشد، دارای خصوصیات زیر است:
این ویژگی تضمین می‌کند که پیمایش میان‌ترتیب یک درخت جستجوی دودویی، کلیدها را به ترتیب صعودی نمایش می‌دهد.
در ادامه برخی اصطلاح‌های مهم مربوط به درخت مورد اشاره قرار گرفته است:عملیات اصلی بر روی یک درخت جستجوی دودویی به زمانی متناسب با ارتفاع درخت احتیاج دارد. برای یک درخت دودویی کامل با n گره چنین عملیاتی در بدترین حالت در زمان (o(logn اجرا میشود. بنابراین اگر درخت یک زنجیر خطی با n گره باشد همین عملیات در زمان بدترین حالت (o(n اجرا میشود. امید ریاضی ارتفاع یک درخت جستجوی دودویی که به تصادف ساخته شده است برابر با (o(logn است.بنابراین عملیات اصلی مجموعه پویا بر روی چنین درختی در حالت میانگین به زمان (o(logn احتیاج دارد.
در عمل، همیشه نمی‌توانیم تضمین کنیم که درخت های جستجوی دودویی به تصادف ساخته میشوند. اما می‌توانیم انواع مختلفی از درخت های جستجوی دودویی را با کارایی بدترین حالتی که بر روی عملیات اصلی به خوبی تضمین شده باشد طراحی کرد.
بعد از ارائه ویژگی های اصلی درخت های جستجوی دودویی در بخش های بعد نشان میدهیم چگونه برای چاپ مقادیر یک درخت جستجوی دودویی به صورت مرتب، عملیات روی آن را انجام میدهیم.
معمولاً عملیات زیر بر روی یک درخت جستجوی دودویی تعریف می‌شود:
در این روش از پیمایش، زیردرخت چپ ابتدا بازدید می‌شود، سپس از گره ریشه بازدید می‌شود و در نهایت زیردرخت راست پیمایش می‌شود. همواره باید به خاطر داشته باشید که هر گره می‌تواند خود نماینده یک زیردرخت باشد.
اگر یک درخت باینری به صوت میان‌ترتیبی پیموده شود، مقادیر کلیدهای ذخیره شده در خروجی به ترتیب نزولی نمایش داده می‌شود
در این روش پیمایش، گره ریشه ابتدا مورد بازدید قرار می‌گیرد، سپس زیردرخت چپ و در نهایت زیردرخت راست پیموده می‌شوند.
در این روش پیمایش، جنان که از روی نام مشخص است گره ریشه در آخر بازدید می‌شود. بنابراین ابتدا زیردرخت سمت چپ و بعد از آن زیردرخت راست و در نهایت گره ریشه بازدید می‌شوند.برای پیدا کردن گرهی با یک کلید خاص مانند key در درخت، ابتدا باید از ریشه درخت شروع کنیم. اگر ریشه تهی باشد، درخت فاقد هر عنصری بوده و جستجو ناموفق خواهد بود. در غیر این صورت، key را با مقدار گره ریشه مقایسه می‌کنیم، اگر برابر بودند، جستجو موفق است و گره ریشه همان گره مورد نظر است. در غیر این صورت، دو حالت پیش خواهد آمد:
سپس بسته به حالت یک و دو، زیردرخت سمت چپ یا زیردرخت سمت راست را به روش بازگشتی و با استفاده از الگوریتم بالا جستجو می‌کنیم. عمل جستجو در درخت جستجوی دودویی، از مرتبه O(h) است که در آن h ارتفاع درخت است. چرا که حداکثر باید به میزان عمق درخت، به طرف پایین حرکت کنیم. node_t search(node_t *root, T value)
برای درج کردن یک گره جدید در درخت جستجوی دودویی، باید گره را طوری درج کرد که خاصیت درخت برهم نخورد و درخت همچنان یک درخت جستجوی دودویی باقی بماند. برای حفظ خاصیت اول (منحصربه‌فرد بودن کلیدها)، باید ابتدا درخت را جستجو کنیم تا مطمئن شویم که عنصر قبلاً در درخت درج نشده‌است. پس از اینکه مطمئن شدیم کلید مورد نظر از قبل در درخت وجود ندارد، می‌توانیم کلید را در درخت درج کنیم. اگر درخت تهی بود، کافیست گره جدید را به عنوان گره ریشه درج کنیم تا عمل درج خاتمه یابد. اگر درخت خالی نبود، کلید جدید را با ریشه مقایسه می‌کنیم، که دو حالت جستجو پیش می‌آید:
عمل درج در درخت جستجوی دودویی، از مرتبه O(h) است.
typedef int T
void insert(node_t **tree, T data)
فرض کنید می‌خواهیم گره i را از درخت جستجوی دودویی حذف کنیم. به‌طوری که P(i) نشان‌دهنده والد گره i و C(i) نشان‌دهنده فرزند گره i است (چپ یا راست). در مورد حذف یک گره از درخت دودویی، سه حالت مختلف پیش می‌آید:
در حالت اول، کافیست اشاره‌گر مناسبی (چپ یا راست) از P(i) را برابر تهی قرار دهیم، عمل حذف خاتمه می‌یابد. در حالت دوم که i یک فرزند دارد، ابتدا گره i را حذف می‌کنیم، سپس فرزند i یا همان C(i) را جانشین گره i می‌کنیم.
در حالتی که i دو فرزند دارد، باید بزرگترین عنصر در زیردرخت چپ یا کوچکترین عنصر در زیردرخت راست را پیدا کرده و سپس آن را با گره i جایگزین و تعویض می‌کنیم. سپس ما این فرایند را با حذف کردن این عنصر جایگزین از زیردرختی که از آن گرفته شده‌است، دنبال می‌کنیم. به عبارتی دیگر، اگر بخواهیم گره N را از درخت حذف کنیم (که این گره دو فرزند دارد)، به جای اینکه خود گره N را پاک کنیم، یا گره بعد از N در پیمایش میانوندی یا گره قبلی N در پیمایش میانوندی را انتخاب می‌کنیم و آن گره را R می‌نامیم. سپس مقادیر N و R را تعویض کرده و سپس R را از درخت حذف می‌کنیم.
این تابع در تابع حذف گره از درخت استفاده می شود ، این تابع دوگره u و v را به عنوان ورودی می گیرد و اگر u ریشه درخت باشد آنگاه v را ریشه درخت قرار می دهد ،اگر u ریشه دره نباشد پدر u را به v اشاره می دهد ،و اگر v نال نبود پدر v را پدر u قرار می دهد ، در واقع این تابع v را جای u قرار می دهد و از نظر رابطه با گره ی پدر کار ها را درست می کند ولی کاری به جابهجا کردن فرزندان u , v ندارد.
پیمایش درخت بدین معنی است که به همه عناصر به ترتیب خاصی دسترسی داشته باشیم، بدون اینکه گرهی جا بیفتد یا اینکه گرهی دو بار در دسترس قرار گیرد. درخت جستجوی دودویی یک درخت دودویی است که می‌توان آن را به روش‌های معمول پیمایش کرد. اما از آنجا که نحوه قرار گرفتن عناصر در یک درخت جستجوی دودویی، تضمین می‌کند در هنگام پیمایش درخت به صورت میانوندی، عناصر به ترتیب صعودی در دسترس قرار گیرند، ما این نوع پیمایش را در ادامه بررسی می‌کنیم. درخت جستجوی دودویی به روش پیش‌ترتیب و پس‌ترتیب هم می‌توان پیمایش کرد، اما این نوع پیمایش‌ها در یک درخت جستجوی دودویی بیهوده هستند. پیمایش میانوندی بدین شکل تعریف می‌شود:
الگوریتم بالا بازگشتی است. void inorder(node_t *root)
درخت جستجوی دودویی گونه‌های مختلفی دارد. درخت ای‌وی‌ال و درخت سرخ-سیاه از جمله درخت جستجوی دودویی خود-متوازن هستند. درخت اسپلی نوعی درخت دودویی است که عناصری که مکرراً مورد استفاده قرار می‌گیرند را به صورت خودکار به ریشه درخت نزدیک می‌کند تا دسترسی به آن‌ها راحت‌تر شود. در یک تریپ، (tree heap) هر گره یک اولویت دارد که این اولویت به شکل تصادفی به گره اختصاص می‌یابد و گره والد اولویت بیشتری نسبت به فرزندش دارد. نوعی درخت جستجوی دودویی هستند که برای جستجوهای بسیار سریع مورد استفاده قرار می‌گیرند.
درختان جست و جوی دودویی هستند که برای کاهش حافظه ی استفاده شده . به‌طور گسترده برای پایگاه داده های درون حافظه ای استفاده می شود.
درخت تباهیده درختی است که در آن هر والد فقط یک فرزند دارد . در واقع می توان گفت هر والد فقط یک گره دارد. ای درخت متوازن نیست و در بدترین حالت مانند یک لیست پیوندی عمل می کند. اگر با درج عنصر جدید در درخت تابع افزودن گره ، ارتفاع درخت را متوازن نکند، می توان به سادگی از بک درخت تباهیده کنک گرفت به‌طوری که آن را با داده های از قبل مرتب شده پر کرد. در واقع به این معنی که این درخت به صورت یک لیست پیوندی عمل خواهد
کرد.
D. A. Heger در سال ۲۰۰۴ روشی برای مقایسه عملکرد درخت های جست و جوی دودویی ارائه داد. در این مقایسه تریپ را به عنوان بهترین عملکرد در حالت میانگین معرفی کرد در حالی که در همین مقایسه درخت سرخ-سیاه کمترین اختلاف را بین بدترین حالت و بهترین حالتش دارد هرچند به‌طور میانگین تریپ بهتر ازآن عمل می کند.
برای این که bst هایی که داریم را به نحوی تغییر دهیم که الگوریتم جست وجوی بهینه ای داشته باشیم از روش زیر مب توان استفاده کرد:
گره هایی که بیشترین استفاده را دارند و دسترسی به آن ها مورد نیاز است را نزدیک ریشه قرار میدهیم . همچین گره هایی که کمترین به آن دسترسی داریم را نزدیک برگ ها قرار می دهیم . با این روش بهینه هزینه جیت و جوی کمتری خواهیم داشت.