"Robot Arm moving block stacks" Programming Challenge in C - c

I'm trying to resolve this for fun but I'm having a little bit of trouble on the implementation, the problem goes like this:
Having n stacks of blocks containing m blocks each, design a program in c that controlls a robotic arm that moves the blocks form an inicial configuration to a final one using the minimum amount of movements posible, your arm can only move one block at a time and can only take the block at the top of the stack, your solution should use either pointers or recursive methods
In other words the blocks should go from this(suposing there are 3 stacks and 3 blocks):
| || || |
|3|| || |
|2||1|| |
to this:
| ||1|| |
| ||2|| |
| ||3|| |
using the shortest amount of movements printing each move
I was thinking that maybe I could use a tree of some sorts to solve it (n-ary tree maybe?) since that is the perfect use of pointers and recursive methods but so far it has proved unsuccesfull, I'm having lots of trouble defining the estructure that will store all the movements since I would have to check every time I want to add a new move to the tree if that move has not been done before, I want each leaf to be unique so when I find the solution it will give me the shortest path.
This is the data structure I was thinking of:
typedef struct tree(
char[MAX_BLOCK][MAX_COL] value;
struct tree *kids
struct tree *brothers;
)Tree;
(I'm really new at C so sorry beforehand if this is all wrong, I'm more used to Java)
How would you guys do it? Do you have any good ideas?

You have the basic idea - though I am not sure why you have elected to choose brothers over the parent.
You can do this problem with a simple BFS search, but it is a slightly less interesting solution, and not the one you for which seemed to have set yourself up.
I think it will help if we concisely and clearly state our approach to the problem as a formulation of either Dijkstra's, A*, or some other search algorithm.
If you are unfamiliar with Dijkstra's, it is imperative that you read up on the algorithm before attempting any further. It is one of the foundational works in shortest path exploration.
With a familiarity of Dijkstra's, A* can readily be described as
Dijsktra's minimizes distance from the start. A* adds a heuristic which minimizes the (expected) distance to the end.
With this algorithm in mind, lets state the specific inputs to an A* search algorithm.
Given a start configuration S-start, and an ending configuration S-end, can we find the shortest path from S-start to S-end given a set of rules R governed by a reward function T
Now, we can envision our data structure not as a tree, but as a graph. Nodes will be board states, and we can transition from state to state using our rules, R. We will pick which edge to follow using the reward function T, the heuristic to A*.
What is missing from your data-structure is the cost. At each node, you will want to store the current shortest path, and whether it is finalized.
Let's make a modification to your data-structure which will allow us to readily traverse a graph and store the shortest path information.
typedef struct node {
char** boardState;
struct node *children;
struct node *parent;
int distance;
char status; //pseudo boolean
} node;
You may want to stop here if you were interested in discovering the algorithm for yourself.
We now consider the rules of our system: one block at a time, from the top of a stack. Each move will constitue an edge in our graph, whose weight is governed by the shortest number of moves from S-begin plus our added heuristic.
We can then sketch a draft of the algorithm as follows:
node * curr = S-begin;
while (curr != S-end) {
curr->status == 'T'; //T for True
for(Node child : children) {
// Only do this update if it is cheaper than the
int updated = setMin(child->distance, curr->distance + 1 + heuristic(child->board));
if(updated == 1) child->parent = curr;
}
//set curr to the node with global minimum distance who has not been explored
}
You can then find the shortest path by tracing the parents backwards from S-end to S-begin.
If you are interested in these types of problems, you should consider taking a uppergraduate level AI course, where they approach these types of problems :-)

Related

Clone a Binary Tree with Random Pointers

