Итеративная реализация дерева Kd ( C )

#c #algorithm #data-structures #kdtree

#c #алгоритм #структуры данных #kdtree

Вопрос:

Здравствуйте, у кого-нибудь есть итеративная реализация Kd-дерева на C . Я пытался, но это не удается, когда количество узлов нечетно. Вот мой код на данный момент. Я имею в виду http://ldots.org/kdtree/#buildingAkDTree подробности на сайте.

 #include <stdio.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
#include <iomanip>


struct Point {
    double pt[2];
    int id;
};

typedef std::vector<Point> TPointVector;

struct KdNode {
    double point[2];
    int id;
    double desc;
    bool leaf;

    KdNode *left;
    KdNode *right;
    KdNode *parent;
    KdNode(KdNode *parent_):parent(parent_),leaf(false){}
    KdNode(KdNode *parent_,TPointVector::iterator itr, int depth, TPointVector amp;pv);
    KdNode(KdNode *t, TPointVector amp;pv);

};

KdNode::KdNode(KdNode *parent_,TPointVector::iterator itr, int depth, TPointVector amp;pv) {
    parent = parent_ ;
    left   = 0;
    right  = 0;
    desc   = itr->pt[depth % 2 ];
    leaf   = false;
}

KdNode::KdNode(KdNode *t, TPointVector amp;pv) {
    id       = pv[0].id;
    point[0] = pv[0].pt[0];
    point[1] = pv[0].pt[1];
    left     = 0;
    right    = 0;
    parent   = t;
    leaf     = true;
}

KdNode *pRoot = 0;


struct ComparePoints {
    int cord;
    ComparePoints(int  cord_) : cord(cord_ % 2) { };
    bool operator()(const Pointamp; lhs, const Pointamp; rhs) const {
        return lhs.pt[cord] < rhs.pt[cord];
    }
};
void buildLeftTree(std::stack<TPointVector > amp;stackL) {
    KdNode *pCurrent = pRoot;
    KdNode **pNode  = amp;(pCurrent->left);
    int depth      = 0; 
    bool changeDirection = false;
    while (! stackL.empty()) {
        TPointVector pv = stackL.top(); 
        stackL.pop();
        if ( pv.size() != 1 ) { 
            std::sort(pv.begin(), pv.end(), ComparePoints(  depth));

            *pNode = new KdNode(pCurrent, pv.begin()   pv.size()/2, depth, pv);

            TPointVector lvp,rvp;
            std::size_t median = pv.size() / 2;
            std::copy(pv.begin(), pv.begin()   median, std::back_inserter(lvp));
            std::copy(pv.begin()   median, pv.end(), std::back_inserter(rvp));

            stackL.push(rvp); 
            stackL.push(lvp);

            if ( changeDirection ) {
                pCurrent = pCurrent->right;
                changeDirection = false;
            } else {
                pCurrent = pCurrent->left;
            }           
            pNode = amp;(pCurrent->left);

        } else {
            KdNode **pNodeLeft   = amp;(pCurrent->left);
            *pNodeLeft  = new KdNode(pCurrent, pv);
            pv = stackL.top();
            stackL.pop();

            KdNode **pNodeRight   = amp;(pCurrent->right);
            *pNodeRight  = new KdNode(pCurrent,pv);

            pCurrent = pCurrent->parent;
            pNode  = amp;(pCurrent->right);
            changeDirection = true;
            depth--;
        }           
    }
}

void buildRightTree(std::stack<TPointVector > amp;stackR) {
    KdNode *pCurrent = pRoot;
    KdNode **pNode  = amp;(pCurrent->right);
    int depth      = 0; 
    bool changeDirection = true;
    while (! stackR.empty()) {
        TPointVector pv = stackR.top(); 
        stackR.pop();

        if ( pv.size() != 1 ) { 
            std::sort(pv.begin(), pv.end(), ComparePoints(  depth));
            *pNode = new KdNode(pCurrent, pv.begin()   pv.size()/2, depth, pv);

            TPointVector lvp,rvp;
            std::size_t median = pv.size() / 2;
            std::copy(pv.begin(), pv.begin()   median, std::back_inserter(lvp));
            std::copy(pv.begin()   median, pv.end(), std::back_inserter(rvp));

            stackR.push(rvp); 
            stackR.push(lvp);       

            if ( changeDirection ) {
                pCurrent = pCurrent->right;
                changeDirection = false;
            } else {
                pCurrent = pCurrent->left;
            }       
            pNode = amp;(pCurrent->left);

        } else {
            KdNode **pNodeLeft   = amp;(pCurrent->left);
            *pNodeLeft  = new KdNode(pCurrent, pv);
            pv = stackR.top();
            stackR.pop();

            KdNode **pNodeRight   = amp;(pCurrent->right);
            *pNodeRight  = new KdNode(pCurrent,pv);

            pCurrent = pCurrent->parent;
            pNode  = amp;(pCurrent->right);
            depth--;
            changeDirection = true;
        }           
    }
}


