How to Recursively Read Elements of a Binary Tree Java Into Array

Binary Tree With Java Examples

Binary Tree (with Java Code)

Author image

by Sven WoltmannMay 28, 2021

Two of the most important topics in computer science are sorting and searching data sets. A data construction often used for both is the binary tree and its concrete implementations binary search tree and binary heap.

In this article, you will learn:

  • What is a binary tree?
  • What types of binary trees exist?
  • How to implement a binary tree in Java?
  • What operations do binary copse provide?
  • What are pre-society, in-club, post-order, and level-order traversal in binary trees?

You tin can find the source code for the article in this GitHub repository.

Binary Tree Definition

A binary tree is a tree information structure in which each node has at well-nigh 2 kid nodes. The child nodes are called left child and right kid.

Binary Tree Case

As an example, a binary tree looks like this:

Binary tree example
Binary tree example

Binary Tree Terminology

Every bit a developer, you should know the following terms:

  • A node is a structure that contains data and optional references to a left and a right child node (or just kid).
  • The connection between ii nodes is called an edge.
  • The top node is chosen the root or root node.
  • A node that has children is an inner node (brusque: inode) and, at the same fourth dimension, the parent node of its kid(ren).
  • A node without children is called an outer node or leaf node, or merely a leaf.
  • A node with only one child is a half node. Attention: this term exists – in contrast to all others – simply for binary trees, non for trees in general.
  • The number of child nodes is also chosen the caste of a node.
  • The depth of a node indicates how many levels the node is away from the root. Therefore, the root has a depth of 0, the root's children have a depth of 1, and so on.
  • The height of a binary tree is the maximum depth of all its nodes.

The following image shows the same binary tree data construction as earlier, labeled with node types, node depth, and binary tree height.

Binary tree data structure with node types
Binary tree information construction with node types

Binary Trees Properties

Earlier we get to the implementation of binary trees and their operations, permit's first briefly await at some special binary tree types.

Full Binary Tree

In a full binary tree, all nodes accept either no children or two children.

Full binary tree
Total binary tree

Complete Binary Tree

In a complete binary tree, all levels, except perhaps the terminal ane, are completely filled. If the last level is not completely filled, so its nodes are bundled as far to the left every bit possible.

Complete binary tree
Complete binary tree

Perfect Binary Tree

A perfect binary tree is a full binary tree in which all leaves have the same depth.

Perfect binary tree of height 3
Perfect binary tree of tiptop 3

A perfect binary tree of height h has northward = twoh+1-ane nodes and l = twoh leaves.

At the height of 3, that's xv nodes, 8 of which are leaves.

Balanced Binary Tree

In a counterbalanced binary tree, each node'south left and right subtrees differ in pinnacle by at most one.

Balanced binary tree
Balanced binary tree

Sorted Binary Tree

In a sorted binary tree (also known as ordered binary tree), the left subtree of a node contains just values less than (or equal to) the value of the parent node, and the correct subtree contains only values greater than (or equal to) the value of the parent node. Such a data construction is likewise called a binary search tree.

Binary Tree in Java

For the binary tree implementation in Java, we first define the data structure for the nodes (class Node in the GitHub repository). For simplicity, we apply int primitives every bit node information. We can, of class, use any other or a generic data type; however, with an int, the code is more than readable – and that is most of import for this tutorial.

              

public course Node { int information; Node left; Node right; Node parent; public Node (int information) { this.data = data; } }

Code language: Java ( java )

The parent reference is not mandatory for storing and displaying the tree. All the same, it is helpful – at least for sure types of binary trees – when deleting nodes.

The binary tree itself initially consists only of the interface BinaryTree and its minimal implementation BaseBinaryTree, which initially contains but a reference to the root node:

              

public interface BinaryTree { Node getRoot () ; } public class BaseBinaryTree implements BinaryTree { Node root; @Override public Node getRoot () { return root; } }

Lawmaking language: Coffee ( coffee )

Why nosotros bother to define an interface here will become credible in the further class of the tutorial.

