Cute trick, apparently old but I didn't know about it: you can have Union Find with union-by-size or union-by-rank without an extra array, at the cost of 1 bit per element. That bit stores whether that element is a root, in which case the rest of the bits store the size or rank, otherwise the rest of the bits store the parent as usual.
reference (but they do it slightly differently than I explained it): https://eecs.wsu.edu/~ananth/CptS223/Fall2009-webpage/Lectures/UnionFind.pdf