I do not think this is an unreasonable question. A simple recursive solution is
boolean equals(x, y) { if (x == null) { return y == null; } if (y == null) { return false; } if (x.val != y.val) { return false; } if (equals(x.left, y.left) && equals(x.right, y.right)) { return true; } if (equals(x.left, y.right) && equals(x.right, y.left)) { return true; } return false; }
This can be very expensive, for example, if we have two large trees of the same shape, where all non-leaf nodes have the same associated value, and the leaf nodes of one are a permutation of the leaf nodes of the other.
To get past this, you can first change left and right as necessary, so that left <on the right, for some recursive definition of <. It can also be costly, but much less than checking every permutation, and I think choosing a definition of <would help. This will allow you to verify equality with the usual definition.
This concept of http://en.wikipedia.org/wiki/Canonicalization , followed by ordinary equality, also solves the question of whether you really have an equivalence relation. The equivalence relation is equivalent to a partition. Ordinary equality is obviously a section. If you compare x and y by comparing f (x) and f (y) followed by an equivalence relation, you have a partition of x and y and therefore an equivalence relation.
After thinking about this, I think that the way to make canonicalization or uniform testing reasonably effective is to work from the bottom up, annotating each node with a token whose value reflects the result of comparisons with other nodes, so that you can compare nodes and subtrees under them, just compare tokens .
Thus, the first step for equality is, for example, use a hash table to annotate each leaf with tokens that are equal only when the values in the leaves are equal. Then, for nodes in which only children are leaves, they use, for example, a hash table to assign additional tokens so that the markers in these nodes are equal only when the leaves, if any, under these nodes match. Then you can go one step further, and this time you can compare tokens in child nodes, rather than recursing down the tree. The cost of assigning tokens in this way should be linear in the size of the trees involved. At the top, you can compare trees by simply comparing tokens in the root.