2014-01-27 5 views
3

Я разрабатываю структуру, которая похожа на двоичное дерево, но обобщена по размерам, поэтому вы можете установить, является ли это двоичным деревом, quadtree, octree и т. Д., Установив параметр измерения во время инициализации.Поиск соседних узлов в дереве

Вот определение этого:

template <uint Dimension, typename StateType> 
class NDTree { 
public: 
    std::array<NDTree*, cexp::pow(2, Dimension)> * nodes; 
    NDTree * parent; 
    StateType state; 
    char position; //position in parents node list 
    bool leaf; 

    NDTree const &operator[](const int i) const 
    { 
     return (*(*nodes)[i]); 
    } 

    NDTree &operator[](const int i) 
    { 
     return (*(*nodes)[i]); 
    } 
} 

Таким образом, для инициализации это- я установить размер, а затем разделить. Я иду на квадрадерево глубины 2 для иллюстрации здесь:

const uint Dimension = 2; 
NDTree<Dimension, char> tree; 
tree.subdivide(); 

for(int i=0; i<tree.size(); i++) 
    tree[i].subdivide(); 

for(int y=0; y<cexp::pow(2, Dimension); y++) { 
    for(int x=0; x<cexp::pow(2, Dimension); x++) { 
     tree[y][x].state = ((y)*10)+(x); 
    } 
} 
std::cout << tree << std::endl; 

Это приведет к квадрадерево, состояние каждого из значений инициализируются [0-4] [0-4].

([{0}{1}{2}{3}][{10}{11}{12}{13}][{20}{21}{22}{23}][{30}{31}{32}{33}]) 

У меня возникли проблемы с поиском соседних узлов из любой части. То, что ему нужно сделать, это принять направление, а затем (при необходимости) пересечь дерево, если направление уходит с края родительского узла (например, если бы мы были в правом нижнем углу квадрата квадрата, и нам нужно было получить часть справа от него). Мой алгоритм возвращает фиктивные значения.

Вот как массивы раскладывают:

enter image description here

А вот структуры необходимо знать для этого:

Это просто держит направление для элементов.

Это держит направление и должно ли оно идти глубже.

template <uint Dimension> 
struct TraversalHelper { 
    std::array<orientation, Dimension> way; 
    bool deeper; 
}; 

node_orientation_table содержит ориентации в структуре. Таким образом, в 2d, 0 0 относится к верхнему левому квадрату (или к левому квадрату). [[LEFT, LEFT], [RIGHT, LEFT], [влево, вправо], [RIGHT, RIGHT]]

И функция getPositionFromOrientation бы влево, влево и вернуть 0. Это только в основном противоположна node_orientation_table выше.

TraversalHelper<Dimension> traverse(const std::array<orientation, Dimension> dir, const std::array<orientation, Dimension> cmp) const 
{ 
    TraversalHelper<Dimension> depth; 

    for(uint d=0; d < Dimension; ++d) { 
     switch(dir[d]) { 
      case CENTER: 
       depth.way[d] = CENTER; 
       goto cont; 

      case LEFT: 
       if(cmp[d] == RIGHT) { 
        depth.way[d] = LEFT; 
       } else { 
        depth.way[d] = RIGHT; 
        depth.deeper = true; 
       } 
       break; 

      case RIGHT: 
       if(cmp[d] == LEFT) { 
        depth.way[d] = RIGHT; 
       } else { 
        depth.way[d] = LEFT; 
        depth.deeper = true; 
       } 
       break; 
     } 

     cont: 
      continue; 
    } 

    return depth; 
} 

std::array<orientation, Dimension> uncenter(const std::array<orientation, Dimension> dir, const std::array<orientation, Dimension> cmp) const 
{ 
    std::array<orientation, Dimension> way; 

    for(uint d=0; d < Dimension; ++d) 
     way[d] = (dir[d] == CENTER) ? cmp[d] : dir[d]; 

    return way; 
} 

