Нахождение соседних узлов в дереве

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

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

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}])

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

Вот как устроены массивы:

введите описание изображения здесь

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

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

enum orientation : signed int {LEFT = -1, CENTER = 0, RIGHT = 1};

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

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

node_orientation_table содержит ориентации в структуре. Таким образом, в 2d, 0 0 относится к верхнему левому квадрату (или левому левому квадрату).
[[ВЛЕВО, ВЛЕВО], [ВПРАВО, ВЛЕВО], [ВЛЕВО, ВПРАВО], [ВПРАВО, ВПРАВО]]

И функция getPositionFromOrientation примет LEFT, LEFT и вернет 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);

введите описание изображения здесь

Это должно захватить верхний правый квадрат в нижнем левом квадрате, например 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 });

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

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

3

Решение

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

 00  01
10  11

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

     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. Так что, если мы применяем тот же алгоритм, который мы используем в структуре, у нас все будет хорошо.

Нижняя линия:
действительный переход в структуре

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.

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

2

Другие решения

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

1

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

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

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

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

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

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

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

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

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

0
По вопросам рекламы [email protected]