The binary tree data structure is thus fully divers.

Binary Tree Traversal

An essential operation on binary trees is the traversal of all nodes, i.e., visiting all nodes in a particular order. The most mutual types of traversal are:

  • Depth-first search (pre-order, mail-order, in-order, reverse in-gild traversal)
  • Breadth-showtime search (level-club traversal)

In the post-obit sections, you will see the dissimilar types illustrated past the following example:

Example of binary tree traversal
Example for binary tree traversal

We implement the visiting during traversal using the visitor design design, i.e., nosotros create a visitor object which we laissez passer to the traversal method.

Depth-First Search in a Binary Tree

In depth-outset search (DFS), we perform the following three operations in a specific order:

  • visiting the current node (from now on referred to equally "Northward),
  • the depth-get-go search is invoked recursively on the left kid (referred to as "L"),
  • the depth-first search is invoked recursively on the right child (referred to as "R").

The standard sequences are:

Binary Tree Pre-Order Traversal

In pre-club traversal (as well known as NLR), traversing is performed in the post-obit social club:

  1. visiting the current node "N",
  2. recursive invocation of depth-first search on left subtree "L",
  3. recursive invocation of depth-first search on right subtree "R".

The nodes of the example tree are visited in the following lodge, every bit shown in the diagram below: 3→1→13→10→11→16→15→2

Binary Tree Preorder Traversal
Binary tree pre-lodge traversal

The code for this is adequately uncomplicated (DepthFirstTraversalRecursive form, starting at line 21):

              

private void traversePreOrder (Node node, NodeVisitor visitor) { if (node == null) { render; } company.visit(node); traversePreOrder(node.left, visitor); traversePreOrder(node.right, company); }

Code language: Java ( java )

Y'all can either invoke the method direct – in which example you must pass the the root node to it – or via the non-static method traversePreOrder() in the same class (DepthFirstTraversalRecursive, starting at line 17):

              

public void traversePreOrder (NodeVisitor company) { traversePreOrder(tree.getRoot(), visitor); }

Code language: Java ( java )

This requires creating an example of DepthFirstTraversalRecursive, passing a reference to the binary tree to the constructor:

              

new DepthFirstTraversalRecursive(tree).traversePreOrder(visitor);

Code language: Coffee ( java )

An iterative implementation is also possible using a stack (course DepthFirstTraversalIterative from line 20). The iterative implementations are pretty circuitous, which is why I do not impress them hither.

You can read why I use ArrayDeque instead of Stack in iterative tree traversals hither: Why y'all should not use Stack.

Binary Tree Post-Society Traversal

In post-club traversal (as well known as LRN), traversing is performed in the following order:

  1. recursive invocation of depth-outset search on left subtree "L",
  2. recursive invocation of depth-first search on right subtree "R",
  3. visiting the electric current node "Due north".

In this case, the nodes of the example tree are visited in the following order: xiii→1→11→xv→2→16→x→3

Binary tree postorder traversal
Binary tree mail service-order traversal

You can observe the code in DepthFirstTraversalRecursive starting at line 42:

              

public static void traversePostOrder (Node node, NodeVisitor company) { if (node == nada) { render; } traversePostOrder(node.left, visitor); traversePostOrder(node.correct, company); visitor.visit(node); }

Code language: Java ( java )

You can find the iterative implementation, which is fifty-fifty more complicated for mail-order traversal than for pre-order traversal, in DepthFirstTraversalIterative starting at line 44.

Binary Tree In-Order Traversal

In-club traversal (also known as LNR) traverses the tree in the post-obit order:

  1. recursive invocation of depth-offset search on left subtree "50",
  2. visiting the electric current node "North",
  3. recursive invocation of depth-first search on right subtree "R".

The nodes of the example tree are visited in the following guild: 13→one→3→11→10→15→16→2

Binary tree inorder traversal
Binary tree in-order traversal

Y'all will discover the recursive code in DepthFirstTraversalRecursive starting at line 62:

              

public static void traverseInOrder (Node node, NodeVisitor company) { if (node == null) { return; } traverseInOrder(node.left, company); visitor.visit(node); traverseInOrder(node.right, visitor); }

Code language: Coffee ( java )

See DepthFirstTraversalIterative starting at line 69 for the iterative implementation of in-order traversal.

In a binary search tree, in-order traversal visits the nodes in the order in which they are sorted.

Binary Tree Reverse In-Guild Traversal

Reverse in-order traversal (also known as RNL) traverses the tree in the following reverse guild:

  1. recursive invocation of depth-kickoff search on right subtree "R",
  2. visiting the electric current node "N",
  3. recursive invocation of depth-commencement search on left subtree "L".

The nodes of the sample tree are visited in reverse order to the in-order traversal: 2→16→15→10→eleven→iii→1→thirteen

Binary tree reverse inorder traversal
Binary tree reverse in-social club traversal

Y'all will discover the recursive code in DepthFirstTraversalRecursive starting at line 83:

              

public static void traverseReverseInOrder (Node node, NodeVisitor visitor) { if (node == null) { return; } traverseReverseInOrder(node.right, visitor); visitor.visit(node); traverseReverseInOrder(node.left, visitor); }

Code language: Java ( java )

You lot can find the iterative implementation of contrary in-lodge traversal in DepthFirstTraversalIterative starting at line 89.

In a binary search tree, reverse in-order traversal visits the nodes in descending sort gild.

Binary Tree Level-Order Traversal

In breadth-first search (BFS) – also called level-order traversal – nodes are visited starting from the root, level by level, from left to correct.

Level-order traversal results in the following sequence: 3→ane→10→thirteen→11→16→15→ii

Binary tree level order traversal
Binary tree level-social club traversal

To visit the nodes in level-lodge, nosotros demand a queue in which nosotros commencement insert the root node and then repeatedly remove the get-go element, visit it, and add together its children to the queue – until the queue is empty again.

You can find the code in the BreadthFirstTraversal form:

              

public static void traverseLevelOrder (Node root, NodeVisitor visitor) { if (root == nada) { return; } Queue<Node> queue = new ArrayDeque<>(); queue.add together(root); while (!queue.isEmpty()) { Node node = queue.poll(); visitor.visit(node); if (node.left != cypher) { queue.add(node.left); } if (node.right != null) { queue.add(node.right); } } }

Code language: Java ( java )

You tin observe examples for invoking all traversal types in the traverseTreeInVariousWays() method of the Example1 form.

Binary Tree Operations

Besides traversal, other basic operations on binary copse are the insertion and deletion of nodes.

Search operations are provided by special binary trees such as the binary search tree. Without special backdrop, we tin can search a binary tree simply by traversing over all nodes and comparing each with the searched element.

Insertion of a Node

When inserting new nodes into a binary tree, we have to distinguish unlike cases:

Case A: Inserting a Node Below a (One-half) Leaf

Es ist leicht einen neuen Knoten an ein Blatt oder ein Halbblatt anzuhängen. Hierzu müssen wir lediglich die left- oder right-Referenz des Parent-Knotens P, an den wir den neuen Knoten N anhängen wollen, auf den neuen Knoten setzen. Wenn wir auch mit parent-Referenzen arbeiten, müssen wir diese im neuen Knoten N auf den Parent-Knoten P setzen.

It is easy to append a new node to a leaf or half leaf. To do this, we just need to set the left or right reference of the parent node P, to which nosotros want to append the new node N, to the new node. If we are working with a parent reference, we demand to fix the new node's parent reference to P.

Binary tree: inserting a new node below a leaf
Inserting a new node below a leafage
Binary tree: inserting a new node below a half leaf
Inserting a new node below a half leaf

Case B: Inserting a Node Betwixt Inner Node and Its Child

Merely how do y'all become most inserting a node between an inner node and ane of its children?

Binary tree: inserting a new node below an inner node
Inserting a new node below an inner node

This is but possible past reorganizing the tree. How exactly the tree is reorganized depends on the concrete blazon of binary tree.

In this tutorial, nosotros implement a very simple binary tree and proceed as follows for the reorganization:

  • If the new node N is to be inserted as a left child below the inner node P, then P's current left subtree Fifty is ready as a left child beneath the new node Due north. Accordingly, the parent of L is prepare to N, and the parent of N is set to P.
  • If the new node N is to be inserted as a correct child beneath the inner node P, so P's current right subtree R is set as a correct child beneath the new node Due north. Appropriately, the parent of R is set to North, and the parent of N is set to P.

The following diagram shows the 2d case: Nosotros insert the new node N betwixt P and R:

Binary tree: inserting a new node between an inner node and its child
Inserting a new node between an inner node and its kid

This is – as mentioned – a very simple implementation. In the case higher up, this results in a highly unbalanced binary tree.

Specific binary trees take a unlike approach here to maintain a tree structure that satisfies the item properties of the binary tree in question (sorting, balancing, etc.).

Inserting a Binary Tree Node – Java Source Code

Here you can encounter the code for inserting a new node with the given data below the given parent node to the specified side (left or correct) using the reorganization strategy defined in the previous section (class SimpleBinaryTree starting at line 18).

(The switch expression with the curly braces was introduced in Java 12/thirteen.)

              

public Node insertNode (int data, Node parent, Side side) { var node = new Node(data); node.parent = parent; switch (side) { instance LEFT -> { if (parent.left != cipher) { node.left = parent.left; node.left.parent = node; } parent.left = node; } case Correct -> { if (parent.right != zip) { node.right = parent.correct; node.right.parent = node; } parent.right = node; } default -> throw new IllegalStateException(); } return node; }

Code linguistic communication: Java ( java )

In the createSampleTree() method of the Example1 class, you tin meet how to create the sample binary tree from the beginning of this article.

Deletion of a Node

Also, when deleting a node, we have to distinguish different cases.

Case A: Deleting a Node Without Children (Foliage)

If the node Northward to exist deleted is a leaf, i.e., has no children itself, and so the node is simply removed. To do this, we check whether the node is the left or correct child of the parent P and set P's left or correct reference to null appropriately.

Binary tree: deleting a leaf node
Deleting a leaf node from a binary tree

Case B: Deleting a Node With I Child (Half Leafage)

If the node Northward to exist deleted has a kid C itself, and so the kid moves upwards to the deleted position. Once again, we check whether node N is the left or right kid of its parent P. Then, accordingly, nosotros set the left or right reference of P to N's child C (the previous grandchild) – and C'due south parent reference to Northward's parent P (the previous grandparent node).

Binary tree: deleting a half leaf
Deleting a half leaf from a binary tree

Instance C: Deleting a Node With Ii Children

How to go along if you want to delete a node with two children?

How to delete an inner node from a binary tree?
How to delete an inner node from a binary tree?

This requires a reorganization of the binary tree. Analogous to insertion, at that place are again unlike strategies for deletion – depending on the concrete type of binary tree. In a heap, for example, the final node of the tree is placed at the position of the deleted node and and so the heap is repaired.

We utilise the following easy-to-implement variant for our tutorial:

  1. We supplant the deleted node N with its left subtree Fifty.
  2. We append the correct subtree R to the rightmost node of the left subtree.
Binary tree: deleting a node with two children
Deleting a node with ii children from a binary tree

We tin see conspicuously how this strategy leads to a severely unbalanced binary tree. Specific implementations like the binary search tree and the binary heap, therefore, have more circuitous strategies.

Deleting a Tree Node – Java Source Code

The following method (class SimpleBinaryTree starting at line 71) removes the passed node from the tree. Corresponding comments mark cases A, B, and C.

              

public void deleteNode (Node node) { if (node.parent == zilch && node != root) { throw new IllegalStateException("Node has no parent and is not root"); } // Case A: Node has no children --> set node to nada in parent if (node.left == null && node.right == nada) { setParentsChild(node, aught); } // Example B: Node has ane child --> replace node by node's child in parent // Case B1: Node has only left kid else if (node.right == null) { setParentsChild(node, node.left); } // Example B2: Node has only correct kid else if (node.left == cipher) { setParentsChild(node, node.right); } // Case C: Node has two children else { removeNodeWithTwoChildren(node); } // Remove all references from the deleted node node.parent = naught; node.left = null; node.right = goose egg; }

Code linguistic communication: Java ( coffee )

The setParentsChild() method checks whether the node to be deleted is the left or right child of its parent node and replaces the corresponding reference in the parent node with the child node. kid is null if the node to exist deleted has no children, and accordingly, the kid reference in the parent node is set to null.

In case the deleted node is the root node, we but supplant the root reference.

              

private void setParentsChild (Node node, Node child) { // Node is root? Has no parent, set root reference instead if (node == root) { root = kid; if (kid != cipher) { child.parent = naught; } render; } // Am I the left or correct child of my parent? if (node.parent.left == node) { node.parent.left = child; } else if (node.parent.right == node) { node.parent.correct = child; } else { throw new IllegalStateException( "Node " + node.data + " is neither a left nor a right child of its parent " + node.parent.data); } if (child != goose egg) { child.parent = node.parent; } }

Lawmaking linguistic communication: Java ( coffee )

In case C (deleting a node with two children), the tree is reorganized as described in the previous section. This is done in the separate method removeNodeWithTwoChildren():

              

private void removeNodeWithTwoChildren (Node node) { Node leftTree = node.left; Node rightTree = node.right; setParentsChild(node, leftTree); // find correct-most child of left tree Node rightMostChildOfLeftTree = leftTree; while (rightMostChildOfLeftTree.right != null) { rightMostChildOfLeftTree = rightMostChildOfLeftTree.right; } // append correct tree to right kid rightMostChildOfLeftTree.correct = rightTree; rightTree.parent = rightMostChildOfLeftTree; }

Code language: Coffee ( coffee )

In the deleteSomeNodes() method of the Example1 class, you can see how some nodes of the example tree are deleted again.

Array Representation of a Binary Tree

Finally, I want to evidence you an culling representation of the binary tree: storing information technology in an array.

The array contains every bit many elements as a perfect binary tree of the superlative of the binary tree to be stored, i.e., iih+1-1 elements for height h (in the following image: seven elements for summit 2).

The nodes of the tree are sequentially numbered from the root downward, level past level, from left to right, and mapped to the array, equally shown in the following illustration:

Array representation of a binary tree
Array representation of a binary tree

For a complete binary tree, we can trim the array appropriately – or store the number of nodes as an boosted value.

Advantages and Disadvantages of the Array Representation

Storing a binary tree every bit an array has the post-obit advantages:

  • Storage is more meaty, as references to children (and parents, if applicative) are not required.
  • Still, y'all quickly get from parents to children and vice versa:
    For a node at index i,
    • the left kid is at index 2i+1,
    • the right child is at index 2i+2,
    • the parent node is at index i/2, rounded down.
  • Yous can perform a level-lodge traversal by but iterating over the array.

Against these, i must weigh the following disadvantages:

  • If the binary tree is not complete, memory is wasted by unused assortment fields.
  • If the tree grows beyond the array size, the data must be copied to a new, larger array.
  • As the tree shrinks, the data should be copied (with some margin) to a new, smaller array to free up unused space.

Summary

In this commodity, you lot learned what a binary tree is, what types of binary trees exist, what operations you can utilize to binary trees, and how to implement a binary tree in Java.

If you liked the article, feel free to share information technology using one of the share buttons below. Do you lot want to be informed when the next article is published? Then click here to sign upwards for the HappyCoders newsletter.

constantinothinscion1974.blogspot.com

Source: https://www.happycoders.eu/algorithms/binary-tree-java/

0 Response to "How to Recursively Read Elements of a Binary Tree Java Into Array"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel