یک مجموعه نقطه داریم در صفحه.
کوئری: تعداد نقاطی که در یک مستطیل می افتند. (اضلاع مستطیل با محورهای مختصات موازی اند.)
یک بعدی: نقاط را مرتب می کنیم و ... .
دو بعدی:
1- k-d tree
اسمش از k dimension tree آمده که به معنی درخت k بعدی است. در حالت دو بعدی، یک عمق در میان x و y است. در هر عمق میانه ی نقاط (از نظر x یا y) به دست می آید و بر اساس آن نقاط را دو دسته می کنیم که فرزندان آن گره می شوند.
زمان ساخت درخت O(nlogn) است و فضای O(n) می گیرد و زمان کوئری آن O(sqrt(n)+k) است.
محاسبه ی زمان کوئری:
T(n) = 2T(n/4)+1 = O(sqrt(n))
نحوه ی کوئری زدن: (پیدا کردن مستطیل با این درخت)
اگر گره ی فعلی برگ است و درون مستطیل می افتد آن را به عنوان جواب برگردان.
در غیر این صورت: اگر فرزند راست گره فعلی کاملا در مستطیل می افتد، کل زیر درخت آن را برگردان؛ در غیر این صورت همین تابع را روی آن انجام بده. اگر فرزند چپ گره فعلی کاملا در مستطیل می افتد، کل زیر درخت آن را برگردان؛ در غیر این صورت همین تابع را روی آن انجام بده.
دلیل زمان کوئری:
چون هر بار که تقسیم می کنیم ممکن است بخشی از مستطیل در یک طرف بیفتد بخش دیگر آن در طرف دیگر.
برای ابعاد دیگر:
یک بعدی: مثل درخت جستجوی دودویی معمولی است فقط دو بار کوئری می زنیم.
سه بعدی: به جای مستطیل مکعب داریم و زمان ساخت درخت nlogn و سایز حافظه n و زمان کوئری n^(1-1/d)+k است.
2- range tree
یک درخت جدا برای x می سازیم و برای نقاط آن زیر درخت (که x آنها در بازه ی مورد نظر است) یک درخت دیگر بر حسب y می سازیم.
تحلیل حافظه: چون هر نقطه در دو گره است (یکبار برای x یکبار برای y ) پس همان O(n) می شود.
تحلیل زمان:
T(n) = 2T(n/2)+O(nlogn)
T(n) = O(nlogn)
طبق حالت دوم قضیه اصلی
n^log(a,b) *logn^k = nlogn
k=1, log(a,b) = 1
اگر به جای اینکه هر بار sort کنیم، فقط یکبار اول این کار را انجام بدهیم (مرتب سازی بر حسب y) و لیست های مرتب شده را به گره های بعدی بدهیم، زمان ساخت درخت به مقدار زیر کاهش پیدا می کند:
T(n) = 2T(n/2)+O(n)
T(n) = O(nlogn)
طبق حالت دوم قضیه اصلی
n^log(a,b) = n
k = 0
کوئری زدن:
اگر گره ی فعلی جواب است آن را برگردان. (اگر برگ است اینجا چک کن)
در غیر این صورت در درخت y جستجو کن و جواب را برگردان.
(اگر غیر برگ است اینجا چک کن.)
زمان کوئری:
logn = زمان پیدا کردن x
logn+k = زمان پیدا کردن y
کلا: O(logn*logn)
ایده کلی و تفاوت با حالت 1 (k-d tree)
از تصویر کردن روی محورها استفاده کردیم. قبلی بیشتر برای پیدا کردن یک نقطه به درد می خورد.
تعمیم: برای d بعد
فضای لازم:
S(d,n) <= 2*S(d,n/2)+S(d-1,n)
S(n) = n*(logn)^(d-1)
برای حلش هم میشه استقرا زد. یعنی فرض کنیم S(d-1,n) = n*(logn)^(d-2) باشه، طبق حالت دوم قضیه اصلی n*(logn)^k که k=d-2 در نتیجه یک logn اضافه میشه و n*(logn)^(k-1) به دست میاد.
حافظه لازم:
T(d,n) <= 2logn*T(d-1,n)+O(logn)
که میشه O(logn^d)
2-2- fractional cascading
هدف: صرفه جویی در زمان باینری سرچ های متوالی روی یک مجموعه
در گره ی ریشه درخت را نگه می داریم، اما درختهای فرزندان را به لیست تبدیل می کنیم.
می توانیم logn ای را که صرف باینری سرچ می کنیم حذف کنیم، به این صورت که لیست هر عمق را به لیست عمق پایینتر با اشاره گر وصل کنیم. (عناصر متناظر را). پس به سادگی می توانیم با دنبال کردن این اشاره گرها به عنصر مورد نظر برسیم. (بدون اینکه نیاز باشد از روی درخت حرکت کنیم.)
این باعث می شود که زمان به O(logn^(d-1)) کاهش پیدا کند. (برای قسمت جستجو روی y و ابعاد بالاتر)
* اگر ساختار داینامیک باشد (حذف و درج داشته باشیم) باید درخت نگه داریم اما باز هم می توانیم همین ایده را پیاده کنیم.
# حالتهای خاص
وقتی که x چند نقطه با هم مساوی باشد (یا y) برای اینکه مشکلی پیش نیاید، در مقایسه ی نقاط مختصات را به ترتیب مقایسه می کنیم. (شبیه مقایسه ی pair در c++)