Can anyone explain the way of cloning the binary tree with random pointers apart from left to right? every node has following structure.
struct node {
int key;
struct node *left,*right,*random;
}
This is very popular interview question and I am able to figure out the solution based on hashing(which is similar to cloning of linked lists). I tried to understand the solution given in Link (approach 2) but am not able to figure out what does it want to convey by reading code also.
I don't expect solution based on hashing as it is intuitive and pretty straight forward. Please explain solution based on modifying binary tree and cloning it.
The solution presented is based on the idea of interleaving both trees, the original one and its clone.
For every node A in the original tree, its clone cA is created and inserted as A's left child. The original left child of A is shifted one level down in the tree structure and becomes a left child of cA.
For each node B, which is a right child of its parent P (i.e., B == P->right), a pointer to its clone node cB is copied to a clone of its parent.
P P
/ \ / \
/ \ / \
A B cP B
/ \ / \ / \
/ \ / \ / \
X Z A cB Z
/ \ /
cA cZ
/
X
/
cX
Finally we can extract the cloned tree by traversing the interleaved tree and unlinking every other node on each 'left' path (starting from root->left) together with its 'rightmost' descendants path and, recursively, every other 'left' descendant of those and so on.
What's important, each cloned node is a direct left child of its original node. So in the middle part of the algorithm, after inserting the cloned nodes but before extracting them, we can traverse the whole tree walking on original nodes, and whenever we find a random pointer, say A->random == Z, we can copy the binding into clones by setting cA->random = cZ, which resolves to something like
A->left->random = A->random->left;
This allows cloning random pointers directly and does not require additional hash maps (at the cost of interleaving new nodes into the original tree and extracting them later).
The interleaving method can be simplified a little, I think.
1) For every node A in the original tree, create clone cA with the same left and right pointers as A. Then, set As left pointer to cA.
P P
/ \ /
/ \ /
A B cP
/ \ / \
/ \ / \
X Z A B
/ /
cA cB
/ \
X Z
/ /
cX cZ
2) Now given a node and it's clone (which is just node.left), the random pointer for the clone is: node.random.left (if node.random exists).
3) Finally, the binary tree can be un-interleaved.
I find this interleaving makes reasoning about the code much simpler.
Here is the code:
def clone_and_interleave(root):
if not root:
return
clone_and_interleave(root.left)
clone_and_interleave(root.right)
cloned_root = Node(root.data)
cloned_root.left, cloned_root.right = root.left, root.right
root.left = cloned_root
root.right = None # This isn't necessary, but doesn't hurt either.
def set_randoms(root):
if not root:
return
cloned_root = root.left
set_randoms(cloned_root.left)
set_randoms(cloned_root.right)
cloned_root.random = root.random.left if root.random else None
def unterleave(root):
if not root:
return (None, None)
cloned_root = root.left
cloned_root.left, root.left = unterleave(cloned_root.left)
cloned_root.right, root.right = unterleave(cloned_root.right)
return (cloned_root, root)
def cloneTree(root):
clone_and_interleave(root)
set_randoms(root)
cloned_root, root = unterleave(root)
return cloned_root
The terminology used in those interview questions is absurdly bad. It’s the case of one unwitting kuckledgragger somewhere calling that pointer the “random” pointer and everyone just nods and accept this as if it was some CS mantra from an ivory tower. Alas, it’s sheer lunacy.
Either what you have is a tree or it isn’t. A tree is an acyclic directed graph with at most a single edge directed toward any node, and adding extra pointers can’t change it - the things the pointers point to must retain this property.
But when the node has a pointer that can point to any other node, it’s not a tree. You got a proper directed graph with cycles in it, and looking at it as if it were a tree is silly at this point. It’s not a tree. It’s just a generic directed edge graph that you’re cloning. So any relevant directed graph cloning technique will work, but the insistence on using the terms “tree” and “random pointer” obscure this simple fact, and confuse the matters terribly.
This snafu indicates that whoever came up with the question was not qualified to be doing any such interviewing. This stuff is covered in any decent introductory data structure textbook so you’d think it shouldn’t present some astronomical uphill effort to just articulate what you need in a straightforward manner. Let the interviewees deal with users who can’t articulate themselves once they get that job - the data structure interview is neither the place nor time for that. It reeks of stupidity and carelessness, and leaves permanently bad aftertaste. It’s probably yet another stupid thing that ended up in some “interview question bank” because one poor soul got it asked by a careless idiot once and now everyone treats it as gospel. It’s yet again the blind leading the blind and cluelessness abounds.
Copying arbitrary graphs is a well solved problem and in all cases you need to retain the state of your traversal somehow. Whether it’s done by inserting nodes into the original graph to mark the progress - one could call it intrusive marking - or by adding data to the copy in progress and removing it when done, or by using an auxiliary structure such as a hash, or by doing repeat traversal to check it you made a copy of that node elsewhere - is of secondary importance, since the purpose Is always the same: to retain the same state information, just encoding it in various ways, trading off speed and memory use (as always).
When thinking of this problem, you need to tell yourself what sort of state you need to finish the copy, and abstract it away, and implement the copy using this abstract interface. Then you can implement it in a few ways, but at that point the copy itself doesn’t obscure things since you look at this simple abstract state-preserving interface and not at the copy process.
In real life the choice of any particular implementation highly depends on the amount and structure of data being copied, and the extent you have control over it all. If you’re the one controlling the structure of the nodes, then you’ll usually find that they have some padding that you could use to store a bit of state information. Or you’ll find that the memory block allocated for the nodes is actually larger than requested: malloc will often end up providing a block larger than asked for, and all reasonable platforms have APIs that let you retrieve the actual size of the block and thus check if there’s maybe some leftover space just begging to be used. These APIs are not always fast so be careful there of course. But you see where this is going: such optimization requires benchmarks and a clear need driven by demands of the application. Otherwise, use whatever is least likely to be buggy - ideally a C library that provides data structures that you could use right away. If you need a cyclic graph there are libraries that do just that - use them first.
But boy, do I hate that idiotic “random” name of the pointer. Who comes up with this nonsense and why do they pollute so many minds? There’s nothing random about it. And a tree that’s not a tree is not a tree. I’d fail that interviewer in a split second…

