GithubHelp home page GithubHelp logo

tree-data-structures's Introduction

Tree Data Structures

Lets take a look at one of my favorite implementations of the tree data structure and identify some terminology along the way:

Figure 1: A tree data structure modeling one of Great Houses of Westeros

Nodes

Trees are organized into nodes that hold data along various branches. Nodes are related through parent, child, and sibling relationships.

Roots

Every tree has a root node at the highest level (ie: Rickard Stark). Every node is also the root of a subtree. We can see that Ned Stark is the root node of the subtree containing him, Robb, Sansa, Arya, Bran, and Rickon.

Depth

Depth refers to the level at which a node sits relative to the root. When referring to the entire tree, a root node, like Rickard Stark has a depth of 0, while Jon Snow and Arya Stark have a depth of 2. However, depth is also relative to a subtree. If we were only considering Ned Stark's subtree, Arya would have a depth of 1.

Height

The height of any node is measured through the deepest child node it has. So the height of Benjen Stark is 0, while the height of Rickard Stark is 2.

Designing a General Tree Data Structure

Strategy

To represent a tree, it would be useful to abstract away the "spider web" feel of the actual tree into something more manageable. Imagine that each node has 2 reference pointers firstChild and nextSibling. Then, we can represent the tree in a chained hierarchy structure focussed on node depth. It would look something like this:

Figure 2: Efficiently representing House Stark with our TreeNode class

Implementing a TreeNode

In practice, we would actually need more than just those 2 pointers to work with the tree efficiently. Here is an outline of our TreeNode class:

Instance Variables

  1. data <String> - Stark family member's name (ie "Ned Stark")
  2. firstChild <TreeNode> - A pointer to a child of this, if any
  3. nextSibling <TreeNode> - A pointer to a sibling of this, if any
  4. previousNode <TreeNode> - A pointer to previous Node along the chain (see Figure 2)
  5. myRoot <TreeNode> - A pointer to the root node associated with this

TreeNode class structure

public class TreeNode {
    protected TreeNode firstChild, nextSibling, previousNode;
    protected String data;
    protected TreeNode myRoot;
    
    public TreeNode() { 
      // Constructor Logic...
    }
    
    public E getData() { 
      return data; // Accessor
    }
}

Data that Could be Stored in a Tree

<TreeNode> this this.firstChild this.nextSibling this.previousNode
rickardStark nedStark null null
nedStark robbStark brandonStark rickardStark
brandonStark null benjenStark nedStark
benjenStark null lyannaStark brandonStark
lyannaStark jonSnow null benjenStark
robbStark null sansaStark nedStark
sansaStark null aryaStark robbStark
aryaStark null branStark sansaStark
branStark null rickonStark aryaStark
rickonStark null null branStark
jonSnow null null lyannaStark

Implementing a Tree class

Let's take a mini tour of a barebones Tree class linking together TreeNodes. We'll focus on the find() method for now and assume an object of this class called starkFamilyTree is already setup.

public class Tree {
    private int mSize;
    TreeNode mRoot;

    public Tree() { 
        // Constrcutor Logic...
    }
    
    // Client side find() function
    public TreeNode find(String data) { 
        return find(mRoot, data, 0); // Runs private recursive version of find()
    }
    
    // Recursive logic - overloaded find()
    private TreeNode find(TreeNode root, String data, int level) {
        TreeNode stackResponse;
        
        if (mSize == 0 || root == null) // Base case 1 (nothing to search)
            return null;
        
        if (root.data.equals(data)) // Base case 2 (found!)
            return root;

        if (level > 0) {
            stackResponse = find(root.nextSibling, data, level); // Recursive Call
            if (stackResponse != null)
                return stackResponse; // Base case 3 (forward result it along the recusrive chain!)
        }

        return find(root.firstChild, data, ++level); // Recursive Call
    }
}

Recursion

To traverse a tree, and check out different nodes on the way we usually use recursion. I really like Mattias Petter Johansson's definition of recursion (check out his awesome YouTube channel Fun Fun Function)

