Tree query algorithm fails test cases - c++

A tree is a simple graph in which every two vertices are connected by exactly one path. You are given a rooted tree with vertices and a lamp is placed on each vertex of the tree.
You are given queries of the following two types:
1 v: You switch the lamp placed on the vertex v, that is, either from On to Off or Off to On.
2 v: Determine the number of vertices connected to the subtree of v if you only consider the lamps that are in On state. In other words, determine the number of vertices in the subtree of v , such as u, that can reach from v by using only the vertices that have lamps in the On state.
Note: Initially, all the lamps are turned On and the tree is rooted from vertex number .
Input format
First line: Two integers and denoting the number of vertices and queries
Next lines: Two number and denoting an edge between the vertices and
Next lines: Two integers and , where is the type of the query
Note: It is guaranteed that these edges form a tree rooted from vertex .
Output format
For every query of type 2, print the answer in a single line.
Constraints
SAMPLE INPUT
5 4
1 2
2 3
1 4
4 5
1 3
2 2
1 3
2 2
SAMPLE OUTPUT
1
2
Explanation:
This is the tree in the first place:
The tree after turning off lamp in node
Obviously the answer now for vertex is
Code:
void updateSubSwitches(vector<vector<int>> &tree,vector<int> &parent, vector<int> &switchesSubTree, int vertex, bool flag) {
if(flag) {
switchesSubTree[vertex]=1;
for(int i=0;i<tree[vertex].size();i++) {
switchesSubTree[vertex] += switchesSubTree[tree[vertex][i]];
}
int parentVertex=parent[vertex];
while(switchesSubTree[parentVertex]!=0 && parentVertex!=0) {
switchesSubTree[parentVertex]=switchesSubTree[parentVertex]+switchesSubTree[vertex];
parentVertex=parent[parentVertex];
}
if(parentVertex == 0) {
switchesSubTree[0]=switchesSubTree[0]+switchesSubTree[vertex];
}
} else {
int parentVertex=parent[vertex];
while(switchesSubTree[parentVertex]!=0 && parentVertex!=0) {
switchesSubTree[parentVertex]=switchesSubTree[parentVertex]-switchesSubTree[vertex];
parentVertex=parent[parentVertex];
}
if(parentVertex == 0) {
switchesSubTree[0]=switchesSubTree[0]-switchesSubTree[vertex];
}
switchesSubTree[vertex]=0;
}
}
int initialSubSwitches(vector<vector<int>> &tree, vector<int> &switchesSubTree,int vertex) {
if(switchesSubTree[vertex] == -1) {
if(tree[vertex].size() > 0) {
switchesSubTree[vertex] = 1;
//cout<<vertex<<endl;
for(int i=0;i<tree[vertex].size();i++){
switchesSubTree[vertex] += initialSubSwitches(tree,switchesSubTree,tree[vertex][i]);
}
return switchesSubTree[vertex];
} else {
//cout<<vertex<<endl;
switchesSubTree[vertex] = 1;
return 1;
}
}
}
int main() {
int n,q;
cin>>n>>q;
vector<vector<int>> tree(n);
vector<bool> arr(n,true);
vector<int> switchesSubTree(n,-1);
vector<int> parent(n);
for(int i=1;i<=n-1;i++) {
int a,b;
cin>>a>>b;
tree[a-1].push_back(b-1);
parent[b-1] = a-1;
}
initialSubSwitches(tree,switchesSubTree,0);
for(int i=1;i<=q;i++) {
int b,v;
cin>>b>>v;
if(b==1) {
arr[v-1]=!arr[v-1];
updateSubSwitches(tree,parent,switchesSubTree,v-1,arr[v-1]);
} else {
cout<<switchesSubTree[v-1]<<endl;
}
}
}
Algorithm:
Initialization
tree stores the graph
arr stores if lamp at vertex v is ON or OFF.
switchesSubTree stores the number of lamps swtiched ON for that subtree.
Steps:
1. initialize the switchesSubTree array if initially all lamps are ON.
2. For each query,
2.1 if lamp is turned OFF:
decrease its ancestors by switchesSubTree[vertex] until we get a node whose lamp is OFF or we reach root vertex
make switchesSubTree[vertex] = 0
2.2 if lamp is turned ON:
calculate no. of lamps swithced ON for this vertex which is
switchesSubTree[vertex] = 1 + sum(all lamps of children)
increase the lamps for ancestors until we get a node whose lamp is OFF or we reach root vertex.
But solution gives TIME LIMIT EXCEEDED for some test cases and also wrong for some test cases.
Where am I going wrong and what can be an efficient and better solution ?

Related

How to do a DFS on a tree? (not necessarily binary)

I have a tree of n nodes (labeled 0 to n). I used two vectors to hold the edge information.
typedef std::vector<std::vector<int>> graph;
The input is n-1 edges in the form:
0 1
1 2
2 3
and so on
I'm told node 0 is always the root node.
I scan the edges using the following:
for (int i = 0; i < n-1; i++) {
scanf("%d %d", &a, &b);
g[a].push_back(b);
g[b].push_back(a); // the second approach doesn't use this line
}
This is my simple dfs:
void dfs(graph &g, int v) {
std::vector<int> visited; // I don't use a visited array for the second approach
for (int i = 0; i < g.size(); i++) {
visited.push_back(0);
}
std::stack<int> s;
std::set<int> t;
s.push(v);
while (!s.empty()) {
int i = s.top(); s.pop();
// do stuff
for (int i = 0; i < g[v].size(); i++) {
if (!visited[i]) {
visited[i] = true;
s.push(g[v][i]);
}
}
}
}
For example say we have 4 nodes and the following edges:
0 1
0 2
3 2
2 4
Say that I'm interested in the sub tree starting at 2. The above approach won't work because I'm inserting undirected edges 0 2 and 2 0. So when I start my dfs at 2 I add node 0 to my stack which is wrong.
I tried another approach of only inserting the edges given only but that also didn't work because in the example I would've inserted 3 2 which is an edge from 3 to node 2 and so when I start my dfs at node 2 I won't be able to reach node 3.
I feel like the problem is simple and I'm missing some big idea!
Since your graph is a rooted tree, you can do the following preprocessing.
Start a DFS from root (vertex #0);
For each vertex u, store its parent v. It means that if you travel alongside shortest path from root to u, the second-to-last vertex on this path will be v. Notice that in any tree there is exactly one shortest path from one vertex to another. Let's say that you have an array parent such that parent[u] = v according to above definition, and parent[0] = -1.
You can compute parent array by noticing, that if you do s.push(g[v][i]), then v is the parent of i (otherwise you would have visited i first);
Since parent[v] is the previous vertex on shortest path from global root (vertex 0), it is also the previous vertex on shortest path from any vertex x, which contains v in its subtree.
Now when you want to DFS over subtree of vertex u, you do DFS as you do it now, but do not visit the parent of any vertex. Say, if you want to do s.push(v), while parent[u] = v, do not do it. This way you will never leave the subtree of u.
Actually, knowing parent, you can get rid of your visited array. When you "do stuff" with vertex v, the only neighbour of v that is already visited is parent[v]. This property does not depend on the initial vertex, whose subtree you want to traverse. The DFS code would look like this (assuming you've done preprocessing to obtain parent:
void dfs(graph &g, vector<int> &parent, int v) {
std::stack<int> s;
s.push(v);
while (!s.empty()) {
int v = s.top(); s.pop(); // By the way, you have mistake here: int i = s.top().
// do stuff
for (int i = 0; i < g[v].size(); i++) {
if (parent[v] != g[v][i]) {
s.push(g[v][i]);
}
}
}
}
PS This approach is somehow similar to your second approach: it only treats edges that go from root to subtree. But it does not have the flaw, such as in your example with "3 2" being the wrong direction, because you infer direction algorithmically by doing DFS from root.

Logic error: Incorrect computation of mean centroid ,Infinite Execution of , 'entries ' function - K means clustering for a set of points, in C++

I am writing a program for K-means clustering, to find the clusters that each point should belong to. There are 8 points and 3 clusters for this code. Somehow in my code the 'entries' function is executing infinitely. I couldn't find where I have gone wrong. This is the logic that I'm following:
Hard coded input of the 8 points
Randomly generate 3 cluster centers
Calculate distance of each point from the 3 cluster centers and use arr1[][] to store the distances.
In cent_tally[][], store the number of the cluster that each point should belong to. eg. 0 for cluster 1, 1 for cluster 2 and 2 for cluster 3. (Also storing the same values in the 4th column of the 2-D array, 'arr1').
Calculate the mean centroids (cluster centers) by using the clusters nos. for each point.
Again call the 'entries' function to calculate the distances and the cluster no. to which each point should belong, but this time using the 2nd set of centroids.i.e. the mean centroids.
If the second set of cluster nos. for each point, (stored in the 2nd column of cent_tally[][]), tallies with the cluster nos. for each point using the randomly generated centroids(first column of cent_tally[][]), then print cent_tally[][], print arr1[][] and stop.
Here is the code:
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<math.h>
using namespace std;
class points
{
float x;
float y;
static int point_cnt;
static int flag;
int cent_tally[8][4];
int count2;
struct centroids
{
float cx;
float cy;
}c[3];
public:
points()
{
count2=0;
for(int i=0;i<3;i++)
{
c[i].cx=0;
c[i].cy=0;
}
for(int i=0;i<8;i++)
{
for(int j=0;j<4;j++)
{
cent_tally[i][j]=0;
}
}
}
void means(points * );
float dist(float a,float b,float c,float d);
int entries(float c11x,float c11y,float c22x,float c22y,float c33x,float c33y,float arr[8][4],points *p);
};
int points::point_cnt=8;
int points::flag=0;
int points::entries(float c11x,float c11y,float c22x,float c22y,float c33x,float c33y,float arr[8][4],points *p)
{
float sum1x,sum1y,sum2x,sum2y,sum3x,sum3y; //to calC mean centroids
sum1x=0;
sum1y=0;
sum2x=0;
sum2y=0;
sum3x=0;
sum3y=0;
int cnt1,cnt2,cnt3;
cnt1=0;
cnt2=0;
cnt3=0; //to calC mean centroids
//count2=0;
//in the first iteration of entries, count2=0
cout<<"count 2 value:"<<count2<<endl;
for(int k=0;k<8;k++) //0 to 7 for 8 points
{
arr[k][0]=dist(p[k].x,p[k].y,c11x,c11y);
arr[k][1]=dist(p[k].x,p[k].y,c22x,c22y);
arr[k][2]=dist(p[k].x,p[k].y,c33x,c33y);
float temp,min;
temp = (arr[k][0] < arr[k][1]) ? arr[k][0] : arr[k][1];
min = (arr[k][2] < temp) ? arr[k][2] : temp;
//cout<<"mins:"<<min<<endl;
for(int l=0;l<3;l++)
{
if(arr[k][l]==min)
{
arr[k][3]=l; //0 for c1, 1 for c2, 2 for c3 in 4th column of table
cent_tally[k][count2]=l;
if(l==0)
{
sum1x+=p[k].x;
sum1y+=p[k].y;
cnt1++;
}
else if (l==1)
{
sum2x+=p[k].x;
sum2y+=p[k].y;
cnt2++;
}
else if (l==2)
{ sum3x+=p[k].x;
sum3y+=p[k].y;
cnt3++;
}
else
{
cout<<"";
}
}
}
}
count2++;//for index into cent_tally
//finding mean centroid ...
//re entering values of mean centroid into the same structure created for 3 centroid coordinates ...
c[0].cx=sum1x/cnt1;
c[0].cy=sum1y/cnt1;
c[1].cx=sum2x/cnt2;
c[1].cy=sum2y/cnt2;
c[2].cx=sum3x/cnt3;
c[2].cy=sum3y/cnt3;
//now the struct contains mean centroids
for(int i=0;i<8;i++)
{ int temp=0;
temp=count2-1;
if(cent_tally[i][temp]==cent_tally[i][count2])
{
flag++;
}
else
{
break;
}
}
if(flag==8)
{
cout<<"centroids found: "<<endl;
for(int i=0;i<8;i++)
{
for(int j=0;j<4;j++)
{
cout<<" "<<cent_tally[i][j];
}
cout<<endl;
}
return 0;
}
else
{
return flag;
}
//while(flag!=8) //WHILE ALL 8 entries of latest 2 columns of cent_tally are not matching
//{
//entries(c[0].cx,c[0].cy,c[1].cx,c[1].cy,c[2].cx,c[2].cy,arr,&p[0]);
//}
}
float points::dist(float a,float b,float c,float d)
{
return (abs(a-c)+abs(b-d));
}
void points:: means(points * p)
{
float arr1[8][4]; //array to store dist b/w each point and cluster center and cluster values for each point after distance calculation
float arr2[8][4];
//let c1 c2 and c3 be initial cluster centers
//float c1x,c2x,c1y,c2y,c3x,c3y;
//Can take input from a file also...
p[0].x=2;
p[0].y=2;
p[1].x=1;
p[1].y=14;
p[2].x=10;
p[2].y=7;
p[3].x=1;
p[3].y=11;
p[4].x=3;
p[4].y=4;
p[5].x=11;
p[5].y=8;
p[6].x=4;
p[6].y=3;
p[7].x=12;
p[7].y=2;
srand ( time(NULL) );
for(int i=0;i<3;i++) //for 3 cluster centers, we need 3 centroids
{
int randIndex=1+rand()%(point_cnt-i-1);//where 8 is the no. of points
c[i].cx=p[randIndex].x;
c[i].cy=p[randIndex].y;
}
int val;
val=entries(c[0].cx,c[0].cy,c[1].cx,c[1].cy,c[2].cx,c[2].cy,arr1,&p[0]);
while(val!=8)
{
val=entries(c[0].cx,c[0].cy,c[1].cx,c[1].cy,c[2].cx,c[2].cy,arr1,&p[0]);
}
for(int i=0;i<8;i++)
{
for(int j=0;j<4;j++)
{
cout<<arr1[i][j]<<" ";
}
cout<<endl;
}
//displaying 1st table
//2.1 calculate mean centroid
//2.2 re enter new values in same table
//2.3 first 2 columns of cent_tally
//2.4 if not same repeat step 2.1
}
int main()
{
int c=8;
points p[8];
points obj;
obj.means(&p[0]);
return 0;
}
Another mistake I made, was not initialising flag=0 at the beginning of the 'entries' function!
Now my entries function is not running infinitely, but I have the following problems now:
Mean centroid(Second set of centroids onwards) is computed wrong after the first set of centroids are used
I'm trying to copy the fourth column of arr[][]into the first column and next columns eventually, of cent_tally[][] by using count2 as index , but the first column of cent-tally does not match the 4th column of arr[][]
I'm unable to figure where I have gone wrong.
Due to this logic in entries function
if(flag==8)
{
cout<<"centroids found: "<<endl;
for(int i=0;i<8;i++)
{
for(int j=0;j<4;j++)
{
cout<<" "<<cent_tally[i][j];
}
cout<<endl;
}
return 0;
}
else
{
return flag;
}
8 will never be returned from entries function.
On the other hand, this logic in means function
while(val!=8)
{
val=entries(c[0].cx,c[0].cy,c[1].cx,c[1].cy,c[2].cx,c[2].cy,arr1,&p[0]);
}
loops until 8 is returned from the entries function.
This seems to be the cause of the infinite loop. Consider adjusting the behavior of one of these two point.
Reasons for incorrect mean centroid computation:
Most Important: Inside the for loop where l runs from 0 to 2, if two values of distances are the same, the counts get incremented for two values of l, hence a flag can be used to ensure that only one minimum distance is taken into consideration, for deciding the centroid to which the point belongs to.
Abs takes integer values and hear we are dealing with float, so we need to define a function which handles float values .
Flag should be initialised to 0 in the beginning of 'entries' function.
If two randomly generated centroids are the same, you may not get the right answer.

Depth First Search: Formatting output?

If I have the following graph:
Marisa Mariah
\ /
Mary---Maria---Marian---Maryanne
|
Marley--Marla
How should be Depth First Search function be implemented such that I get the output if "Mary" is my start point ?
Mary
Maria
Marisa
Mariah
Marian
Maryanne
Marla
Merley
I do realize that the number of spaces equal to depth of the vertex( name ) but I don't how to code that. Following is my function:
void DFS(Graph g, Vertex origin)
{
stack<Vertex> vertexStack;
vertexStack.push(origin);
Vertex currentVertex;
int currentDepth = 0;
while( ! vertexStack.empty() )
{
currentVertex = vertexStack.top();
vertexStack.pop();
if(currentVertex.visited == false)
{
cout << currentVertex.name << endl;
currentVertex.visited = true;
for(int i = 0; i < currentVertex.adjacencyList.size(); i++)
vertexStack.push(currentVertex.adjacencyList[i]);
}
}
}
Thanks for any help !
Just store the node and its depth your stack:
std::stack<std::pair<Vertex, int>> vertexStack;
vertexStack.push(std::make_pair(origin, 0));
// ...
std::pair<Vertex, int> current = vertexStack.top();
Vertex currentVertex = current.first;
int depth = current.second;
If you want to get fancy, you can extra the two values using std::tie():
Vertex currentVertex;
int depth;
std::tie(currentVertex, depth) = vertexStack.top();
With knowing the depth you'd just indent the output appropriately.
The current size of your stack is, BTW, unnecessarily deep! I think for a complete graph it may contain O(N * N) elements (more precisely, (N-1) * (N-2)). The problem is that you push many nodes which may get visited.
Assuming using an implicit stack (i.e., recursion) is out of question (it won't work for large graphs as you may get a stack overflow), the proper way to implement a depth first search would be:
push the current node and edge on the stack
mark the top node visited and print it, using the stack depth as indentation
if there is no node
if the top nodes contains an unvisited node (increment the edge iterator until such a node is found) go to 1.
otherwise (the edge iterator reached the end) remove the top node and go to 3.
In code this would look something like this:
std::stack<std::pair<Node, int> > stack;
stack.push(std::make_pair(origin, 0));
while (!stack.empty()) {
std::pair<Node, int>& top = stack.top();
for (; top.second < top.first.adjacencyList.size(); ++top.second) {
Node& adjacent = top.first.adjacencyList[top.second];
if (!adjacent.visited) {
adjacent.visted = true;
stack.push(std::make_pair(adjacent, 0));
print(adjacent, stack.size());
break;
}
}
if (stack.top().first.adjacencyList.size() == stack.top().second) {
stack.pop();
}
}
Let Rep(Tree) be the representation of the tree Tree. Then, Rep(Tree) looks like this:
Root
<Rep(Subtree rooted at node 1)>
<Rep(Subtree rooted at node 2)>
.
.
.
So, have your dfs function simply return the representation of the subtree rooted at that node and modify this value accordingly. Alternately, just tell every dfs call to print the representation of the tree rooted at that node but pass it the current depth. Here's an example implementation of the latter approach.
void PrintRep(const Graph& g, Vertex current, int depth)
{
cout << std::string(' ', 2*depth) << current.name << endl;
current.visited = true;
for(int i = 0; i < current.adjacencyList.size(); i++)
if(current.adjacencyList[i].visited == false)
PrintRep(g, current.adjacencyList[i], depth+1);
}
You would call this function with with your origin and depth 0 like this:
PrintRep(g, origin, 0);

Dijkstra's Algorithm - Initializing Node Distances

I have an assignment to use Dijkstra's shortest path algorithm for a simple network simulation. There's one part of the coding implementation that I don't understand and it's giving me grief.
I searched around on stack overflow and found many helpful questions about Dijkstra's, but none with my specific question. I apologize if I didn't research thoroughly enough.
I'm using this pseudocode from Mark Allen Weiss's Data Structures and Algorithm Analysis in C++:
void Graph::dijkstra( Vertex s)
{
for each Vertex v
{
v.dist = INFINITY;
v.known = false;
}
s.dist = 0;
while( there is an unknown distance vertex )
{
Vertex v = smallest unknown distance vertex;
v.known = true;
for each Vertex w adjacent to v
{
if (!w.known)
{
int cvw = cost of edge from v to w;
if(v.dist + cvw < w.dist)
{
//update w
decrease(w.dist to v.dist + cvw);
w.path = v;
}
}
}
}
and my implementation seems to work aside from the last if statement.
if(v.dist + cvw < w.dist)
My code will never go into what's underneath because the distance for every node is initialized to (essentially) infinity and the algorithm never seems to change the distance. Therefore the left side of the comparison is never smaller than the right side. How am I misunderstanding this?
Here is my (messy) code:
class Vertex
{
private:
int id;
unordered_map < Vertex*, int > edges;
int load_factor;
int distance;
bool known;
public:
//getters and setters
};
void dijkstra(Vertex starting_vertex)
{
for (int i = 0; i < vertices.size(); i++)
{
//my program initially stores vertices in the vertex in spot (id - 1).
if (vertices[i].get_id() == starting_vertex.get_id())
{
vertices[i].set_distance(0);
vertices[i].set_known(true);
}
else
{
vertices[i].set_distance(10000000);
vertices[i].set_known(false);
}
}
for (int i = 0; i < vertices.size(); i++)
{
//while there is an unknown distance vertex
if (vertices[i].is_known() == false)
{
vertices[i].set_known(true);
//for every vertex adjacent to this vertex
for (pair<Vertex*, int> edge : vertices[i].get_edges())
{
//if the vertex isn't known
if (edge.first->is_known() == false)
{
//calculate the weight using Adam's note on dijkstra's algorithm
int weight = edge.second * edge.first->get_load_factor();
if (vertices[i].get_distance() + weight < edge.first->get_distance())
//this is my problem line. The left side is never smaller than the right.
{
edge.first->set_distance(vertices[i].get_distance() + weight);
path.add_vertex(edge.first);
}
}
}
}
}
}
Thank you!
You are missing out this step:
Vertex v = smallest unknown distance vertex;
and instead looping through all vertices.
The distance to the starting vertex is initialized to 0 so if you implement this part of the algorithm and pick the v with the smallest distance that is not "known" you will start with the starting vertex and the if should work.
Replace:
for (int i = 0; i < vertices.size(); i++)
{
//while there is an unknown distance vertex
if (vertices[i].is_known() == false)
{
...
}
}
With something like:
while(countNumberOfUnknownVertices(vertices) > 0)
{
Vertex& v = findUnknownVertexWithSmallestDistance(vertices);
...
}
You missed two important parts of Dijkstra's Algorithm.
In implementing
while( there is an unknown distance vertex )
{
Vertex v = smallest unknown distance vertex;
you set v to the first unknown vertex you come to. It's supposed to be, of all the unknown vertices, the one whose distance is least.
The other misstep is that, instead of making one pass over the vertices and doing some work on each unknown one you find, you need to search again after doing the work.
For example, if on one iteration you expand outward from vertex 5, that may make vertex 3 the new unknown vertex with least distance. You can't just continue the search from 5.
The search for the least-distance unknown vertex is going to be slow unless you develop some data structure (a Heap, perhaps) to make that search fast. Go ahead and do a linear search for now. Dijkstra's Algorithm will still work, but it'll take time O(N^2). You should be able to get it down to at least O(N log N).

Finding adjacent nodes in a tree

I'm developing a structure that is like a binary tree but generalized across dimensions so you can set whether it is a binary tree, quadtree, octree, etc by setting the dimension parameter during initialization.
Here is the definition of it:
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]);
}
}
So, to initialize it- I set a dimension and then subdivide. I am going for a quadtree of depth 2 for illustration here:
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;
This will result in a quadtree, the state of each of the values are initialized to [0-4][0-4].
([{0}{1}{2}{3}][{10}{11}{12}{13}][{20}{21}{22}{23}][{30}{31}{32}{33}])
I am having trouble finding adjacent nodes from any piece. What it needs to do is take a direction and then (if necessary) traverse up the tree if the direction goes off of the edge of the nodes parent (e.g. if we were on the bottom right of the quadtree square and we needed to get the piece to the right of it). My algorithm returns bogus values.
Here is how the arrays are laid out:
And here are the structures necessary to know for it:
This just holds the direction for items.
enum orientation : signed int {LEFT = -1, CENTER = 0, RIGHT = 1};
This holds a direction and whether or not to go deeper.
template <uint Dimension>
struct TraversalHelper {
std::array<orientation, Dimension> way;
bool deeper;
};
node_orientation_table holds the orientations in the structure. So in 2d, 0 0 refers to the top left square (or left left square).
[[LEFT, LEFT], [RIGHT, LEFT], [LEFT, RIGHT], [RIGHT, RIGHT]]
And the function getPositionFromOrientation would take LEFT, LEFT and return 0. It is just basically the opposite of the node_orientation_table above.
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;
}
For an example of this:
std::array<orientation, Dimension> direction;
direction[0] = LEFT; //x
direction[1] = CENTER; //y
NDTree<Dimension> * result = tree[3][0]->getAdjacentNode(direction);
This should grab the top right square within bottom left square, e.g. tree[2][1] which would have a value of 21 if we read its state. Which works since my last edit (algorithm is modified). Still, however, many queries do not return correct results.
//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 });
There are more examples of incorrect behavior such as tree[0][0](LEFT, LEFT), but many others work correctly.
Here is the folder of the git repo I am working from with this. Just run g++ -std=c++11 main.cpp from that directory if it is necessary.
here is one property you can try to exploit:
consider just the 4 nodes:
00 01
10 11
Any node can have up to 4 neighbor nodes; two will exist in the same structure (larger square) and you have to look for the other two in neighboring structures.
Let's focus on identifying the neighbors which are in the same structure: the neighbors for 00 are 01 and 10; the neighbors for 11 are 01 and 10. Notice that only one bit differs between neighbor nodes and that neighbors can be classified in horizontal and vertical. SO
00 - 01 00 - 01 //horizontal neighbors
| |
10 11 //vertical neighbors
Notice how flipping the MSB gets the vertical neighbor and flipping the LSB gets the horizontal node? Let's have a close look:
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
So now we can determine the node's in each direction assuming they exist in the same substructure. What about the node to the left of 00 or above 10?? According to the logic so far if you want a horizontal neighbor you should flip the LSB; but flipping it would retrieve 10 ( the node to the right). So let's add a new rule, for impossible operations:
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
*impossible operations refers to operations within the same structure.
Let's look at the bigger picture which are the up and left neighbors for 00? if we go left for 00 of strucutre 0 (S0), we should end up with 01 of(S1), and if we go up we end up with node 10 of S(2). Notice that they are basically the same horizontal/ veritical neighbor values form S(0) only they are in different structures. So basically if we figure out how to jump from one structure to another we have an algorithm.
Let's go back to our example: going up from node 00 (S0). We should end up in S2; so again 00->10 flipping the MSB. So if we apply the same algorithm we use within the structure we should be fine.
Bottom line:
valid transition within a 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.
I hope this idea is explicit enough
Check out this answer for neighbor search in octrees: https://stackoverflow.com/a/21513573/3146587. Basically, you need to record in the nodes the traversal from the root to the node and manipulate this information to generate the required traversal to reach the adjacent nodes.
The simplest answer I can think of is to get back your node from the root of your tree.
Each cell can be assigned a coordinate mapping to the deepest nodes of your tree. In your example, the (x,y) coordinates would range from 0 to 2dimension-1 i.e. 0 to 3.
First, compute the coordinate of the neighbour with whatever algorithm you like (for instance, decide if a right move off the edge should wrap to the 1st cell of the same row, go down to the next row or stay in place).
Then, feed the new coordinates to your regular search function. It will return the neighbour cell in dimension steps.
You can optimize that by looking at the binary value of the coordinates. Basically, the rank of the most significant bit of difference tells you how many levels up you should go.
For instance, let's take a quadtree of depth 4. Coordinates range from 0 to 15.
Assume we go left from cell number 5 (0101b). The new coordinate is 4 (0100b). The most significant bit changed is bit 0, which means you can find the neighbour in the current block.
Now if you go right, the new coordinate is 6 (0110b), so the change is affecting bit 1, which means you have to go up one level to access your cell.
All this being said, the computation time and volume of code needed to use such tricks seems hardly worth the effort to me.