Frickin Trees!

7 replies [Last post]
Posts: 458

Argh,

I need to create a Binary Tree with 8 layers.

These layers then read how many "children" it has on its branches, and based off of these numbers,
goes through tons of formulas.

MY PROBLEM:

How on earth do I cleanly create the tree with exactly 8 Layers?

What I mean by layer:
(example with 3 layers)

       0
     /   \
     0   0
  /  /    \  \
0  0        0  0

and how do I give the Nodes IDs incrementedly?

       1
     /   \
     2   3
  /  /    \  \
4  5        6  7

Im not experienced in Trees at all, as I was mostly able to simple use any form of arrays.

Well, this time I need the "branch" fact way too often...

Posts: 1691

Does this help?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
        }
    }
 
    class Node
    {
        Node _left;
        Node _right;
        Node _parent;
 
        /// <summary>
        /// Gets if this node is a leaf node (has no children).
        /// </summary>
        public bool IsLeaf { get { return Left == null && Right == null; } }
 
        /// <summary>
        /// Gets if this node is a root node (has no parent).
        /// </summary>
        public bool IsRoot { get { return Parent == null; } }
 
        /// <summary>
        /// Gets all of the nodes under this node on both the left and right branches, but not including this node.
        /// </summary>
        public IEnumerable<Node> Nodes
        {
            get
            {
                // Grab the left side
                if (Left != null)
                {
                    // Return the node to the left
                    yield return Left;
 
                    // Create a recursion to perform this same operation on every single node
                    foreach (var leftChildNodes in Left.Nodes)
                        yield return leftChildNodes;
                }
 
                // Do the exact same thing for the right side
                if (Right != null)
                {
                    yield return Right;
 
                    foreach (var rightChildNode in Right.Nodes)
                        yield return rightChildNode;
                }
            }
        }
 
        /// <summary>
        /// Get the depth (level) of this node in the tree, where the root node is the a depth of 1.
        /// </summary>
        public int Depth
        {
            get
            {
                // Initial depth is 1
                int depth = 1;
 
                // Grab the initial node, which is the parent of this node
                var n = Parent;
 
                // Keep looping back through the tree until we hit the root node (parent == null), incrementing the
                // depth count each time
                while (n != null)
                {
                    n = n.Parent;
                    depth++;
                }
 
                return depth;
            }
        }
 
        /// <summary>
        /// Gets the parent node. Will be null if this is the root.
        /// </summary>
        public Node Parent { get { return _parent; } }
 
        /// <summary>
        /// Gets the child node on the left side.
        /// </summary>
        public Node Left
        {
            get { return _left; }
            set
            {
                _left = value;
                if (_left != null)
                    _left._parent = this; 
            }
        }
 
        /// <summary>
        /// Gets the child node on the right side.
        /// </summary>
        public Node Right
        {
            get { return _right; }
            set
            {
                _right = value;
                if (_right != null)
                    _right._parent = this;
            }
        }
    }
 
    class BinaryTree
    {
        readonly Node _root;
 
        /// <summary>
        /// Gets the root node in the tree.
        /// </summary>
        public Node Root { get { return _root; } }
 
        /// <summary>
        /// Gets all of the nodes in the tree.
        /// </summary>
        public IEnumerable<Node> Nodes
        {
            get
            {
                if (Root == null)
                    yield break;
 
                // Return the root node (since it is not included in the call to Node.Nodes)
                yield return Root;
 
                // Return all the child node in the root
                foreach (var child in Root.Nodes)
                    yield return child;
            }
        }
 
        /// <summary>
        /// Initializes a new instance of the <see cref="BinaryTree"/> class.
        /// </summary>
        public BinaryTree()
        {
            _root = new Node();
        }
    }
}

If you want to fill the tree completely to a certain depth, you simply do this:

static void FillTree(Node node, int targetDepth)
        {
            // Hit the needed depth, no need to add more nodes
            if (node.Depth == targetDepth)
                return;
 
            // Add nodes to the left and right side, if needed
            if (node.Left == null)
                node.Left = new Node();
 
            if (node.Right == null)
                node.Right = new Node();
 
            // Recurse on both the sides
            FillTree(node.Left, targetDepth);
            FillTree(node.Right, targetDepth);
        }

As for IDs, it would be best if you avoid it. I can't see why you would want IDs for your tree, since its not like you can look it up by index. There are a few ways you can give unique IDs to your nodes, but an incrementing ID would not be recommended as it would require you to rebuild the IDs every time a node is added (what happens if you have a "gap" in your tree?).

