چاردرخت

انواع

چاردرخت‌ها را می‌توان بر اساس نوع داده‌ای که نمایش می‌دهند (مثل سطح، نقطه، خط یا منحنی) تقسیم‌بندی کرد. یک نحوۀ دیگر تقسیم‌بندی بر این اساس است که آیا شکل درخت ایجادشده مستقل از ترتیب پردازش داده‌ها است یا خیر. تعدادی از چاردرخت‌های متداول عبارتند از:

چاردرخت ناحیه‌ای

این نوع چاردرخت، نمایشگر افرازی از یک صفحۀ دوبعدی است که در آن افراز، صفحۀ دوبعدی به چهار ناحیۀ برابر افراز شده و برخی ناحیه‌ها خود به چهار زیرناحیۀ برابر افراز شده‌اند و به همین ترتیب فرایند افراز می‌تواند ادامه پیدا کند. در چنین افرازی، هر گره، یا چهار فرزند دارد یا فرزندی ندارد (برگ است). هر برگ از درخت مربوط به یکی از زیر‌ناحیه‌‌های افراز است و اطلاعات آن زیرناحیه در آن برگ ذخیره می‌شود. چاردرخت ناحیه‌ای یک نوع ترای است.
می‌توان از یک چاردرخت ناحیه‌ای با ارتفاع n برای نمایش یک تصویر با ابعاد 2ⁿ × 2ⁿ پیکسل (که مقدار هر پیکسل ۰ یا ۱ است) استفاده کرد. ریشۀ درخت، نمایشگر کل تصویر است. هر ناحیه‌ای که مقدار همۀ پیکسل‌هایش با هم برابر نیست، به ۴ زیرناحیه تقسیم می‌شود. در نهایت هر برگ از درخت، مجموعه‌ای مربع‌شکل از تعدادی پیکسل را نشان می‌دهد که همگی ۰ یا همگی ۱ هستند.
اگر چاردرخت ناحیه‌ای برای نمایش مجموعه‌ای از نقاط (مثلاً تعدادی شهر که با طول و عرض جغرافیایی مشخص شده‌اند) استفاده شود، ناحیه‌ها تا جایی تقسیم می‌شوند که هر زیرناحیه حداکثر یک نقطه را شامل شود.

چاردرخت نقطه‌ای

چاردرخت نقطه‌ای تعمیمی از درخت دودویی است. از یک درخت دودویی می‌توان برای ذخیره تعدادی عدد (از فضای یک‌بعدی) استفاده کرد. ولی از چاردرخت نقطه‌ای می‌توان برای ذخیره تعدادی نقطه (دوتایی مرتب) از فضای دوبعدی استفاده کرد. این درخت، همۀ ویژگی‌های چاردرخت را دارد و یک درخت واقعی است. یعنی گره‌های درخت، خود نقاطی هستند که می‌خواهیم نمایش دهیم (برخلاف چاردرخت ناحیه‌ای، وقتی که برای نمایش تعدادی نقطه مورد استفاده قرار بگیرد). شکل ناحیه‌های ایجاد شده به ترتیب اضافه کردن نقاط بستگی دارد (درست مثل درخت دودویی جستجو). چاردرخت نقطه‌ای، معمولاً برای مقایسه بین مجموعه‌ای از نقاط در صفحۀ دوبعدی بسیار بهینه (معمولاً از O(log n)) عمل می‌کند.

چاردرخت یالی

این چاردرخت برای ذخیرۀ خط و منحنی (به جای نقطه) استفاده می‌شود. یک منحنی با تقسیمات زیاد ناحیه‌ها تقریب زده می‌شود. این کار ممکن است باعث شود که یک درخت بسیار نامتوازن ایجاد شود که با هدف کاراتر شدن دسترسی‌ها تناقض دارد.

چاردرخت نقشۀ چندضلعی