NDTree * getAdjacentNode(const std::array<orientation, Dimension> direction) const 
{ 
    //our first traversal pass 
    TraversalHelper<Dimension> pass = traverse(direction, node_orientation_table[position]); 

    //if we are lucky the direction results in one of our siblings 
    if(!pass.deeper) 
     return (*(*parent).nodes)[getPositionFromOrientation<Dimension>(pass.way)]; 


    std::vector<std::array<orientation, Dimension>> up; //holds our directions for going up the tree 
    std::vector<std::array<orientation, Dimension>> down; //holds our directions for going down 
    NDTree<Dimension, StateType> * tp = parent;   //tp is our tree pointer 
    up.push_back(pass.way); //initialize with our first pass we did above 

    while(true) { 
     //continue going up as long as it takes, baby 
     pass = traverse(up.back(), node_orientation_table[tp->position]); 
     std::cout << pass.way << " :: " << uncenter(pass.way, node_orientation_table[tp->position]) << std::endl; 

     if(!pass.deeper) //we've reached necessary top 
      break; 
     up.push_back(pass.way); 

     //if we don't have any parent we must explode upwards 
     if(tp->parent == nullptr) 
      tp->reverseBirth(tp->position); 

     tp = tp->parent; 
    } 

    //line break ups and downs 
    std::cout << std::endl; 

    //traverse upwards combining the matrices to get our actual position in cube 
    tp = const_cast<NDTree *>(this); 
    for(int i=1; i<up.size(); i++) { 
     std::cout << up[i] << " :: " << uncenter(up[i], node_orientation_table[tp->position]) << std::endl; 
     down.push_back(uncenter(up[i], node_orientation_table[tp->parent->position])); 
     tp = tp->parent; 
    } 

    //make our way back down (tp is still set to upmost parent from above) 
    for(const auto & i : down) { 
     int pos = 0; //we need to get the position from an orientation list 

     for(int d=0; d<i.size(); d++) 
      if(i[d] == RIGHT) 
       pos += cexp::pow(2, d); //consider left as 0 and right as 1 << dimension 

     //grab the child of treepointer via position we just calculated 
     tp = (*(*tp).nodes)[pos]; 
    } 

    return tp; 
} 

В качестве примера этого:

std::array<orientation, Dimension> direction; 
direction[0] = LEFT; //x 
direction[1] = CENTER; //y 

NDTree<Dimension> * result = tree[3][0]->getAdjacentNode(direction); 

enter image description here

Это должно захватить верхний правый квадрат внутри нижнего левого квадрата, например, tree[2][1], который будет иметь значение 21, если мы прочитаем его состояние. Что работает с моего последнего редактирования (алгоритм изменен). Тем не менее, однако, многие запросы не возвращают правильные результаты.

//Should return tree[3][1], instead it gives back tree[2][3] 
NDTree<Dimension, char> * result = tree[1][2].getAdjacentNode({ RIGHT, RIGHT }); 

//Should return tree[1][3], instead it gives back tree[0][3] 
NDTree<Dimension, char> * result = tree[3][0].getAdjacentNode({ RIGHT, LEFT }); 

Есть несколько примеров неправильного поведения, таких как дерево [0] [0] (влево, влево), но и многие другие правильно работать.

Here - это папка git repo, с которой я работаю. Просто запустите g++ -std=c++11 main.cpp из этого каталога, если это необходимо.

+0

Я не могу скомпилировать. Он ищет 'gmp.h'. Можете ли вы сказать все библиотеки, которые используете? – Gene

+0

Зайдите в папку test/treet и скомпилируйте ее оттуда. Корневая папка использует другой код вне вопроса. – jett

+0

Спасибо. Он компилируется здесь просто отлично. Это довольно барочная реализация. Много избыточной информации. Вы уверены, что не хотите быть более консервативным? Пространство обычно является проблемой в реалистичных приложениях 2^n-дерева. – Gene

ответ

2

здесь одно свойство, вы можете попробовать использовать: рассматривать только 4 узла:

00 01 
10 11 

Любой узел может иметь до 4-х соседних узлов; два будут существовать в одной структуре (большой квадрат), и вам придется искать два других в соседних структурах. Давайте сосредоточимся на идентификации соседей, которые находятся в одной структуре: соседи для 00 - 01 и 10; соседи для 11 - 01 и 10. Обратите внимание, что только один бит отличается между соседними узлами и что соседи могут быть классифицированы по горизонтали и по вертикали. SO

 00 - 01 00 - 01 //horizontal neighbors 
    |    | 
    10    11 //vertical neighbors 

Уведомление о том, как перевернутый MSB получает вертикальный сосед и переворачивает LSB, получает горизонтальный узел? Давайте внимательно посмотреть:

MSB: 0 -> 1 gets the node directly below 
     1 -> 0 sets the node directly above 

    LSB: 0 -> 1 gets the node to the right 
     1 -> 0 gets the node to the left 

Итак, теперь мы можем определить узла в каждом направлении при условии, что они существуют в том же подструктуры. Что относительно узла слева от 00 или выше 10 ?? Согласно логике до сих пор, если вы хотите горизонтального соседа, вы должны перевернуть LSB; но щелчок будет извлекать 10 (узел справа). Итак, давайте добавим новое правило для невозможных операций:

you can't go left for x0 , 
    you can't go right for x1, 
    you can't go up for 0x, 
    you can't go down for 1x 

* невозможные операции относятся к операциям в рамках одной и той же структуры. Давайте посмотрим на большую картинку, которая находится вверху и слева от соседей за 00? если мы идем влево на 00 strucutre 0 (S0), мы должны закончиться с 01 of (S1), и если мы пойдем вверх, мы получим узел 10 из S (2). Обратите внимание, что они представляют собой, по сути, одни и те же горизонтальные/верительные значения соседних значений S (0) только в разных структурах. Итак, в принципе, если мы выясним, как перейти от одной структуры к другой, у нас есть алгоритм. Вернемся к нашему примеру: переходим от узла 00 (S0). Мы должны оказаться в S2; поэтому снова 00-> 10 переворачивает MSB. Поэтому, если мы применяем тот же алгоритм, который мы используем в структуре, мы должны быть в порядке.

Итог: действительный переход через strucutres

MSB 0, go down 
    1, go up 
LSB 0, go right 
    1, go left 

for invalid transitions (like MSB 0, go up) 
determine the neighbor structure by flipping the MSB for vertical and LSB for vertical 
and get the neighbor you are looking for by transforming a illegal move in structure A 
into a legal one in strucutre B-> S0: MSB 0 up, becomes S2:MSB 0 down. 

Я надеюсь, что эта идея достаточно четко

+0

Да, это то, что я пытаюсь сделать внутри прогулки. Это помогает увидеть это, написанное кем-то другим. – jett

+0

, поэтому я немного переписал свой алгоритм, и я продвигаюсь дальше, он решил некоторые проблемы моего старого (но не все). – jett

+0

ОК, с какими проблемами вы сталкиваетесь сейчас? – Pandrei

1

Отметьте этот ответ для поиска по соседству в окс.: https://stackoverflow.com/a/21513573/3146587. В принципе, вам нужно записать в узлах обход от корня к узлу и манипулировать этой информацией, чтобы генерировать требуемый обход для достижения соседних узлов.

+0

Благодарим вас за ссылку; с этой структурой нет статического корня (и обновление всех узлов для нового корня является чрезмерно дорогостоящим). Я должен сделать локальный подход, идущий вверх от текущего узла, а не сверху вниз. – jett

+0

Затем вы можете создать так называемый «индекс nD» в ответе, на который я ссылался (запись обхода от корня к узлу), идя вверх, пока не будет найден корень. И все еще выполняется вычисление «nD-индекса» желаемого соседа, а также соответствующий обход для поиска этого соседа. – user3146587

+0

Мне не нравятся худшие последствия перехода к root каждый раз; Я знаю, что возможно, вам не придется идти полным путем, если у вас есть позиция в списке узлов вашего узла, доступных в списке, например. родитель, положение и дети. Существует около 1/2^n вероятность того, что на самом деле нужно повышать каждый раз, поэтому по умолчанию полный маршрут - это много дополнительной работы. – jett

0

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

Каждой ячейке может быть присвоено сопоставление координат самым глубоким узлам вашего дерева. В вашем примере координаты (x, y) будут находиться в диапазоне от 0 до 2 Измерение -1 т.е. от 0 до 3.

Сначала вычислите координату соседа любым любым алгоритмом (например, правый переход от края должен быть перенесен в первую ячейку того же ряда, перейти к следующей строке или остаться на месте).

Затем отправьте новые координаты в обычную функцию поиска. Он вернет соседнюю ячейку в dimension шагах.

Вы можете оптимизировать это, посмотрев на двоичное значение координат. В основном, ранг наиболее значимой бит разницы говорит вам, сколько уровней вы должны идти.

Например, давайте квадрадерево глубинного 4. Координаты диапазоне от 0 до 15.

Предположим, мы идем налево из ячейки № 5 (0101b). Новая координата - 4 (0100b). Самый старший бит изменен - ​​бит 0, что означает, что вы можете найти соседа в текущем блоке.

Теперь, если вы идете вправо, новая координата равна 6 (0110b), поэтому изменение влияет на бит 1, а это значит, что вам нужно подняться на один уровень, чтобы получить доступ к вашей ячейке.

Все это говорит о том, что время вычисления и объем кода, необходимый для использования таких трюков, вряд ли стоит усилий для меня.

Смежные вопросы