| 
 Java Data Structures - Contents A D V E R T I S E M E N T
 
 
 
     Reading and writing binary trees to/from files is
not as simple as reading and writing arrays. They're not linear, and require some
algorithm to accomplish the task.     One simple approach would be to convert the tree to
a node-pool one, and simply save the node-pool, and the first node (or something along
those lines). That's the approach taken by lots of programs in saving trees. Actually,
most programs implement the tree as a node-pool from the start, so, the whole thing is
much simpler.     But what do you do when you're not using node-pools,
but would still like to save the tree (and it's structure)? You can use a technique known
as serialization.     There are many ways of serializing a binary tree,
and incidentally, I've lost the "formal algorithm," so, I'll re-derive
it again, maybe in a little bit different form. The basic idea is to first write the size
of data inside the node, and then the data itself. If there is no data you write zero, and
return (without writing the data). If you've written any data, you then recursively write
the left and right children of that node. There can be many variations of this; the most
obvious is switching the order of left and right child.     The reading process has to know the way in which the
tree was written. It has to first read the size of the data, if the size is zero, it sets
the pointer to null, and returns. If the size is not zero, it allocates a new node in the
tree, and reads the data into that node. Then, it recursively, reads the left and then the
right children for that node.     This simple algorithm is very effective, and can be
used to read and write data of different sizes (i.e.: character strings). The explanation
of it may not be too clear at first; that's why I've written some source to clear up the
confusion. The source that follows creates a binary tree holding strings, saves the tree
to a file, and later reads that file. import java.lang.*;
import java.io.*;
public class pBinTreeStringWR{
    public pBinTreeStringWR left,right;
    public String data;
    public static String[] strings = {"one","two",
        "three","four","five","six","seven","eight",
        "nine","ten","zero","computer","mouse","screen",
        "laptop","book","decimal","binary","quake"};
    public static pBinTreeStringWR tree_AddString(
        pBinTreeStringWR r,String s){
        if(r == null){
            r = new pBinTreeStringWR();
            r.left = r.right = null;
            r.data = s;
        }else if(r.data.compareTo(s) < 0)
            r.right = tree_AddString(r.right,s);
        else
            r.left = tree_AddString(r.left,s);
        return r;
    }
    public static void tree_InOrderPrint(
        pBinTreeStringWR r){
        if(r != null){
            tree_InOrderPrint(r.left);
            System.out.print(" "+r.data);
            tree_InOrderPrint(r.right);
        }
    }
    public static void tree_FileWrite(
        pBinTreeStringWR r,
        DataOutputStream output) throws IOException{
        if(r != null){
            byte[] tmp = r.data.getBytes();
            output.writeInt(tmp.length);
            output.write(tmp);
            tree_FileWrite(r.left,output);
            tree_FileWrite(r.right,output);
        }else
            output.writeInt(0);
    }
    public static pBinTreeStringWR tree_FileRead(
        pBinTreeStringWR r,
        DataInputStream input) throws IOException{
        int n = input.readInt();
        if(n != 0){
            byte[] tmp = new byte[n];
            input.read(tmp);
            r = new pBinTreeStringWR();
            r.data = new String(tmp);
            r.left = tree_FileRead(r.left,input);
            r.right = tree_FileRead(r.right,input);
        }else
            r = null;
        return r;
    }
    public static boolean tree_Compare(
        pBinTreeStringWR a,pBinTreeStringWR b){
        if(a != null && b != null){
            return a.data.compareTo(b.data) == 0 &&
                tree_Compare(a.left,b.left) &&
                tree_Compare(a.right,b.right);
        }else if(a == null && b == null)
            return true;
        else
            return false;
    }
    public static void main(String[] args){
        File file = new File("pBinTreeStringWR.dat");
        pBinTreeStringWR read_tree = null,tree = null;
        System.out.print("inserting: ");
        for(int i=0;i<strings.length;i++){
            String s = new String(strings[i]);
            System.out.print(" "+s);
            tree = tree_AddString(tree,s);
        }
        System.out.print("\ntree: ");
        tree_InOrderPrint(tree);
        System.out.println("\nwriting to "+file);
        try{
            tree_FileWrite(tree,
                new DataOutputStream(
                new FileOutputStream(file)));
        }catch(IOException e){
            System.out.println(e);
        }
        System.out.println("reading from "+file);
        try{
            read_tree = tree_FileRead(read_tree,
                new DataInputStream(
                new FileInputStream(file)));
        }catch(IOException e){
            System.out.println(e);
        }
        System.out.print("read tree: ");
        tree_InOrderPrint(read_tree);
        if(tree_Compare(tree,read_tree))
            System.out.println(
                "\nThe two trees are identical.");
        else
            System.out.println(
                "\nThe two trees are different.");
        System.out.println("done ;-)");
    }
}
     The program both illustrates the algorithm and tests
it. pBinTreeStringWRclass is itself a node. These nodes are manipulated
usingstaticmethods. I did it this way to reduce the number of needed
classes (combining the program class with the node class).     In the program, we start out by creating a Fileobject with the file we're about to write and read. We then declare two trees, one namedtree,
the other namedread_tree. We then loop to addstaticstrings to
thetree. The add function adds them in a binary search tree fashion; it is
using theComparableinterface to do the comparisons.     Then we open a file, create a DataOutputStreamobject to that file, and write the tree to that file usingtree_FileWrite()function. Thistree_FileWrite()closely matches the algorithm described
earlier. If the node is notnull, it writes thelengthof theString,
and then theStringitself (converted tobytes). It then
recursively writes theleftandrightchildren of that node. If
the node is initiallynull, the function simply writes a zero for thelength,
and returns.     The program continues by reading that file it has
just written. It stores the read tree in read_treevariable. The writing
process,tree_FileRead()also closely matches the algorithm described
earlier. It is basically the opposite of writing algorithm. We first read thelengthof theString, if it's zero, we set the node tonull, and
continue on. If thelengthis not zero, we read that number ofbytes,
and store them in that node'sdataString. We then continue
recursively by reading theleftandrightchildren of thattree.     The program then calls a compare function, which
compares the trees (including the tree structure). Tests indicate that the program is
working correctly, since the test function always returns that the tree written, and the
tree read are identical. In the middle of all this, there is an in-order print function,
but I don't think I need to describe it since I assume you know the basics of trees.
(Basically, go over the source and you'll understand the whole thing.) The output from the
program follows. inserting:  one two three four five six seven eight
nine ten zero computer mouse screen laptop book decimal binary quake
tree:  binary book computer decimal eight five four laptop mouse
nine one quake screen seven six ten three two zero
writing to pBinTreeStringWR.dat
reading from pBinTreeStringWR.dat
read tree:  binary book computer decimal eight five four laptop
mouse nine one quake screen seven six ten three two zero
The two trees are identical.
done ;-)
     Another useful trick that you can use when you are
not reading or writing variable length data like strings is not to write the length,
but simply some marker. For example, lets say your tree holds integers, and you know all
integers are32bits. Thus, yourlengthparameter can simply be
abooleanvalue of whether there exists a next node or not. The next example
illustrates this by storing abooleanvalue instead of thelengthparameter. import java.lang.*;
import java.io.*;
public class pBinTreeIntegerWR{
    public pBinTreeIntegerWR left,right;
    public Integer data;
    public static pBinTreeIntegerWR tree_AddNumber(
        pBinTreeIntegerWR r,Integer n){
        if(r == null){
            r = new pBinTreeIntegerWR();
            r.left = r.right = null;
            r.data = n;
        }else if(r.data.compareTo(n) < 0)
            r.right = tree_AddNumber(r.right,n);
        else
            r.left = tree_AddNumber(r.left,n);
        return r;
    }
    public static void tree_InOrderPrint(
        pBinTreeIntegerWR r){
        if(r != null){
            tree_InOrderPrint(r.left);
            System.out.print(" "+r.data);
            tree_InOrderPrint(r.right);
        }
    }
    public static void tree_FileWrite(
        pBinTreeIntegerWR r,
        DataOutputStream output) throws IOException{
        if(r != null){
            output.writeBoolean(true);
            output.writeInt(r.data.intValue());
            tree_FileWrite(r.left,output);
            tree_FileWrite(r.right,output);
        }else
            output.writeBoolean(false);
    }
    public static pBinTreeIntegerWR tree_FileRead(
        pBinTreeIntegerWR r,
        DataInputStream input) throws IOException{
        if(input.readBoolean()){
            r = new pBinTreeIntegerWR();
            r.data = new Integer(input.readInt());
            r.left = tree_FileRead(r.left,input);
            r.right = tree_FileRead(r.right,input);
        }else
            r = null;
        return r;
    }
    public static boolean tree_Compare(
        pBinTreeIntegerWR a,pBinTreeIntegerWR b){
        if(a != null && b != null){
            return a.data.compareTo(b.data) == 0 &&
                tree_Compare(a.left,b.left) &&
                tree_Compare(a.right,b.right);
        }else if(a == null && b == null)
            return true;
        else
            return false;
    }
    public static void main(String[] args){
        File file = new File("pBinTreeIntegerWR.dat");
        pBinTreeIntegerWR read_tree = null,tree = null;
        System.out.print("inserting: ");
        for(int i=0;i<10;i++){
            Integer n = new Integer((int)(Math.random()*100));
            System.out.print(" "+n);
            tree = tree_AddNumber(tree,n);
        }
        System.out.print("\ntree: ");
        tree_InOrderPrint(tree);
        System.out.println("\nwriting to "+file);
        try{
            tree_FileWrite(tree,
                new DataOutputStream(
                new FileOutputStream(file)));
        }catch(IOException e){
            System.out.println(e);
        }
        System.out.println("reading from "+file);
        try{
            read_tree = tree_FileRead(read_tree,
                new DataInputStream(
                new FileInputStream(file)));
        }catch(IOException e){
            System.out.println(e);
        }
        System.out.print("read tree: ");
        tree_InOrderPrint(read_tree);
        if(tree_Compare(tree,read_tree))
            System.out.println(
                "\nThe two trees are identical.");
        else
            System.out.println(
                "\nThe two trees are different.");
        System.out.println("done ;-)");
    }
}
     The program above is almost identical to the one
before, except it stores java.lang.Integerobjects instead ofjava.lang.Stringobjects. Since both of these implementjava.lang.Comparableinterface,
the functions look pretty much the same. The only functions which look different are the
file reading and writing. You should quickly notice that nolengthparameter
is involved, but a simplebooleanvalue telling us whether there exists a
next node. Output from the program above follows: inserting:  29 59 25 16 32 43 68 32 8 43
tree:  8 16 25 29 32 32 43 43 59 68
writing to pBinTreeIntegerWR.dat
reading from pBinTreeIntegerWR.dat
read tree:  8 16 25 29 32 32 43 43 59 68
The two trees are identical.
done ;-)
     The funny thing that I always save till the end is
that in practice, you'll probably never use any of these approaches yourself. Java
provides a fairly useful java.io.Serializableinterface, which given a tree,
will nicely store it into a file. Anyway, it does pay to know how these kinds of things
are done... 
 Back to Table of Contents 
 
A D V E R T I S E M E N T 
 
 
 
 
 
 
   
    |   | Subscribe to SourceCodesWorld - Techies Talk |  
 
 |