Recursion is when a function calls itself until it doesn't. That is seriously all recursion is. It's really simple.

With that in mind, we see that private TreeNode find(TreeNode root, String data, int level) calls itself mutltiple times. Each call, generates a Stack Frame (a snapshot of the call's local variables), and recursion aims to pile and unpile these Stack Frames. Luckily base cases help us break out of this cycle and eventually end up with one answer.

Let's assume we have a Tree object named starkFamilyTree already setup, so we can try to find() aryaStark. Since a girl has many faces lol, we have to be very careful with our recursive process - it can get very complicated. Follow along if you like, this is the whole procedure...

Global - calls Function

This frame runs the following global code statement:

TreeNode result = starkFamilyTree.find("Arya Stark"); // Call into Function
Stack Frame Global
global variable value
result starkFamilyTree.find("Arya Stark") awaiting return from Function

Function - calls Node 0

This frame runs the following code statement from

public TreeNode find(String data)

        return find(mRoot, data, 0); // Recursive Call to Node 0 (rickardStark)
Stack Frame Function
local variable value
mRoot rickardStark
data "Arya Stark"
return value find(rickardStark, "Arya Stark", 0) awaiting return from Node 0 & will eventually return to Global

Node 0 - calls Node 1

Because local variable level == 0, This frame hits the following code block from

private TreeNode find(TreeNode root, String data, int level)

        return find(root.firstChild, data, ++level); // Recursive call to Node 1 (nedStark)
Stack Frame Node 0 (rickardStark)
local variable value
root
data "Arya Stark"
level 0
return value find(nedStark, "Arya Stark", 1) awaiting return from Node 1 (nedStark) & will eventually return to Function

Node 1 - calls Node 2

Because local variable level > 0, This frame hits the following code block from

private TreeNode find(TreeNode root, String data, int level)

        if (level > 0) {
            stackResponse = find(root.nextSibling, data, level); // Recursive call to Node 2 (brandonStark)
            if (stackResponse != null)
                return stackResponse;
        }
Stack Frame Node 1 (nedStark)
local variable value
root
data "Arya Stark"
level 1
stackResponse find(brandonStark, "Arya Stark", 1) awaiting return from Node 2 (brandonStark)
return value N/A will eventually return to Node 0 (rickardStark)

Node 2 - calls Node 3

Because local variable level > 0, This frame hits the following code block from

private TreeNode find(TreeNode root, String data, int level)

        if (level > 0) {
            stackResponse = find(root.nextSibling, data, level); // Recursive call to Node 3 (benjenStark)
            if (stackResponse != null)
                return stackResponse;
        }
Stack Frame Node 2 (brandonStark)
local variable value
root
data "Arya Stark"
level 1
stackResponse find(benjenStark, "Arya Stark", 1) awaiting return from Node 3 (benjenStark)
return value N/A will eventually return to Node 1 (nedStark)

Node 3 - calls Node 4

Because local variable level > 0, This frame hits the following code block from

private TreeNode find(TreeNode root, String data, int level)

        if (level > 0) {
            stackResponse = find(root.nextSibling, data, level); // Recursive call to Node 4 (lyannaStark)
            if (stackResponse != null)
                return stackResponse;
        }
Stack Frame Node 3 (benjenStark)
local variable value
root
data "Arya Stark"
level 1
stackResponse find(lyannaStark, "Arya Stark", 1) awaiting return from Node 4 (lyannaStark)
return value N/A will eventually return to Node 2 (brandonStark)

Node 4 - calls lyannaStark.nextSibling

Because local variable level > 0, This frame hits the following code block from

private TreeNode find(TreeNode root, String data, int level)

        if (level > 0) {
            stackResponse = find(root.nextSibling, data, level); // Recursive call to lyannaStark.nextSibling
            if (stackResponse != null)
                return stackResponse;
        }
Stack Frame Node 4 (lyannaStark)
local variable value
root
data "Arya Stark"
level 1
stackResponse find(null, "Arya Stark", 1) awaiting return from lyannaStark.nextSibling
return value N/A will eventually return to Node 3 (benjenStark)

lyannaStark.nextSibling - returns null to Node 4

Because local variable root == null, this frame hits the following base case from

private TreeNode find(TreeNode root, String data, int level)

        if (mSize == 0 || root == null)
            return null; // Recursive return to Node 4 (lyannaStark)
Stack Frame lyannaStark.nextSibling
local variable value
root null
data "Arya Stark"
level 1
return value null returning to Node 4 (lyannaStark)

Node 4 - calls Node 5

Node 4 now has received stackResponse == null from lyannaStark.nextSibling, so it runs the following code block from

private TreeNode find(TreeNode root, String data, int level)

        return find(root.firstChild, data, ++level); // Recursive call to Node 5 (jonSnow)
Stack Frame Node 4 (lyannaStark)
local variable value
root
data "Arya Stark"
level 1
stackResponse null received from lyannaStark.nextSibling
return value find(jonSnow, "Arya Stark", 2) awaiting response from Node 5 (jonSnow) & will eventually return to Node 3 (benjenStark)

Node 5 - calls jonSnow.nextSibling

Because local variable level > 0, This frame hits the following code block from

private TreeNode find(TreeNode root, String data, int level)

        if (level > 0) {
            stackResponse = find(root.nextSibling, data, level); // Recursive call to jonSnow.nextSibling
            if (stackResponse != null)
                return stackResponse;
        }
Stack Frame Node 5 (jonSnow)
local variable value
root
data "Arya Stark"
level 2
stackResonse find(null, "Arya Stark", 2) awaiting return from jonSnow.nextSibling
return value N/A will eventually return to Node 4 (lyannaStark)

jonSnow.nextSibling - returns null to Node 5

Because local variable root == null, this frame hits the following base case from

private TreeNode find(TreeNode root, String data, int level)

        if (mSize == 0 || root == null)
            return null; // Recursive return to Node 5 (jonSnow)
Stack Frame jonSnow.nextSibling
local variable value
root null
data "Arya Stark"
level 2
return value null returning to Node 5 (jonSnow)

Node 5 - calls jonSnow.firstChild

Frame 8 now has received stackResponse == null from jonSnow.nextSibling, so it runs the following code block from

private TreeNode find(TreeNode root, String data, int level)

        return find(root.firstChild, data, ++level); // Recursive call to jonSnow.firstChild
Stack Frame Node 5 (jonSnow)
local variable value
root
data "Arya Stark"
level 2
stackResonse null received from jonSnow.nextSibling
return value find(null, "Arya Stark", 3) awating response from jonSnow.firstChild & will eventually return to Node 4 (lyannaStark)

jonSnow.firstChild - returns null to Node 5

Because local variable root == null, this frame hits the following base case from

private TreeNode find(TreeNode root, String data, int level)

        if (mSize == 0 || root == null)
            return null; // Recursive return to Node 5 (jonSnow)
Stack Frame jonSnow.firstChild
local variable value
root null
data "Arya Stark"
level 3
return value null returning to Node 5 (jonSnow)

Node 5 - returns null to Node 4

Node 5 now has received null from jonSnow.firstChild, so it runs the following code block from

private TreeNode find(TreeNode root, String data, int level)

        return find(root.firstChild, data, ++level); // Recursive return to Node 4 (lyannaStark)
Stack Frame Node 5 (jonSnow)
local variable value
root
data "Arya Stark"
level 2
stackResonse null received from jonSnow.nextSibling
return value null received from jonSnow.firstChild & returning to Node 4 (lyannaStark)

Node 4 - returns null to Node 3

Node 4 now has received null from 8, so it finishes the following code block from

private TreeNode find(TreeNode root, String data, int level)

        return find(root.firstChild, data, ++level); // Recursive return to Node 3 (benjenStark)
Stack Frame Node 4 (lyannaStark)
local variable value
root
data "Arya Stark"
level 1
stackResponse null received from lyannaStark.nextSibling
return value null received from Node 5 (jonSnow) & returning to Node 3 (benjenStark)

Node 3 - calls benjenStark.firstChild

Node 3 now has received stackResponse == null from Node 4, so it runs the following code block from

private TreeNode find(TreeNode root, String data, int level)

        return find(root.firstChild, data, ++level); // Recursive call to benjenStark.firstChild
Stack Frame Node 3 (benjenStark)
local variable value
root
data "Arya Stark"
level 1
stackResponse null received from Node 4 (lyannaStark)
return value find(null, "Arya Stark", 2) awating response from benjenStark.firstChild & will eventually return to Node 2 (brandonStark)

benjenStark.firstChild returns null to Node 3

Because local variable root == null, this frame hits the following base case from

private TreeNode find(TreeNode root, String data, int level)

        if (mSize == 0 || root == null)
            return null; // Recursive return to Node 3 (benjenStark)
Stack Frame benjenStark.firstChild
local variable value
root null
data "Arya Stark"
level 2
return value null returning value to Node 3 (benjenStark)

Node 3 - returns value to Node 2

Node 3 now has received null from benjenStark.firstChild, so it finishes the following code block from

private TreeNode find(TreeNode root, String data, int level)

        return find(root.firstChild, data, ++level); // Recursive return to Node 2 (brandonStark)
Stack Frame Node 3 (benjenStark)
local variable value
root
data "Arya Stark"
level 1
stackResponse null received from Node 4 (lyannaStark)
return value null received from benjenStark.firstChild & returning to Node 2 (brandonStark)

Node 2 - calls brandonStark.firstChild

Frame 4 now has received stackResponse == null from 5, so it runs the following code block from

private TreeNode find(TreeNode root, String data, int level)

        return find(root.firstChild, data, ++level); // Recursive call to brandonStark.firstChild
Stack Frame Node 2 (brandonStark)
local variable value
root
data "Arya Stark"
level 1
stackResponse null received from Node 3 (benjenStark)
return value find(null, "Arya Stark, 2) awating response from brandonStark.firstChild & will eventually return to Node 1 (nedStark)

brandonStark.firstChild - returns null to Node 2

Because local variable root == null, this frame hits the following base case from

private TreeNode find(TreeNode root, String data, int level)

        if (mSize == 0 || root == null)
            return null; // Recursive return to Node 2 (brandonStark)
Stack Frame brandonStark.firstChild
local variable value
root null
data "Arya Stark"
level 2
return value null returning value to Node 2 (brandonStark)

Node 2 - returns null to Node 1

Node 2 now has received null from brandonStark.firstChild, so it finishes the following code block from

private TreeNode find(TreeNode root, String data, int level)

        return find(root.firstChild, data, ++level); // Recursive return to Node 1 (nedStark)
Stack Frame Node 2 (brandonStark)
local variable value
root
data "Arya Stark"
level 1
stackResponse null received from Node 3 (benjenStark)
return value null received from brandonStark.firstChild & returning to Node 1 (nedStark)

Node 1 - calls Node 6

Node 1 now has received stackResponse == null from Node 2, so it runs the following code block from

private TreeNode find(TreeNode root, String data, int level)

        return find(root.firstChild, data, ++level); // Recursive call to Node 6 (robbStark)
Stack Frame Node 1 (nedStark)
local variable value
root
data "Arya Stark"
level 1
stackResponse null received from Node 2 (brandonStark)
return value find(robbStark, "Arya Stark", 2) awating response from Node 6 (robbStark) & will eventually return to Node 0 (rickardStark)

Node 6 - calls Node 7

Because local variable level > 0, This frame hits the following code block from

private TreeNode find(TreeNode root, String data, int level)

        if (level > 0) {
            stackResponse = find(root.nextSibling, data, level); // Recursive call to Node 7 (sansaStark)
            if (stackResponse != null)
                return stackResponse;
        }
Stack Frame Node 6 (robbStark)
local variable value
root
data "Arya Stark"
level 2
stackResonse find(sansaStark, "Arya Stark", 2) awaiting return from Node 7 (sansaStark)
return value N/A will eventually return to Node 1 (nedStark)

Node 7 - calls Node 8

Because local variable level > 0, This frame hits the following code block from

private TreeNode find(TreeNode root, String data, int level)

        if (level > 0) {
            stackResponse = find(root.nextSibling, data, level); // Recursive call to Node 8 (aryaStark)
            if (stackResponse != null)
                return stackResponse;
        }
Stack Frame Node 7 (sansaStark)
local variable value
root
data "Arya Stark"
level 2
stackResonse find(aryaStark, "Arya Stark", 2) awaiting return from Node 8 (aryaStark)
return value N/A will eventually return to Node 6 (robbStark)

Node 8 - returns aryaStark to Node 7

Because local variable root.data == "Arya Stark", This frame hits the base case and finds Arya!

private TreeNode find(TreeNode root, String data, int level)

        if (root.data.equals(data)) // base case
            return root; // recursive return to Node 7 (sansaStark)
Stack Frame Node 7 (aryaStark)
local variable value
root
data "Arya Stark"
level 2
return value aryaStark returning to Node 7 (sansaStark)

Node 7 - returns aryaStark to Node 6

Node 7 (sansaStark) receives stackReponse == aryaStark from Node 8 (aryaStark), so it finishes the stack by returning stackResponse to Node 6 in this code block from

private TreeNode find(TreeNode root, String data, int level)

        if (level > 0) {
            stackResponse = find(root.nextSibling, data, level);
            if (stackResponse != null)
                return stackResponse; // Recursive return to Node 6 (robbStark)
        }
Stack Frame Node 7 (sansaStark)
local variable value
root
data "Arya Stark"
level 2
stackResonse aryaStark received from Node 8 (aryaStark)
return value aryaStark returning to Node 6 (robbStark)

Node 6 - returns aryaStark to Node 1

Node 6 (robbStark) receives stackReponse == aryaStark from Node 7 (sansaStark), so it finishes the stack by returning stackResponse to Node 1 in this code block from

private TreeNode find(TreeNode root, String data, int level)

        if (level > 0) {
            stackResponse = find(root.nextSibling, data, level);
            if (stackResponse != null)
                return stackResponse; // Recursive return to Node 1 (nedStark)
        }
Stack Frame Node 6 (robbStark)
local variable value
root
data "Arya Stark"
level 2
stackResonse aryaStark received from Node 7 (sansaStark)
return value aryaStark returning to Node 1 (nedStark)

Node 1 - returns aryaStark to Node 0

Node 1(nedStark) now has received aryaStark from Node 6 (robbStark), so it finishes the following code block from

private TreeNode find(TreeNode root, String data, int level)

        return find(root.firstChild, data, ++level); // Recursive return to Node 0 (rickardStark)
Stack Frame Node 1 (nedStark)
local variable value
root
data "Arya Stark"
level 1
stackResponse null received from Node 2 (brandonStark)
return value aryaStark received from Node 6 (robbStark) & returning to Node 0 (rickardStark)

Node 0 - returns aryaStark to Function

Node 0 (rickardStark) receives aryaStark from Node 1 (nedStark) Because local variable level == 0, This frame finishes the following code block from

private TreeNode find(TreeNode root, String data, int level)

        return find(root.firstChild, data, ++level); // Recursive return to Function
Stack Frame Node 0 (rickardStark)
local variable value
root
data "Arya Stark"
level 0
return value aryaStark received from Node 1 (nedStark) & returning to Function

Function - returns aryaStark to Global

This frame receives aryaStark from Node 0 (rickardStark) and finishes the following code block from

public TreeNode find(String data)

        return find(mRoot, data, 0); // Recursive return to Global
Stack Frame Function
local variable value
mRoot rickardStark
data "Arya Stark"
return value aryaStark received from Node 0 & returning to Global

Global - returns aryaStark to result

This frame receives aryaStark from Function and finishes the following global code statement:

TreeNode result = starkFamilyTree.find("Arya Stark"); // end of recusrsion
Stack Frame Global
global variable value
result aryaStark received from Function

tree-data-structures's People

Contributors

grkml avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.