简体   繁体   中英

BinaryTree in Java - build words and display it

I have a problem with the college that I can't figure out. I have a file with instructions to build a binary tree

r a
rr b
rl c
rrr h
rrl i
rlr j
rll k
d
l e
ll f
lr g
lll l
llr m
lrl n
lrr o

r - right, l - left, what is to be printed. "d" is the root

I load the file, break each line into instruction, and a value and I don't know what to do next - I'm stuck.

I must build a tree and use recursion to solve it... and print all words from tree-like "hbad", "kcad", "lfed" etc...

my code for now:

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        int i = 0;
        String key[] = new String[2];
        String root = "";
        ArrayList<String> lineFile = new ArrayList<String>();

        Scanner scan = null;
        try {
            scan = new Scanner(new File("tree.txt"));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        while (scan.hasNextLine()) {
            lineFile.add(scan.nextLine());
        }

        for (i = 0; i < lineFile.size(); i++) {
            if (lineFile.get(i).length() > 1) {
                key = lineFile.get(i).split(" ");
                System.out.print(key[0] + " " + key[1]);
            } else {
                root = lineFile.get(i);
            }
            System.out.println(" " + lineFile.get(i).length());
        }
        System.out.println(root);
    }
}

Pls help... any example implementation, link or else...

A Binary Tree, as mentioned, consists of a set of nodes. Each node has two children, left and right, which are also nodes. A simple node implementation might look like this:

public class BinaryTreeNode<T> {
    private final T value;
    private BinaryTreeNode<T> left;
    private BinaryTreeNode<T> right;

    public BinaryTreeNode(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }

    public BinaryTreeNode<T> getLeft() {
        return left;
    }

    public void setLeft(BinaryTreeNode<T> left) {
        this.left = left;
    }

    public BinaryTreeNode<T> getRight() {
        return right;
    }

    public void setRight(BinaryTreeNode<T> right) {
        this.right = right;
    }
}

This version is generic but you could just set value as String and make life easier. Create the base of the tree from your root value:

    BinaryTreeNode<String> tree = new BinaryTreeNode<>(root); 

Parsing your input and adding it to the tree is trickier. to simplify a bit I created a new Instruction class with the two parts of each line (position and value) split. I made an inner class for convenience (no need for getter/setter) but that's not necessary:

private class Instruction {
    String node;
    String value;

    public Instruction(String node, String value) {
        this.node = node;
        this.value = value;
    }
}

Create a list of Instructions:

List<Instruction> instructions = new ArrayList<>();

And fill it up in your loop:

    instructions.add(new Instruction(key[0], key[1]));

Now for the recursive loading. This is probably not the best solution but it works:

private void buildSubTree(BinaryTreeNode node, List<Instruction> instructions) {
    // Get the left branch
    List<Instruction> left = instructions.stream()
            .filter(i -> i.node.startsWith("l"))
            .collect(Collectors.toList());
    // Get the right branch
    List<Instruction> right = instructions.stream()
            .filter(i -> i.node.startsWith("r"))
            .collect(Collectors.toList());

    // Find the first left instruction
    Instruction firstLeft = left.stream().filter(i -> i.node.length() == 1).findFirst().orElse(null);
    // Find the first right instruction
    Instruction firstRight = right.stream().filter(i -> i.node.length() == 1).findFirst().orElse(null);

    if (firstLeft != null) {
        // Set the left child
        node.setLeft(new BinaryTreeNode(firstLeft.value));
        // Find left children by reducing instruction node String (remove first character)
        List<Instruction> leftChildren = left.stream().map(i -> new Instruction(i.node.substring(1), i.value)).collect(Collectors.toList());
        // Recursively build left branch
        buildSubTree(node.getLeft(), leftChildren);
    }

    if (firstRight != null) {
        // Set the right child
        node.setRight(new BinaryTreeNode(firstRight.value));
        // Find right children by reducing instruction node String (remove first character)
        List<Instruction> rightChildren = right.stream().map(i -> new Instruction(i.node.substring(1), i.value)).collect(Collectors.toList());
        // Recursively build right branch
        buildSubTree(node.getRight(), rightChildren);
    }
}

Build the tree:

buildSubTree(tree, instructions);

The method will build the two subtrees under the given node (the root node in this first call) and recursively call itself with each child node and the reduced set of instructions for each side. Some improvements could be done here and make the Instruction class more functional to help out.

Building words from the list is easier:

private List<String> buildWordsFromTreeLeafUp(BinaryTreeNode<String> startNode) {
    // List of words to return
    List<String> words = new ArrayList<>();

    // First populate the list with words from each child branch
    if (startNode.getLeft() != null) {
        words.addAll(buildWordsFromTreeLeafUp(startNode.getLeft()));
    }

    if (startNode.getRight() != null) {
        words.addAll(buildWordsFromTreeLeafUp(startNode.getRight()));
    }

    // Add the startNodes value to each word
    words = words.stream().map(s -> s + startNode.getValue()).collect(Collectors.toList());
    // Add the value as a word of it's own
    words.add(startNode.getValue());

    return words;
}

Not the most elegant implementation I bet but it gets the job done.

If you only want the full length (4 letters) words, here is an alternative to the last part of the word building method:

if (words.isEmpty()) {
    // Add the value as a word of it's own
    words.add(startNode.getValue());
} else {
    // Add the startNodes value to each word
    words = words.stream().map(s -> s + startNode.getValue()).collect(Collectors.toList());
}

Edit

The last code block above can replace the equivalent lines in buildWordsFromTreeLeafUp(). This is how it works:

  • If the current node is a leaf (that is, a node at the end of a branch with no children (left or right), the words list will be empty at this point. If so, we add the value (letter) of this node as a word.
  • If there are already words in the list it means that there was at least one child node. In that case we don't add the current letter as a new word. Instead we append the current letter at the end of each word already in the list. This way as the recursion returns one step at the time, we write the full four letter words only.

The method writes all the words, one letter at a time, but for each word.

In the first implementation above, we add the current letter as a new word as well. That way we will, in addition to all 4 letter words, also get all 3 and 2 letter words (starting somewhere in the middle of the tree) as well as a 1 letter word ('d') consisting only of the root element.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM