8

I am trying to implement a Binary Tree, NOT Binary Search Tree, in C#. I implemented the below code which is working fine but is not what I am looking for. Basically I am trying to implement a Complete Binary Tree, but with my below code, I am getting an unbalanced binary tree.

Input : 10, 20, 30, 40, 50, 60, 70, 80, 90, 100
Desired Output : 

                        10
                     /       \
                 20            30
               /    \         /  \
            40        50    60    70
           /  \      /
         80    90  100     


Current Output : 
                                10
                              /    \
                            20      30
                                  /    \
                                40      50    
                                       /   \
                                     60     70
                                           /  \
                                         80    90  
                                              /
                                            100   

Here is my code :

  class Node 
  {
    public int data;
    public Node left;
    public Node right;

    public Node() 
    {
      data = 0;
      left = null;
      right = null;
    }
  }

  class Tree 
  {
    private Node root;

    public Tree() 
    {
      root = null;
    }

    public void AddNode(int data)
    {
      root = AddNode(root, data);
    }

    public Node AddNode(Node node, int data) 
    {
      if (node == null)
      {
        node = new Node();
        node.data = data;
      }
      else
      {
        if (node.left == null)
        {
          node.left = AddNode(node.left, data);
        }
        else
        {
          node.right = AddNode(node.right, data);
        }
      }
      return node;
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      int[] nodeData = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
      Tree tree1 = new Tree();
      foreach (int i in nodeData)
      {
        tree1.AddNode(i);
      }
      Console.ReadKey();
    }
  }

I know the issue is in my AddNode(Node node, int data) {...} function's else block, but I am not able to figure out the resolution.

I tried to look for solution online but most of the places its Binary Search Tree implementation. One of the solution i liked is here but the solution is passing the input array as arguments for recursive calling, which I don't know will be efficient or not in case of a very large tree. There were several other posts but none of them is resolving my problem.

Although I am implementing it in C#, but more specifically I am looking the logic to fix my AddNode(...) function so I am fine with the algorithm if not the code implementation.

Any help?

2
  • 1
    Does it need to be using nodes? Can it be done using an array instead? Commented Jan 13, 2018 at 3:12
  • Do you want the tree to sort the input data or just add it? Commented Jan 18, 2018 at 10:50

8 Answers 8

9
+50

Trees are by definition recursive data structures.

class Node<T>
{
    public Node(T data)
    {
        Data = data;
    }
    public T Data { get; }
    public Node<T> Left { get; set; }
    public Node<T> Right { get; set; }
}

As such, constructing them using recursion is far more intuitive.

Input: 10, 20, 30, 40, 50, 60, 70, 80, 90, 100

Desired output --complete binary tree:

               10
           /        \
         20          30
      /     \     /      \
    40       50  60      70
  /   \     /    
80     90  100

Matching index of input:

               0
           /       \
          1         2
      /     \     /     \
    3        4   5       6
  /   \     /    
 7     8   9

A pattern emerges, for a node with index i:

  • the left child has index 2*i + 1
  • the right child has index 2*i + 2

With the base case for recursion,

i >= input.length

all we need to do is call the recursive method on the root.

class TreeBuilder<T>
{
    public Node<T> Root { get; }

    public TreeBuilder(params T[] data)
    {
        Root = buildTree(data, 0);
    }

    private Node<T> buildTree(T[] data, int i)
    {
        if (i >= data.Length) return null;
        Node<T> next = new Node<T>(data[i]);
        next.Left = buildTree(data, 2 * i + 1);
        next.Right = buildTree(data, 2 * i + 2);
        return next;
    }
}

Usage:

int[] data = { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
TreeBuilder<int> builder = new TreeBuilder<int>(data);
Node<int> tree = builder.Root;

Switching API, a couple of ways to add nodes one by one:

  • traversing the tree from the root down (absolute)
  • travelling from the previously added node (relative)
  • maintaining an ordered collection of nodes that don't have two child nodes

As the second one involves longer travel distances (2 * height of the tree) and the third one has already been implemented (queue for bookkeeping), let's have a look at the first.

This time, visualizing the node count at a given position:

               1
           /        \
         2           3
      /     \     /     \
    4        5   6       7 
  /   \     /    
8      9   10 

Mapping to binary representation:

               1
           /        \
         10          11
      /     \     /     \
   100     101  110     111 
  /   \     /    
1000  1001 1010 

If we ignore the leftmost bit, again, a pattern emerges. We can use the bits as a road map, or in this case node map.

class TreeBuilder<T>
{
    private int level;
    private int nodeCount;
    public Node<T> Root { get; }

    public TreeBuilder(T data)
    {
        Root = new Node<T>(data);
        nodeCount = 1;
        level = 0;
    }

    public void addNode(T data)
    {
        nodeCount++;
        Node<T> current = Root;
        if (nodeCount >= Math.Pow(2, level + 1)) level++;
        for (int n=level - 1; n>0; n--)
            current = checkBit(nodeCount, n) ? current.Left : current.Right;
        if (checkBit(nodeCount, 0))
            current.Left = new Node<T>(data);
        else
            current.Right = new Node<T>(data);
    }

    private bool checkBit(int num, int position)
    {
        return ((num >> position) & 1) == 0;
    }
}

Usage:

int[] data = { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
TreeBuilder<int> builder = new TreeBuilder<int>(data[0]);
for (int i=1; i<data.Length; i++)
{
    builder.addNode(data[i]);
}
Node<int> tree = builder.Root;
Sign up to request clarification or add additional context in comments.

1 Comment

The question explicitly questions the efficiency of passing the input array as [an argument in recursive calls].
3

You need to populate the tree level by level. A level n has 2^n nodes, that is there are 2^n paths from the root. Each path can be encoded as an n-bit number (0 means take a left branch, 1 means take a right). That is, to populate nth level,

    for path from 0 to 2^n - 1
        value = get_next_value()
        node = root
        for level = 0 to n - 1
            if path & 0x1 == 0
                node = node->left
            else
                node = node->right
            ++level
            path >>= 1
        if path == 0
            node->left = new node(value)
        else
            node->right = new node(value)

3 Comments

This probably works, but doesn't exactly look C#. If you insist on the tree being filled left to right, process the bits most (but one) to least significant.
@greybeard Why? I don't see the difference. There is no requirement of which order the level shall be populated. And yes, it is not C#, not C, not Python. It is pseudocode.
(Why left to right? The sketch from the question looks the type, and elsewhere I've seen complete binary defined top down, left to right - which landed the suggestion to process the bits in opposite order in a conditional sentence.)
2

This algorithm can solve your problem in pretty simple and efficient way.

Consider this Node class:

public class Node<T>
{
     public Node(T data) 
     {
         Data = data;
     }

    public T Data { get; }

    public Node<T>  Left { get; set;}

    public Node<T>  Right { get; set;}
}

This class will help you to compose your tree:

public class TreeBuilder<T>
{
    private readonly Queue<Node<T>> _previousNodes;

    public Node<T> Root { get; }

    public TreeBuilder(T rootData)
    {
        Root = new Node<T>(rootData)
        _previousNodes = new Queue<Node<T>>();
        _previousNodes.Enqueue(Root);
    }

    public void AddNode(T data)
    {
        var newNode = new Node<T>(data);
        var nodeToAddChildTo = _previousNodes.Peek();
        if(nodeToAddChildTo.Left == null)
        {
           nodeToAddChildTo.Left = node; 
        }
        else
        {
            nodeToAddChildTo.Right = node;
            _previousNodes.Dequeue();
        }      
        _previousNodes.Enqueue(newNode);
    } 
}

The logic behind AddNode method is based on FIFO methodology so we will use Queue<T> in the implementation.

We will start with the first node and attach to it a left child first (and add it to the queue afterwards) and then we will attach to it a right one (and also add it to the queue afterwards) and only after we will attach both we will remove it from the queue and start to attach children to it's left child (which is the next in the queue) and when we will finish with it we will start to attach children to it's right child (which will be the next in the queue), we will do this operation constantly from left to right from the top to the bottom until the tree will be composed.

Now you can use it from your main method like this:

public class Program
{
    public static void Main(string[] args)
    {
        int[] values = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
        var treeBuilder = new TreeBuilder<int>(values[0]);
        foreach (int value in values.Skip(1))
        {
            treeBuilder.AddNode(value);
        }
        //here you can use treeBuilder Root property as an entry point to the tree
    }
}

1 Comment

(Given there have been two valid answers on 2016/10/21, what attention do you deem enough? user58697's binary little endian approach builds a tree with minimum possible difference in number of left and right children using just the total number of nodes as additional information, this one fills levels left to right using a queue holding about half the nodes. "Binary big-endian" fills from left to right too, "The Queue" can be modified to always Dequeue(), and Enqueue() the old node unless "full", in that case all descendants.)
1

Here is another way to get your answer without tracking left, right and/or parent. In fact the Node class becomes real simple, the Tree class does the work when you call tree1.Root ... below I used the constructor to add the values, but I have included the AddNodes method, where you can add a new node as many values as you would like to add with a single call.

  class Node<T>
  {
    public T Data { get; set; }
    public Node<T> Left { get; set; }
    public Node<T> Right { get; set; }

    public override string ToString()
    {
      return Data.ToString();
    }
  }


  class Tree<T>
  {
    private readonly List<T> list;

    private static readonly Func<List<T>, int, Node<T>> LeftFunc = (l, i) =>
    {
      var lix = Convert.ToInt32(Convert.ToString(i, 2) + "0", 2) - 1;
      return l.Count > lix ? new Node<T> {Data = l[lix], Left = LeftFunc(l, lix + 1), Right = RightFunc(l, lix + 1) } : null;
    };

    private static readonly Func<List<T>, int, Node<T>> RightFunc = (l, i) =>
    {
      var rix = Convert.ToInt32(Convert.ToString(i, 2) + "1", 2) - 1;
      return l.Count > rix ? new Node<T> { Data = l[rix], Left = LeftFunc(l, rix + 1), Right = RightFunc(l, rix + 1) } : null;
    };

    public Node<T> Root => list.Any() ? new Node<T>{Data=list.First(), Left = LeftFunc(list,1), Right= RightFunc(list,1)} : null;

    public Tree(params T[] data)
    {
      list = new List<T>(data);
    }

    public int Count => list.Count;

    public void AddNodes(params T[] data)
    {
      list.AddRange(data);
    }


    public double Levels => Math.Floor(Math.Log(list.Count,2))+1;

    public override string ToString()
    {
      return  (list?.Count ?? 0).ToString();
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      var nodeData = new [] { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
      var tree1 = new Tree<int>(nodeData);



      Console.ReadKey();
    }

For fun I created an extension that will write the tree to the console, to help visualize the tree. In order to use this you will need to ensure that the Node and Tree classes are public and add a Values property to the Tree class as well. So your Tree class would look like this:

  public class Tree<T>
  {
    private readonly List<T> list;

    private static readonly Func<List<T>, int, Node<T>> LeftFunc = (l, i) =>
    {
      var lix = Convert.ToInt32(Convert.ToString(i, 2) + "0", 2) - 1;
      return l.Count > lix ? new Node<T> {Data = l[lix], Left = LeftFunc(l, lix + 1), Right = RightFunc(l, lix + 1) } : null;
    };

    private static readonly Func<List<T>, int, Node<T>> RightFunc = (l, i) =>
    {
      var rix = Convert.ToInt32(Convert.ToString(i, 2) + "1", 2) - 1;
      return l.Count > rix ? new Node<T> { Data = l[rix], Left = LeftFunc(l, rix + 1), Right = RightFunc(l, rix + 1) } : null;
    };

    public Node<T> Root => list.Any() ? new Node<T>{Data=list.First(), Left = LeftFunc(list,1), Right= RightFunc(list,1)} : null;

    public Tree(params T[] data)
    {
      list = new List<T>(data);
    }

    public int Count => list.Count;

    public void AddNodes(params T[] data)
    {
      list.AddRange(data);
    }

    public IEnumerable<T> Values => list.ToArray();

    public double Levels => Math.Floor(Math.Log(list.Count,2))+1;

    public override string ToString()
    {
      return  (list?.Count ?? 0).ToString();
    }
  }

And here is the extensions class:

  public static class TreeAndNodeExtensions
  {
    public static void Write<T>(this Tree<T> tree)
    {
      var baseMaxNodes = Convert.ToInt32(Math.Pow(2, tree.Levels - 1));

      // determine the required node width based on the the last two levels and their value lengths...
      var nodeWidth = Math.Max(tree.Values.Skip(Convert.ToInt32(Math.Pow(2, tree.Levels - 2) - 1)).Max(n => n.ToString().Length), tree.Values.Skip(Convert.ToInt32(Math.Pow(2, tree.Levels - 1) - 1)).Max(n => n.ToString().Length) + 1) + 1;

      var baseWidth = baseMaxNodes * nodeWidth;
      Console.CursorLeft = baseWidth/2;
      tree.Root.Write(baseWidth);
    }

    private static void Write<T>(this Node<T> node, int nodeWidth, int level=0)
    {
      var cl = Console.CursorLeft;
      var ct = Console.CursorTop;

      if (Console.CursorLeft >= Convert.ToInt32(Math.Ceiling(node.Data.ToString().Length / 2.0)))
      {
        Console.CursorLeft -= Convert.ToInt32(Math.Ceiling(node.Data.ToString().Length / 2.0));
      }
      Console.Write(node.Data);
      if (node.Left != null)
      {
        var numCenter = cl - nodeWidth/4;
        Console.CursorLeft = numCenter;
        Console.CursorTop = ct + 2;
        Console.Write('/');
        Console.CursorTop = ct + 1;
        Console.Write(new string('_',cl-Console.CursorLeft));
        Console.CursorLeft = numCenter;
        Console.CursorTop = ct+3;
        node.Left.Write(nodeWidth/2, level+1);
      }

      if (node.Right != null)
      {
        var numCenter = cl + nodeWidth/4;
        Console.CursorLeft = cl;
        Console.CursorTop = ct + 1;
        Console.Write(new string('_', numCenter-cl-1));
        Console.CursorTop = ct + 2;
        Console.Write('\\');
        Console.CursorLeft = numCenter;
        Console.CursorTop = ct+3;
        node.Right.Write(nodeWidth/2,level + 1);
      }

      Console.SetCursorPosition(cl,ct);
    }
  }

Then you can update your program to use this extension:

  class Program
  {
    static void Main(string[] args)
    {
      var nodeData = new [] { 10, 20, 30, 40, 50, 60, 70, 80,90,100 };
      var tree1 = new Tree<int>(nodeData);

      tree1.Write();

      Console.ReadKey();
    }
  }

And you should see this:

                   10
           __________________
          /                  \
         20                  30
      ________            ________
     /        \          /        \
    40        50        60        70
    __        _
   /  \      /
  80  90   100

Comments

0

Each node will need to know it's parent, root node being the exception as it will have a null parent. Then each node will need to know which path it passed a value down last. Then when a node is asked to add a child it will go in sequence of: left, right, parent, left, right, parent, ... (root node being the exception as it will skip parent and will just alternate left, right, left, right ...)

I quickly adjusted your code to make it work as you intended, this could be much cleaner with a little time.

 class Node
  {
    private readonly Node parent;
    private Direction lastPath;

    public int Data { get; set; }
    public Node Left { get; set; }
    public Node Right { get; set; }

    public Node(int data, Node parent = null)
    {
      Data = data;
      this.parent = parent;
    }

    public Node AddChild(int data)
    {
      if (Left == null)
      {
        lastPath = Direction.Left;
        Left = new Node(data, this);
        return this;
      }
      if (Right == null)
      {
        lastPath = Direction.Right;
        Right = new Node(data, this);
        return parent ?? this;
      }

      if (lastPath == Direction.Parent || parent==null && lastPath == Direction.Right)
      {
        lastPath = Direction.Left;
        return Left.AddChild(data);
      }

      if (lastPath == Direction.Left)
      {
        lastPath = Direction.Right;
        return Right.AddChild(data);
      }

      lastPath = Direction.Parent;
      return parent?.AddChild(data);
    }
  }

  enum Direction
  {
    Left,
    Right,
    Parent
  }

  class Tree
  {
    public Node Root { get; private set; }
    private Node current;

    public void AddNode(int data)
    {
      if (current == null)
      {
        Root = new Node(data);
        current = Root;
      }
      else
      {
        current = current.AddChild(data);
      }
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      var nodeData = new [] { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
      var tree1 = new Tree();

      foreach (var data in nodeData)
      {
        tree1.AddNode(data);
      }
      Console.ReadKey();
    }
  }

3 Comments

Each node will need to know it's parent Whence that need? What definition of Binary Tree are you using? I concede that the question insinuates said sad claim presenting Node AddNode(Node node, int data) public.
When a node is asked to add a child, it can set it in it's left or right, but once those two slots are filled it must hand off to either it's left, right or parent. This allows the tree to traverse across each level horizontally. It was either that or a complete rewrite of the tree/node structure. I tried to keep along the logic of the initial post. I have already solved a better way to construct the tree based on generating it off of the list. I will publish it as a separate answer.
I didn't take public Node AddNode(Node node, int data) a signature that can be meaningfully implemented; starting with not being an instance method of Node, over don't see how with the presented members of Node and if the new Node doesn't end up in node's subtree, what does it have to do with node to don't see how without #children (or equivalent) in each node : try adding -1 and 0 to your tree immediately after instantiation, get its Root.Left and have the loop add data to that.
0

To answer your question:

Passing an array is done by passing the function a reference to the array. So in terms of performance, it is exactly the same as passing an object of any type (https://msdn.microsoft.com/en-us/library/bb985948.aspx).

Personnaly speaking, I'm not a big fan of recursive functions that always pass the same object, neither of constructors calling recursive functions which will call constructors.

Without arrays, here is a solution:

One interesting thing to notice is that in such trees, the node address can be used to find a node if you start addressing from 1:

                0001
        0010            0011
    0100    0101    0110   0111
1000

So if you keep track of the node count in your tree, you know that the next item to add will be at address 1001.

To do that properly, we can find the parent node (which is 1001 shifted right once : 100) then decide if we add left or right (depending on 1001 modulo 2 = 1 : must add to the right).

So here is the code that could do it:

Node class

//Having a generic here allows you to create a tree of any data type
class Node<T> 
{
    public T Data { get; set; }
    public Node<T> Left { get; set; }
    public Node<T> Right { get; set; }
    public Node(T Data)
    {
        this.Data = Data;
    }
}

Tree class

class Tree<T>
{
    Node<T> root = null;
    int nodeCount = 0;

    public void AddNode(T Data)
    {
        AddNode(new Node<T>(Data));
    }

    public void AddNode(Node<T> Node)
    {
        nodeCount++;
        if (root == null)
            root = Node;
        else
        {
            //First we find the parent node
            Node<T> parent = FindNodeWithAddress(nodeCount >> 1);
            //Then we add left or right
            if (nodeCount % 2 == 0)
                parent.Left = Node;
            else
                parent.Right = Node;
        }
    }

    private Node<T> FindNodeWithAddress(int address)
    {
        if (address == 1)
            return root;
        //To find the proper address we use the same trick
        //We first find our parent's address
        Node<T> parent = FindNodeWithAddress(address >> 1);
        //Then we go left or right
        return (address % 2 == 0 ? parent.Left : parent.Right);
    }
}

2 Comments

Taking into account that FindNodeWithAddress() starts handling Node<T>s from root, how does this differ from user58697's 2016 answer?
@greybeard First of all because I answer OP's question. Then, although the algorithms are the same in their concept, I present it in an OOP, recursive and C# way.
0

Have you tried implementing it using an array?

You would use an array, and an int in which to save the last position used for any position pos the left "child node" would be in position 2*pos the right "child node" would be in position 2*pos+1 and a "father node" would be in position pos/2

(don't consider this code to be syntactically correct, it is just an example)

template<class T>
class CompleteBinTree<T>{
    private int[] arr;
    private int arrSize;
    private int pos;

    public CompleteBinTree(){
        arrSize = 100;
        arr = new int[arrSize]//you can always change this number
        int pos = 1; //you can use 0 instead of 1, but this way it's easier to understand
    }

    public AddNode(T t){
        if(pos + 1 == arrSize){
            int[] aux = int[arrSize];
            for(int i = 0; i < arrSize; i++)
                aux[i] = arr[i];
            arr = aux[i];
            arrSize = arrSize * 2;
        }
            arr[pos] = t;
            pos++;
    }

    public IndexOfLeftSon(int x){
        return 2*x;
    }

    public IndexOfRightSon(int x){
        return 2*x + 1;
    }

    public DeleteNode(int x){
        for(int i = x; i < pos; i++)
            arr[i] = arr[i+1];
    }
}

Comments

0

Given that you really want to build a tree of allocated nodes, not an array as others have suggested, there is a very simple algorithm: Use a queue of locations (left or right child of given node or else the root) to expand the tree in top-down levels. Add new locations at the tail and remove from the head to add each successive node. No recursion.

Sorry I don't have access to a C# environment at the moment, so I'll have show this in Java. Translation should be pretty straightforward.

import java.util.ArrayDeque;
import java.util.Deque;

public class CompleteBinaryTree {
  final Deque<Location> queue = new ArrayDeque<>();

  /** Build a tree top-down in levels left-to-right with given node values. */
  Node build(int [] vals) {
    Node root = null;   
    queue.clear();
    queue.add(new Location(root, Location.Kind.ROOT));
    for (int val : vals) {
      Location next = queue.pollFirst();
      switch (next.kind) {
      case ROOT: root = addNode(val); break;
      case LEFT: next.node.left = addNode(val); break;
      case RIGHT: next.node.right = addNode(val); break;
      }
    }
    return root;
  } 

  /** Create a new node and queue up locations for its future children. */
  Node addNode(int val) {
    Node node = new Node(val);
    queue.addLast(new Location(node, Location.Kind.LEFT));
    queue.addLast(new Location(node, Location.Kind.RIGHT));
    return node;
  }

  static class Node {
    final int val;
    Node left, right;
    Node(int val) {
      this.val = val;
    }
    void print(int level) {
      System.out.format("%" + level + "s%d\n", "", val);
      if (left != null) left.print(level + 1);
      if (right != null) right.print(level + 1);
    }
  }

  static class Location {
    enum Kind { ROOT, LEFT, RIGHT }
    final Node node;
    final Kind kind;
    Location(Node node, Kind kind) {
      this.node = node;
      this.kind = kind;
    }
  }

  public static void main(String [] args) {
    int [] vals = { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
    new CompleteBinaryTree().build(vals).print(1);
  }
}

When run, this produces, as you'd hope,

10
 20
  40
   80
   90
  50
   100
 30
  60
  70

1 Comment

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.