void constructKD(TPointVector amp;pv) {
    int depth = 0;
    std::sort(pv.begin(), pv.end(), ComparePoints(depth));

    pRoot        = new KdNode(0);
    pRoot->desc  = ( pv.begin()   pv.size()/2)->pt[0];
    pRoot->left  = 0;
    pRoot->right = 0;

    TPointVector lvp, rvp;
    std::copy(pv.begin(), pv.begin()   pv.size()/2, std::back_inserter(lvp));
    std::copy(pv.begin()   pv.size()/2, pv.end(), std::back_inserter(rvp));

    std::stack<TPointVector > stackL, stackR;
    stackL.push(lvp);
    stackR.push(rvp);

    buildLeftTree(stackL);
    buildRightTree(stackR);

}
void readPoints(const char* fileName, TPointVectoramp; points) {
    std::ifstream input(fileName);

    if ( input.peek() != EOF ) {
        while(!input.eof()) {
            int id = 0;
            double x_cord, y_cord;
            input >> id >> x_cord >> y_cord;

            Point t ;
            t.pt[0] = x_cord;
            t.pt[1] = y_cord;
            t.id    = id;

            points.push_back(t);
        }
        input.close();
    }   
}
void _printLevelWise(KdNode *node, std::queue<KdNode *> Q) {
    int depth = 0;
    while ( ! Q.empty()) {
        KdNode *qNode = Q.front();Q.pop();
        if ( qNode->leaf ) {
            std::cout << "[" << qNode->id << "]" << std::setprecision (25) << "(" << qNode->point[0] << "," << qNode->point[1] << ")" << std::endl;
        } else {
            std::cout << std::setprecision (25) << qNode->desc << std::endl;
        }       
        if (qNode->left != 0)
            Q.push(qNode->left);
        if (qNode->right != 0)
            Q.push(qNode->right);
    }
}
void PrintLevelWise(KdNode *node) {
    std::queue<KdNode *> Q;
    Q.push(node);
    _printLevelWise(node, Q);
}
int main ( int argc, char **argv ) {
    if ( argc <= 1 ) {
        return 0;
    }
    TPointVector points;
    readPoints(argv[1], points);
    for ( TPointVector::iterator itr = points.begin(); itr != points.end();   itr) {
        std::cout << "(" << itr->pt[0] << "," << itr->pt[1] << ")" << std::endl;
    }
    if ( points.size() == 0 )
        return 0;
    constructKD(points);
    PrintLevelWise(pRoot);
    std::cout << "Construction of KD Tree Done " << std::endl;
}
  

Пример ввода, для которого это не удается :

 1 6 1 
2 5 5 
3 9 6 
4 3 6 
5 4 9
  

Пример ввода, для которого это работает :

 1 6 1 
2 5 5 
3 9 6 
4 3 6 
5 4 9 
6 4 0 
7 7 9 
8 2 9
  

Комментарии:

1. Как это «сбой», когда оно нечетное? Ошибка Seg?

2. Обновите вопрос, извините, что не указал деталей.

3. Для заинтересованных сторон «сбой» — это пропуск одного из прочитанных узлов, т. Е. в дереве оказывается только N-1 узел для N записей (когда N нечетно).

Ответ №1:

Ваш else in buildLeftTree и buildRightTree не обрабатывает случай, когда в правом поддереве нечетное число узлов. В вашем примере из 5 пунктов else случай в buildRightTree заканчивается тремя точками на stackR , первая используется для left узла, две вторые он молча присваивает right узлу, как если бы это был единственный узел.

Это связано с вашим выбором медианы, который использует критерии, отличные от тех, которые указаны на сайте, на который вы ссылаетесь.

 std::size_t median = pv.size() / 2; // degenerates in cases where size() is odd
  

Предполагается, что ваши критерии выбора основаны на медианном значении x или y и используют подсписки на основе этих критериев (которые не предполагают какого-либо заданного размера).

Комментарии:

1. Я внесу это изменение и проведу повторное тестирование, но у меня есть другой вопрос, почему не доступна итеративная реализация KD-дерева. Доступна ли простая версия реализации KD-дерева (рекурсивная может подойти)

Ответ №2:

В else из buildLeftTree и buildRightTree .

Добавить

 if (pv.size() > 1)
{
    pNode = amp;(pCurrent->right);
    continue;
}
  

между top и pop функцией стека. Все будет в порядке.

У меня есть еще один вопрос, когда количество точек равно миллиону, построение дерева будет очень медленным, как улучшить производительность?