tree balancing act in C: can java do this?
Contents
So Jenny and I were looking at a
programming problem wherein there was a sequence like this:
while (nodes_on_left > nodes_on_right+1) {
parent = start;
node_to_move = start.leftChild;
while (node_to_move.rightChild == null) {
parent = node_to_move;
node_to_move = node_to_move.rightChild;
}
if (parent == start) {
start.leftChild = node_to_move.leftChild;
} else {
parent.rightChild = node_to_move.leftChild;
}
// code to make node_to_move the new "start" (i.e., root of this subtree)
// code to put the old "start" in the leftmost position in the right subtree
nodes_on_left--;
nodes_on_right++;
}
while (nodes_on_right > nodes_on_left+1) {
// Essentially the same code as above, except swapping "right" and "left"
}
I immediately thought of how I'd do this in C -- e.g., using the preprocessor to
create a macro "move1" which would look something like this:
#define move1(_start, _from, _to) { \
parent = start; \
node_to_move=start._from; \
while (node_to_move._to == null) { \
parent = node_to_move; \
node_to_move = node_to_move._to; \
} \
/* ... and so on ... */
but that seemed awfully long. So while riding on the airplane today I hacked
it together in C -- download treestuff.c).
I even ran it on the powerbook. Embarrassing number of bugs defects.
But it seems to work. It builds on Lintel and powerbook (G4) with no warnings, and
when run like this:
./treestuff foo bar baz bam bal bag baf bae bad bac bab baa ba0 ba
it balances the tree according to spec. Jenny can tell you more about how
the problem went, though my output is not nearly as nice as the prof's Eclipse/java
gooey thingie. Mine has "L-->" for the left child and "R-->" for the right child.
The output looks like this:
./treestuff foo bar baz bam bal bag baf bae bad bac bab baa ba0 ba
================ start tree dump ================
before: L-->L-->L-->L-->L-->L-->L-->L-->L-->L-->L-->L-->ba
before: L-->L-->L-->L-->L-->L-->L-->L-->L-->L-->L-->ba0
before: L-->L-->L-->L-->L-->L-->L-->L-->L-->L-->baa
before: L-->L-->L-->L-->L-->L-->L-->L-->L-->bab
before: L-->L-->L-->L-->L-->L-->L-->L-->bac
before: L-->L-->L-->L-->L-->L-->L-->bad
before: L-->L-->L-->L-->L-->L-->bae
before: L-->L-->L-->L-->L-->baf
before: L-->L-->L-->L-->bag
before: L-->L-->L-->bal
before: L-->L-->bam
before: L-->bar
before: L-->R-->baz
before: foo
================ end tree dump ================
calling rebalance_below()
================ start tree dump ================
after: L-->L-->L-->ba
after: L-->L-->ba0
after: L-->L-->R-->baa
after: L-->bab
after: L-->R-->L-->bac
after: L-->R-->bad
after: L-->R-->R-->bae
after: baf
after: R-->L-->L-->bag
after: R-->L-->bal
after: R-->L-->R-->bam
after: R-->bar
after: R-->R-->L-->baz
after: R-->R-->foo
================ end tree dump ================
Original problem and ./treestuff
run thereon
Original problem at http://www.cs.sunysb.edu/~cse214/hw/HW3.html (local copy) (we are doing part1 only).
Here's how it looks when run on that data set. Use your imagination to beautify my output:
% ./treestuff JPM JNJ HPQ BA AIG C INTC IBM MMM MCD T MO PFE MSFT MRK
================ start tree dump ================
before: L-->L-->L-->L-->AIG
before: L-->L-->L-->BA
before: L-->L-->L-->R-->C
before: L-->L-->HPQ
before: L-->L-->R-->L-->IBM
before: L-->L-->R-->INTC
before: L-->JNJ
before: JPM
before: R-->L-->MCD
before: R-->MMM
before: R-->R-->L-->MO
before: R-->R-->L-->R-->L-->L-->MRK
before: R-->R-->L-->R-->L-->MSFT
before: R-->R-->L-->R-->PFE
before: R-->R-->T
================ end tree dump ================
calling rebalance_below()
================ start tree dump ================
after: L-->L-->L-->AIG
after: L-->L-->BA
after: L-->L-->R-->C
after: L-->HPQ
after: L-->R-->L-->IBM
after: L-->R-->INTC
after: L-->R-->R-->JNJ
after: JPM
after: R-->L-->L-->MCD
after: R-->L-->MMM
after: R-->L-->R-->MO
after: R-->MRK
after: R-->R-->L-->MSFT
after: R-->R-->PFE
after: R-->R-->R-->T
================ end tree dump ================
%
So... can Java do that?
I am going to argue that having just one copy of "move1()
", rather than
two copies (wherein left/right are swapped between them) is better software practice.
Updated 2008-04-03
Yes, java can do that... most of it anyway. The key info is at
http://collinpark.blogspot.com/2008/04/what-are-pointers-and-why-should-i.html
Update: Python can!
Updated 3/26
But it's harder to read. Python doesn't have the address-of operator
(C's "&"), nor does it have C's "*" or "->".
But it does have arrays! Source (revised since last night) is now down
to 180 lines, with (some) special cases removed:
downloadable as treestuff.py
Python does have function pointers, or function references if you like.
The version above uses function pointers much like C does. For the
pointers into the data structures, well, Python arrays are always
references. So by changing child-node references to actually
use the arrays (the array has only one element, indexed of course
as "[0]"), and the functions return references to the arrays.
So, where C has
treenode_t **leftptr(treenode_t *p)
{
return &p->leftnode;
}
...
from_side = leftptr;
I have this in Python:
to_side = lambda x: x.leftnode
To set the leftnode, in C this could be possible
*from_side(node2move) = whatever;
whereas in Python I could say
from_side(node2move)[0] = whatever
Of course, this means "normal" references all have that '[0]' business
in the Python version,
which is what makes the C version easier reading than Python.
Updated 2008-04-03
Another Python implementation -- made lazier (following the move1
seen in
my blog posting) and shorter -- is at treestuff2.py; it has a special case like the Java version has, but this is probably justified since it's also quite a bit shorter.