این چاردرخت (PMQuadtree) نوعی چاردرخت است که برای ذخیرۀ مجموعه‌ای از چندضلعی‌ها استفاده می‌شود که ممکن است تبه‌گون باشند (یعنی یک ضلع مجزا یا یک رأس مجزا در میانشان باشد). سه ردۀ اصلی از PMQuadtree ها وجود دارد که به خاطر نوع داده‌هایی که در گره‌های سیاه ذخیره می‌شود تفاوت دارند. چاردرخت PM3 می‌تواند تعدادی ضلع نامتقاطع و حداکثر یک نقطه را ذخیره کند. چاردرخت PM2 همانند PM3 است با این تفاوت که همه ضلع‌ها باید نقطۀ پایانی یکسان داشته باشند. چاردرخت PM1 نیز شبیه PM2 است. ولی گره‌های سیاه می‌توانند یک نقطه و ضلع‌های متصل به آن یا فقط تعدادی ضلع که در یک نقطه مشترک هستند را در خود ذخیره کنند، ولی نمی‌توان یک نقطه و تعدادی ضلع که در آن نقطه مشترک نیستند را در گره ذخیره کرد.

شبه کد

این شبه کد، روشی برای پیاده‌سازی یک چاردرخت است که فقط نقاط را ذخیره می‌کند. پیاده‌سازی‌های دیگری نیز برای این کار وجود دارد.

مقدمات

در این پیاده‌سازی، از این ساختارها استفاده شده است:
// Simple coordinate object to represent points and vectorsstruct XY{
  float x;
  float y;

  function __construct(float _x, float _y) {...}}// Axis-aligned bounding box with half dimension and centerstruct AABB{
  XY center;
  XY halfDimension;

  function __construct(XY center, XY halfDimension) {...}
  function containsPoint(XY p) {...}
  function intersectsAABB(AABB other) {...}}

کلاس چاردرخت

این کلاس، ریشۀ یک چاردرخت را (که کل چاردرخت را مشخص می‌کند) ذخیره می‌کند.
class QuadTree{
  // Arbitrary constant to indicate how many elements can be stored in this quad tree node
  constant int QT_NODE_CAPACITY = 4;

  // Axis-aligned bounding box stored as a center with half-dimensions
  // to represent the boundaries of this quad tree
  AABB boundary;

  // Points in this quad tree node
  Array of XY [size = QT_NODE_CAPACITY] points;

  // Children
  QuadTree* northWest;
  QuadTree* northEast;
  QuadTree* southWest;
  QuadTree* southEast;

  // Methods
  function __construct(AABB _boundary) {...}
  function insert(XY p) {...}
  function subdivide() {...} // create four children which fully divide this quad into four quads of equal area
  function queryRange(AABB range) {...}}

درج

این تابع یک نقطه را در چارک (زیرناحیه) مناسب از چاردرخت ذخیره می‌کند و در صورت نیاز ناحیه را تکه می‌کند.
class QuadTree{
  ...

  // Insert a point into the QuadTree
  function insert(XY p)
  {
    // Ignore objects which do not belong in this quad tree
    if (!boundary.containsPoint(p))
      return false; // object cannot be added

    // If there is space in this quad tree, add the object here
    if (points.size < QT_NODE_CAPACITY)
    {
      points.append(p);
      return true;
    }

    // Otherwise, we need to subdivide then add the point to whichever node will accept it
    if (northWest == null)
      subdivide();

    if (northWest->insert(p)) return true;
    if (northEast->insert(p)) return true;
    if (southWest->insert(p)) return true;
    if (southEast->insert(p)) return true;

    // Otherwise, the point cannot be inserted for some unknown reason (which should never happen)
    return false;
  }}

یافتن نقاط در یک بازه

این تابع، همۀ نقاطی که در یک بازه قرار دارند را پیدا می‌کند.
class QuadTree{
  ...

  // Find all points which appear within a range
  function queryRange(AABB range)
  {
    // Prepare an array of results
    Array of XY pointsInRange;

    // Automatically abort if the range does not collide with this quad
    if (!boundary.intersectsAABB(range))
      return pointsInRange; // empty list

    // Check objects at this quad level
    for (int p := 0; p < points.size; p++)
    {
      if (range.containsPoint(points[p]))
        pointsInRange.append(points[p]);
    }

    // Terminate here, if there are no children
    if (northWest == null)
      return pointsInRange;

    // Otherwise, add the points from the children
    pointsInRange.appendArray(northWest->queryRange(range));
    pointsInRange.appendArray(northEast->queryRange(range));
    pointsInRange.appendArray(southWest->queryRange(range));
    pointsInRange.appendArray(southEast->queryRange(range));

    return pointsInRange;
  }}