However, if you want just a unique ID for each node, then you can do a reverse-crawling Huffman-encoding style counting. Basically, it works like this:

  1. Assign the value 1 to the node.
  2. If the node has no parent (root), break. As a result, the first node has the value 1.
  3. Bit-shift to the left 1. This gives us a representation of the depth, since the greatest set bit (this 1 we initially set) will always have the bit position of the node's depth.
  4. If the node is on the right-side of the parent, OR on a 1. This tells us how to crawl the tree. 0 means left, 1 means right.
  5. Repeat step 2.

In code:

        public uint ID
        {
            get
            {
                return GetID(1);
            }
        }
 
        uint GetID(uint id)
        {
            if (IsRoot)
                return id;
 
            id <<= 1;
 
            if (Parent.Right == this)
                id |= 1;
 
            return Parent.GetID(id);
        }

To test it:

        static void Main(string[] args)
        {
            BinaryTree t = new BinaryTree();
            FillTree(t.Root, 8);
 
            foreach (var node in t.Nodes.Distinct().OrderBy(x => x.ID))
                Console.WriteLine(node.ID);
 
            Console.ReadLine();
        }

We can even do a reverse-lookup by putting this in the BinaryTree class:

        public Node FindNode(uint id)
        {
            // Grab the first node, which is always the root
            var current = Root;
 
            // Get the bit index to start at (the left-most bit)
            var i = GetFirstSetBitIndex(id);
 
            // Keep following the tree by using the bit at "i" (0 means go right, 1 means go left) until
            // we hit the end. We do --i instead of i-- since we still have to decrement the count once
            // for the root. If current == null, then we have an invalid ID.
            while ((--i >= 0) && current != null)
            {
                if ((id & (1 << i)) != 0)
                    current = current.Left;
                else
                    current = current.Right;
            }
 
            return current;
        }
 
        /// <summary>
        /// Gets the 0-based index of the left-most set bit. For a more efficient way, see BitOps.RequiredBits in NetGore.
        /// Value will be -1 if no bits are set.
        /// </summary>
        static int GetFirstSetBitIndex(uint value)
        {
            if (value == 0)
                return -1;
 
            for (int i = 32; i > 0; i--)
            {
                if ((value & (uint)(1 << i)) != 0)
                    return i;
            }
 
            return 0;
        }

And to test it:

        static void Main(string[] args)
        {
            BinaryTree t = new BinaryTree();
            FillTree(t.Root, 8);
 
            // Grab a random node (whatever the 50th node is) in the tree
            var n = t.Nodes.Skip(50).First();
 
            // Get the node by using the ID
            var nFromID = t.FindNode(n.ID);
 
            // Ensure it is correct
            System.Diagnostics.Debug.Assert(n == nFromID);
        }

Posts: 458

I dont have the time to read this atm, its 2 am.
(i fell asleep at my GFs, meant to be home sooner)

Spodi, thank you so much...

will definetely work through this tommorrow!
Hope I get the time to post tommorrow already Laughing out loud

Thank you so much!

Posts: 56

Holy crap Spodi. God of algorithms much?

bake wrote:
Women = (money)^2

Root both sides.

Rooting a woman = money

Therefore root a woman and you get money.

Posts: 458

Well, the Tree I need will need to have values entered into each Node,
Thats why I want them to be adressable easily...

I thought a unique ID would be the easiest to do this.

I'll dig into that post & code later today, thats a hell of a lot for sure Laughing out loud

Posts: 1691

Well usually the point with a tree is that you crawl it based on some criteria. For instance, a common usage is have the left node be "less than" and right node "greater than". Then, you crawl it until you find the value you want based on that recursive "less than"/"greater than" comparison.

Posts: 458

Hmm...
Okay So I build the Binary Tree, and instead of holding a simple value,
each Node will hold an object (An Investment with details of who invested, how much, and what profit they made with this investment)

Calculating the Profit shouldnt be hard at all with the formulas I got from the guy,
and your base-tree there has everything I could think of needing.

Now one thing I'm wondering about...

the Nodes dont need IDs if the Investments have unique IDs or?

So I could just have each placed Investment get a new ID (incrementing)
and via recursive crawling I could simply look for the Node that contains the Investment.ID im looking for or?

EDIT: I need to display some layers and have these Nodes selectable.
(you click the Node and you get a menu what you want to invest into that position)
Just curious if this would require an ID system, or if I can work with the location of the Node...

i.e. how would you handle this?

P.S. when I read the fill tree, Do I get it right that it will first create the very left nodes
(with 2 children each), and then create the right nodes with its children?

Posts: 458

Noone have time or an idea for this?