درخت بازهها
در علوم کامپیوتر، از ساختمان دادهٔ درخت بازهها∗ برای ذخیرهکردن بازهها استفاده میشود. این ساختمانداده امکان یافتن بازهٔ مربوط به یک نقطهٔ داده شده را فراهم میکند. میتوان هر گره از درخت در با پیچیدگی زمانی (o (log n تغییر داد.
در مورد درختها
درختها قسمت بزرگی از دادهساختارهای علم کامپیوتر را پوشش میدهند. درخت در حالت کلی به گراف همبندی گفته میشود که دور ندارد.
درخت ریشهدار درختی است، که در آن بین رأسها رابطهٔ پدر و فرزندی وجود دارد (ساختاری شبیه شجرهنامه دارد).
ساختار درخت بازهها
درخت بازهها، درختی ریشهدار است که هر رأس غیر برگ∗ آن دقیقاً دو فرزند دارد. در این درخت هر رأس نشاندهندهٔ یک بازه است. برگها در این درخت نشاندهندهٔ بازههایی به طول یک هستند. رئوس غیر برگ همگی دو فرزند دارند؛ اگر بازهٔ نسبتدادهشده به فرزندان چپ و راست به ترتیب و باشد، بازهٔ این رأس است. طول بازهٔ نسبتدادهشده به هر رأس توانی از ۲ است (در صورتی که تعداد برگها توانی از ۲ باشد). در نتیجه اگر رأسی نشاندهندهٔ بازهٔ باشد؛ فرزند سمت چپ نشان دهندهٔ بازهٔ و فرزند راست نشاندهندهٔ بازهٔ است. این درخت اغلب در مورد سؤالهایی به کار میرود که عملیاتی روی بازهها انجام میشود یا در هر مرحله به ما دستوری میدهند و باید تصمیم درست را در هر مرحله بگیریم.
تحلیل زمانی
اردر همهٔ کارها در این دادهساختار ( است که طول بازهای است که ریشه نشان میدهد. چرا که اگر در حال جستجوی بازهٔ ، در رأس x باشیم (x نمایندهٔ بازهٔ است)؛
۱- اگر p برابر P و q برابر Q باشد بازه پیدا شده و عمل مورد نظر را انجام بده (پایان جستجو).
۲- اگر بازهٔ مورد نظر با هر دو فرزند تداخل داشت: به دنبال در و در بگرد.
۳- اگر بازهٔ مورد نظر فقط با یکی از فرزندان تداخل داشت: به دنبال در فرزند مذکور بگرد.
به نظر میرسد ممکن است حالت دوم تکرار شود و تمام رئوس را پیمایش کنیم، در صورتی که به سادگی میتوان دریافت اگر یک بار حالت دوم رخ دهد (هر دو فرزند را پیمایش کنیم) آنگاه در هیچ مرحلهٔ دیگری لازم نیست هر دو انتهای بازه را بررسی کنیم. چرا که از یک طرف بازهٔ ما به ابتدا یا انتها چسبیده و اگر دو قسمت را بررسی کنیم یکی از دو قسمت درجا به شرط پایان میرسد.
پیادهسازی با ++C
void find(int p,int q,node x,int P,int Q){
if(p==P && q==Q){
//found and do the query
return;
}
int mid=(P+Q)/2;
if(q<=mid){
find(p,q,x.left,P,mid);
}else if(p>=mid){
find(p,q,x.right,mid,Q);
}else{
find(p,mid,x.left,P,mid);
find(mid,q,x.right,mid,Q);
}
}
و این هم یک کد ساده که دو عمل تبدیل تمامی اعداد یک بازه به یک عدد و پیدا کردن بزرگترین عدد یک بازه را پشتیبانی میکند.
const int N = 1000 * 100 + 5;
class segment{
int v[4*N], P[4*N], Q[4*N], mrk[4*N];
public:
void build(int x,int p,int q){
P[x] = p, Q[x] = q;
if(p == q) return ;
int m = (p + q)>> 1;
build(x <<1,p,m);
build((x <<1) + 1,m + 1,q);
}
inline void mpas(int x){
if(mrk[x]){
mrk[x] = 0;
if(P[x] == Q[x]) return ;
mrk[x <<1] = mrk[(x <<1) + 1] = 1;
v[x <<1] = v[(x <<1) + 1] = v[x];
}
}
void change(int x,int p,int q,int val){
mpas(x);
if(p <= P[x] && Q[x] <= q){
mrk[x] = 1;
v[x] = val;
return ;
}
if(Q[x] <p || P[x]> q)
return ;
change(x <<1,p,q,val);
change((x <<1) + 1,p,q,val);
v[x] = max(v[x <<1],v[(x <<1) + 1]);
}
int find_max(int x,int p,int q){
mpas(x);
if(p <= P[x] && Q[x] <= q)
return v[x];
if(Q[x] <p || P[x]> q)
return -INF;
return max(find_max(x <<1,p,q), find_max((x <<1) + 1,p,q) );
}
};
جستارهای وابسته
- درختهای بازهای دوبعدی نیز وجود دارند. یعنی بازههای یک بعدی تبدیل به یک زیر مستطیل میشوند. در این حالت هر رأس نشاندهندهٔ یک مستطیل است که طول و عرض آن توانی از دو است. در حالت کلی میتوان گفت درخت بازهای دوبعدی یک درخت بازهای معمولی است که هر رأس آن ریشهٔ یک درخت بازهای است و هر راس دو پدر دارد.
- نوع دیگری درخت بازهای به نام درخت فنویک هم وجود دارد.∗
پیوند به بیرون
- مقالهای در مورد درخت بازهها در TopCoder (به زبان انگلیسی)
- درس درخت بازهها در خبرگاه المپیاد کامپیوتر (به زبان فارسی)
- http://www.algorithmist.com/index.php/Fenwick_tree