I don't 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 in the case when we have two large trees of similar shape where all the non-leaf nodes have the same associated value and the leaf nodes of one are a permutation of the leaf nodes of another.
To get past this you could first of all change left and right as required so that left < right, for some recursive definition of <. This could also be expensive, but much less so than checking every permutation, and I think a choice of definition of < would help. This would then allow you to check for equality with an ordinary definition.
This notion of http://en.wikipedia.org/wiki/Canonicalization followed by ordinary equality also resolves questions about whether you really do have an equivalence relation. An equivalence relation is equivalent to a partition. Ordinary equality is obviously a partition. 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.
Thinking more about this, I think the way to make either canonicalisation or equality-testing reasonably efficient 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 the subtrees below them, just be comparing tokens.
So the first step for equality is to e.g. use a hash table to annotate each leaf with tokens which are equal only when the values at the leaves are equal. Then, for nodes whose only children are leaves, use e.g. a hash table to assign further tokens so that the tokens in those nodes are equal only when the leaves, if any, beneath those nodes match. Then you can go one more step up, and this time you can compare tokens at child nodes instead of recursing down the tree there. 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 just by comparing the tokens at the root.