views:

255

answers:

4

Here's the node definition:

struct node{
    int data;
    stuct node * left;
    struct node * right;
};

What I am trying to do is list all the nodes that point to an ancestor node. After posting the wrong solution and taking advice from the answers, my new solution is:

Recursively go through the binary tree. Add the current node to an array of nodes and then check if the children of the current node point to any of the previous ancestor nodes.

The default case is the node being NULL. If that happens the function returns.

How it is supposed to work:

Adds the node to the array

Checks if the left child is NULL.

If not, it compares the child to each of the previous nodes.

If it finds a fault, it reports it.

If not, it calls the function with the child as the argument.

Repeat until finished. (Does same for rhs of binary tree)

Questions:

  • Is an array the best thing to store the nodes?
  • Does this work? for (i = 0; i < sizeof(arrOfNodes) / sizeof(node); i++)
  • Because the function is recursive, the array and the array index can't be initialized inside the function (or can they be?) so should they be global?
  • Would it be better to have two arrays? (one for the LHS and one for the RHS)

The code:

void findFault(node * root){
    if (root == NULL){
      return;
    }

    arrOfNodes[index++] == root; // array of nodes

    if (root->left != NULL){
      for (i = 0; i < sizeof(arrOfNodes) / sizeof(node); i++){
         if (ar[i] == root->left){
             printf("%d", root->left);
             return;
         }
       }
       findFault(root->left);
    } else return;

    if (root->right != NULL){
      for (i = 0; i < sizeof(ar) / sizeof(node); i++){
         if (ar[i] == root->right){
             printf("%d", root->right);
             return;
         }
      }
      findFault(root->right);
    } else return;
}
+6  A: 

I don't know about recursion, but this:

if (&root->left->left == &root){

is wrong in more ways that I can possibly describe, but anyway here are three issues:

  • Why are you taking the address of root?
  • Why don't you test that the first left pointer is null?
  • You could simply use a std::map, but learning how to implement a binary tree is a good idea too.
anon
As to your third question, the particular code they're working with may be legacy code, or may just be written in C, and thus they don't have access to std::map. A lot of C questions get mislabeled as C++ because of a) a general confusion that because one is based off the other, the languages should be considered similar, and b) the higher popularity of the C++ tag on this site.
Chris Lutz
Incorrect tagging by the questioner is really not my problem - - I'm not psychic.
anon
Thanks for the answer. I'm not using std::map because I want to learn how to do it this way. I tagged it c andc++ because without using the standard c++ library the solution could be done in both the same way, I think.
GreenRails
@Bua Of course, learning how to do it yourself is a valuable exercise. I'll modify my answer to reflect that.
anon
@Neil - Fair enough. I guess I can't require you to be psychic, no matter how cool that would be.
Chris Lutz
+5  A: 

This is an incorrect solution to the problem. Neil Butterworth already noted on your code, I'll note on the algorithm.

Your algorithm only checks a very specific case - whether a grandchild node points to its grandparent. What you should do is collect the parents along the way to a node and see that a node's child isn't one of its parents.

There are many ways to do this. One is to add a counter to your node struct and set all nodes' counters to zero before you begin traversing the tree. Whenever you reach a node you make sure the counter is zero and then increase it by one. This means that if you see a child whose counter isn't zero, you've already visited it and therefore the tree isn't valid.

Asaf R
That is a good idea, but I would prefer not to modify the node struct.
GreenRails
@Bua - You can use an external data structure for the counter, for instance you can use an std::map where the address is the key and the counter is the data.
Asaf R
A: 

I don't know if the algorithm that generates the binary tree is able to propagate a fault other than node's left/right child.

Anyway, this is a corrected version for your code:

void findFault(node * root){
    if (root == NULL){
      return;
    }

    if (root->left == root){
      printf("left: %d", root->data);
    } else findFault(root->left);

    if (root->right == root){
      printf("right: %d", root->data);
    } else findFault(root->right);
}
Nick D
+1  A: 

Another way to accomplish this kind of check is to do a breadth-first sweep of the nodes, all the while keeping a vector of nodes you have visited already (which you can keep sorted by address). Each time you visit a node, assert it is not in the vector, then add it to the appropriate place to keep the visited list sorted.

The advantage to this kind of check is it can be performed without modifying the tree or node struct itself, though there is a bit of a performance penalty.

Notes:

  • An array would be a fine way to store the nodes. If you're avoiding STL (curious: why?) then you'll have to manage your own memory. Doable, but it's a brittle wheel to reinvent.
  • Your for loop check to get the size of the arrays will not work; if you use malloc/free or new/delete then you'll have to specify the size of the array you want beforehand; you should use that size instead of calculating it every time through the for loop.
  • The typical pattern for a recursive algorithm is to have an "outer" and "inner" function. The outer function is the one called by external code and does the initial setup, etc. The inner function is only called by the outser function, tends to have a more complicated parameter set (taking data set up by the outer function), and calls itself to perform the actual recursion.
  • You will need two arrays: one for the list of nodes you have visited, and one for the list of nodes you have yet to visit.
fbrereto
Accepted this as the correct answer. I'll allocate memory as needed with malloc/free.I don't understand why I would need two arrays though.
GreenRails
You can use one array to track both nodes you've visited and nodes you have yet to visit, and have a pointer to the first node in each set. 6 of one, half-dozen of another for the most part.
fbrereto