Checking every bit is on some cycle

I'm coding a model where :
Node are represented as bitvectors of 10 length each representing some molecules and edges can take any molecules that was present at source to to a target node.
for example
S_Node : 0b0100000011 // Molecule 0 , 1 , 8 present on node
One_Edge : 0b0000000010 // Molecule 1 is going out on edge
I have to enforce condition that each outgoing Molecule on edge is coming back to the source node on some cycle. Molecule has to come back in a cycle means that during taking path of the cycle it has to be present on evry node and evry edge it takes.
* Parallel edges are allowed.
Molecule 1 takes path S_Node -> Node_1 -> Node_2 ... -> S_Node. So Molecule 1 started from S_Node on an edge and traveled through Node_1 ... and came back to S_Node on a cycle. Hence this molecule satisfies the condition.
Similarly i have to check for each molecule on each edge.
I'm doing in trivial possible way of checking for each nodes what are possible edges going out and then for each edge what are possible bits that are present and enforcing each coming back on some cycle.
for (i = 0; i < N; i++) { // for each Node
for (j = 0; j < E; j++) { // for each Edge going out frm node i
// Lets say we have some way of finding E
if(edgeWeight & (1 << j)) { //All outgoing bits
// Enforcing that each will come back
// On some Cycle
Its easily visible that i have to iterate over all nodes and then all edges going out and then for each bits on those edges, have to write code for enforcing the same. Enforcing itself have to iterate over at least no.Of Nodes #N.
Any better way to efficiently do this ? Any other way to check for same thing in graph theory ? Thanks
You seem to have a directed graph per molecule (per bit) Simply do your trick to check for any non-cycles per molecule.
You can take btillys way of checking for cycles, another option is to look at strongly connected components. You essentially want each subgraph (for a given molecule) to be a graph where each connected component is actually strongly connected. There are some good algorithms to for strongly connected components referred to from the wikipedia article linked to earlier.
The representation of nodes is irrelevant to the problem. You have a directed graph. You wish to verify that for every node and edge, there exists a cycle containing that edge. And you want to be reasonably efficient about it (rather than doing a brute force search for all possible cycles from all edges).
Here is an observation. Suppose that you find a cycle in your graph G. Consider the graph G' which is the same as your original graph EXCEPT that the cycle has been collapsed down to a single node. The answer to your question for G is the same as the answer to your question for G' because any cycle in G leads to a cycle in G' (possibly a self-intersecting one that can be turned into 2 cycles), and any cycle in G' leads to a cycle in G (if you hit the collapsed node, then follow its cycle around until you find the exit point to continue).
So now the question goes from brute force discovery of cycles to collapsing cycles until you have a small graph where the question is easily answered. So for each node, for each edge, you start a path. Your path continues until you have discovered a cycle. Any cycle. (Not necessarily back to the original node!) Collapse that cycle, and keep traveling until you either have to backtrack (in which case your condition is not met) or you manage to loop back to your original node, collapse that cycle, and move on to another edge.
If you implement this, you'll have a polynomial algorithm, but not the best you can do. The problem is that creating new graphs with a cycle collapsed is an expensive operation. But there is a trick that helps. Instead of collapsing the graph every time you find a cycle, try to be lazy about it. Create a new "fake node" for that cycle, and mark each node in that cycle as going to that fake one. Every time you see an edge that goes to a node, do a recursive search through those mappings to the most collapsed node that you've found, and mark everything you saw in that search as directly mapping there.
If you implement the lazy bit well, your overall algorithm should wind up O(E) where E is the number of edges in your graph. You actually can't do better than that given that you have to visit every edge no matter what you do.

Using A* search algorithm to solve 3x3 three-dimensional box puzzle?

I am working on a 3x3 three-dimensional box puzzle problem in my homework. I will code with C.
There are 26 boxes and at first, first place is empty. By sliding boxes I must arrange them in correct order. Red numbers shows correct order and 27th place must be empty at last. I do not want you to give me code; I searched in forums and it seems that I must use the A* search algorithm, but how?
Can you give me tips about how I can use the A* algorithm on this problem? What type of data structure should I use?
Define your problem as a states-graph:
G=(V,E) where V=S={(x_1,x_2,...,x_54) | all possible states the 3d board can be in} [each number is representing a single 'square' on the 3d board].
and define E={(v1,v2)| it is possible to move from state v1 to state v2 with a single step} an alternative definition [identical] for E is by using the function successors(v):
For each v in V: successors(v)={all possible boards you can get, with 1 step from v}
You will also need an admissible heuristic function, a pretty good one for this problem can be: h(state)=Sigma(manhattan_distance(x_i)) where i in range [1,54]) basically, it is the summation of manhattan distances for each number from its target.
Now, once we got this data, we can start running A* on the defined graph G, with the defined heuristic. And since our heuristic function is admissible [convince yourself why!], it is guaranteed that the solution A* finds will be optimal, due to admissibility and optimality of A*.
Finding the actual path: A* will end when you develop the target state. [x_i=i in the terms we used earlier]. You will find your path to it by stepping back from the target to the source, using the parent field in each node.
You know how graphs work and how A* finds shortest paths on them, right?
The basic idea is that each configuration of the puzzle can be considered a vertex in a graph and the edges represent the moves (by connecting the configurations before and after the move).
Finding a set of moves that leads from an original configuration to a desired one can be seen as a path finding problem.

Pacman: how do the eyes find their way back to the monster hole?

I found a lot of references to the AI of the ghosts in Pacman, but none of them mentioned how the eyes find their way back to the central ghost hole after a ghost is eaten by Pacman.
In my implementation I implemented a simple but awful solution. I just hard coded on every corner which direction should be taken.
Are there any better/or the best solution? Maybe a generic one that works with different level designs?
Actually, I'd say your approach is a pretty awesome solution, with almost zero-run time cost compared to any sort of pathfinding.
If you need it to generalise to arbitrary maps, you could use any pathfinding algorithm - breadth-first search is simple to implement, for example - and use that to calculate which directions to encode at each of the corners, before the game is run.
EDIT (11th August 2010): I was just referred to a very detailed page on the Pacman system: The Pac-Man Dossier, and since I have the accepted answer here, I felt I should update it. The article doesn't seem to cover the act of returning to the monster house explicitly but it states that the direct pathfinding in Pac-Man is a case of the following:
continue moving towards the next intersection (although this is essentially a special case of 'when given a choice, choose the direction that doesn't involve reversing your direction, as seen in the next step);
at the intersection, look at the adjacent exit squares, except the one you just came from;
picking one which is nearest the goal. If more than one is equally near the goal, pick the first valid direction in this order: up, left, down, right.
I've solved this problem for generic levels that way: Before the level starts, I do some kind of "flood fill" from the monster hole; every tile of the maze that isn't a wall gets a number that says how far it is away from the hole. So when the eyes are on a tile with a distance of 68, they look which of the neighbouring tiles has a distance of 67; that's the way to go then.
For an alternative to more traditional pathfinding algorithms, you could take a look at the (appropriately-named!) Pac-Man Scent Antiobject pattern.
You could diffuse monster-hole-scent around the maze at startup and have the eyes follow it home.
Once the smell is set up, runtime cost is very low.
Edit: sadly the wikipedia article has been deleted, so WayBack Machine to the rescue...
You should take a look a pathfindings algorithm, like Dijsktra's Algorithm or A* algorithm. This is what your problem is : a graph/path problem.
Any simple solution that works is maintainable, reliable and performs well enough is a good solution. It sounds to me like you have already found a good solution ...
An path-finding solution is likely to be more complicated than your current solution, and hence more likely to require debugging. It will probably also be slower.
IMO, if it ain't broken, don't fix it.
EDIT
IMO, if the maze is fixed then your current solution is good / elegant code. Don't make the mistake of equating "good" or "elegant" with "clever". Simple code can also be "good" and "elegant".
If you have configurable maze levels, then maybe you should just do the pathfinding when you initially configure the mazes. Simplest would be to get the maze designer to do it by hand. I'd only bother automating this if you have a bazillion mazes ... or users can design them.
(Aside: if the routes are configured by hand, the maze designer could make a level more interesting by using suboptimal routes ... )
In the original Pacman the Ghost found the yellow pill eater by his "smell" he would leave a trace on the map, the ghost would wander around randomly until they found the smell, then they would simply follow the smell path which lead them directly to the player. Each time Pacman moved, the "smell values" would get decreased by 1.
Now, a simple way to reverse the whole process would be to have a "pyramid of ghost smell", which has its highest point at the center of the map, then the ghost just move in the direction of this smell.
Assuming you already have the logic required for chasing pacman why not reuse that? Just change the target. Seems like it would be a lot less work than trying to create a whole new routine using the exact same logic.
It's a pathfinding problem. For a popular algorithm, see http://wiki.gamedev.net/index.php/A*.
How about each square having a value of distance to the center? This way for each given square you can get values of immediate neighbor squares in all possible directions. You pick the square with the lowest value and move to that square.
Values would be pre-calculated using any available algorithm.
This was the best source that I could find on how it actually worked.
http://gameai.com/wiki/index.php?title=Pac-Man#Respawn
When the ghosts are killed, their disembodied eyes return to their starting location. This is simply accomplished by setting the ghost's target tile to that location. The navigation uses the same rules.
It actually makes sense. Maybe not the most efficient in the world but a pretty nice way to not have to worry about another state or anything along those lines you are just changing the target.
Side note: I did not realize how awesome those pac-man programmers were they basically made an entire message system in a very small space with very limited memory ... that is amazing.
I think your solution is right for the problem, simpler than that, is to make a new version more "realistic" where ghost eyes can go through walls =)
Here's an analog and pseudocode to ammoQ's flood fill idea.
queue q
enqueue q, ghost_origin
set visited
while q has squares
p <= dequeue q
for each square s adjacent to p
if ( s not in visited ) then
add s to visited
s.returndirection <= direction from s to p
enqueue q, s
end if
next
next
The idea is that it's a breadth-first search, so each time you encounter a new adjacent square s, the best path is through p. It's O(N) I do believe.
I don't know much on how you implemented your game but, you could do the following:
Determine the eyes location relative position to the gate. i.e. Is it left above? Right below?
Then move the eyes opposite one of the two directions (such as make it move left if it is right of the gate, and below the gate) and check if there are and walls preventing you from doing so.
If there are walls preventing you from doing so then make it move opposite the other direction (for example, if the coordinates of the eyes relative to the pin is right north and it was currently moving left but there is a wall in the way make it move south.
Remember to keep checking each time to move to keep checking where the eyes are in relative to the gate and check to see when there is no latitudinal coordinate. i.e. it is only above the gate.
In the case it is only above the gate move down if there is a wall, move either left or right and keep doing this number 1 - 4 until the eyes are in the den.
I've never seen a dead end in Pacman this code will not account for dead ends.
Also, I have included a solution to when the eyes would "wobble" between a wall that spans across the origin in my pseudocode.
Some pseudocode:
x = getRelativeOppositeLatitudinalCoord()
y
origX = x
while(eyesNotInPen())
x = getRelativeOppositeLatitudinalCoordofGate()
y = getRelativeOppositeLongitudinalCoordofGate()
if (getRelativeOppositeLatitudinalCoordofGate() == 0 && move(y) == false/*assume zero is neither left or right of the the gate and false means wall is in the way */)
while (move(y) == false)
move(origX)
x = getRelativeOppositeLatitudinalCoordofGate()
else if (move(x) == false) {
move(y)
endWhile
dtb23's suggestion of just picking a random direction at each corner, and eventually you'll find the monster-hole sounds horribly ineficient.
However you could make use of its inefficient return-to-home algorithm to make the game more fun by introducing more variation in the game difficulty. You'd do this by applying one of the above approaches such as your waypoints or the flood fill, but doing so non-deterministically. So at every corner, you could generate a random number to decide whether to take the optimal way, or a random direction.
As the player progresses levels, you reduce the likelihood that a random direction is taken. This would add another lever on the overall difficulty level in addition to the level speed, ghost speed, pill-eating pause (etc). You've got more time to relax while the ghosts are just harmless eyes, but that time becomes shorter and shorter as you progress.
Short answer, not very well. :) If you alter the Pac-man maze the eyes won't necessarily come back. Some of the hacks floating around have that problem. So it's dependent on having a cooperative maze.
I would propose that the ghost stores the path he has taken from the hole to the Pacman. So as soon as the ghost dies, he can follow this stored path in the reverse direction.
Knowing that pacman paths are non-random (ie, each specific level 0-255, inky, blinky, pinky, and clyde will work the exact same path for that level).
I would take this and then guess there are a few master paths that wraps around the entire
maze as a "return path" that an eyeball object takes pending where it is when pac man ate the ghost.
The ghosts in pacman follow more or less predictable patterns in terms of trying to match on X or Y first until the goal was met. I always assumed that this was exactly the same for eyes finding their way back.
Before the game begins save the nodes (intersections) in the map
When the monster dies take the point (coordinates) and find the
nearest node in your node list
Calculate all the paths beginning from that node to the hole
Take the shortest path by length
Add the length of the space between the point and the nearest node
Draw and move on the path
Enjoy!
My approach is a little memory intensive (from the perspective of Pacman era), but you only need to compute once and it works for any level design (including jumps).
Label Nodes Once
When you first load a level, label all the monster lair nodes 0 (representing the distance from the lair). Proceed outward labelling connected nodes 1, nodes connected to them 2, and so on, until all nodes are labelled. (note: this even works if the lair has multiple entrances)
I'm assuming you already have objects representing each node and connections to their neighbours. Pseudo code might look something like this:
public void fillMap(List<Node> nodes) { // call passing lairNodes
int i = 0;
while(nodes.count > 0) {
// Label with distance from lair
nodes.labelAll(i++);
// Find connected unlabelled nodes
nodes = nodes
.flatMap(n -> n.neighbours)
.filter(!n.isDistanceAssigned());
}
}
Eyes Move to Neighbour with Lowest Distance Label
Once all the nodes are labelled, routing the eyes is trivial... just pick the neighbouring node with the lowest distance label (note: if multiple nodes have equal distance, it doesn't matter which is picked). Pseudo code:
public Node moveEyes(final Node current) {
return current.neighbours.min((n1, n2) -> n1.distance - n2.distance);
}
Fully Labelled Example
For my PacMan game I made a somewhat "shortest multiple path home" algorithm which works for what ever labyrinth I provide it with (within my set of rules). It also works across them tunnels.
When the level is loaded, all the path home data in every crossroad is empty (default) and once the ghosts start to explore the labyrinth, them crossroad path home information keeps getting updated every time they run into a "new" crossroad or from a different path stumble again upon their known crossroad.
The original pac-man didn't use path-finding or fancy AI. It just made gamers believe there is more depth to it than it actually was, but in fact it was random. As stated in Artificial Intelligence for Games/Ian Millington, John Funge.
Not sure if it's true or not, but it makes a lot of sense to me. Honestly, I don't see these behaviors that people are talking about. Red/Blinky for ex is not following the player at all times, as they say. Nobody seems to be consistently following the player, on purpose. The chance that they will follow you looks random to me. And it's just very tempting to see behavior in randomness, especially when the chances of getting chased are very high, with 4 enemies and very limited turning options, in a small space. At least in its initial implementation, the game was extremely simple. Check out the book, it's in one of the first chapters.

How to get the size of a binary tree?

I have a very simple binary tree structure, something like:
struct nmbintree_s {
unsigned int size;
int (*cmp)(const void *e1, const void *e2);
void (*destructor)(void *data);
nmbintree_node *root;
};
struct nmbintree_node_s {
void *data;
struct nmbintree_node_s *right;
struct nmbintree_node_s *left;
};
Sometimes i need to extract a 'tree' from another and i need to get the size to the 'extracted tree' in order to update the size of the initial 'tree' .
I was thinking on two approaches:
1) Using a recursive function, something like:
unsigned int nmbintree_size(struct nmbintree_node* node) {
if (node==NULL) {
return(0);
}
return( nmbintree_size(node->left) + nmbintree_size(node->right) + 1 );
}
2) A preorder / inorder / postorder traversal done in an iterative way (using stack / queue) + counting the nodes.
What approach do you think is more 'memory failure proof' / performant ?
Any other suggestions / tips ?
NOTE: I am probably going to use this implementation in the future for small projects of mine. So I don't want to unexpectedly fail :).
Just use a recursive function. It's simple to implement this way and there is no need to make it more compilcated.
If you were doing it "manually" you'd basically end up implementing the same thing, just that you wouldn't use the system call stack for temporary variables but your own stack. Usually this won't have any advantages outweighing the more complicated code.
If you later find out that a substantial time in your program is spend calculating the sizes of trees (which probably won't happen) you can still start to profile things and try how a manual implementation performs. But then it might also better to do algorithmic improvements like already keeping track of the changes in size during the extraction process.
If your "very simple" binary tree isn't balanced, then the recursive option is scary, because of the unconstrained recursion depth. The iterative traversals have the same time problem, but at least the stack/queue is under your control, so you needn't crash. In fact, with flags and an extra pointer in each node and exclusive access, you can iterate over your tree without any stack/queue at all.
Another option is for each node to store the size of the sub-tree below it. This means that whenever you add or remove something, you have to track all the way up to the root updating all the sizes. So again if the tree isn't balanced that's a hefty operation.
If the tree is balanced, though, then it isn't very deep. All options are failure-proof, and performance is estimated by measurement :-) But based on your tree node struct, either it's not balanced or else you're playing silly games with flags in the least significant bits of pointers...
There might not be much point being very clever with this. For many practical uses of a binary tree (in particular if it's a binary search tree), you realise sooner rather than later that you want it to be balanced. So save your energy for when you reach that point :-)
How big is this tree, and how often do you need to know its size? As sth said, the recursive function is the simplest and probably the fastest.
If the tree is like 10^3 nodes, and you change it 10^3 times per second, then you could just keep an external count, which you decrement when you remove a node, and increment when you add one. Other than that, simple is best.
Personally, I don't like any solution that requires decorating the nodes with extra information like counts and "up" pointers (although sometimes I do it). Any extra data like that makes the structure denormalized, so changing it involves extra code and extra chances for errors.

Resources