Problem:
Binary Tree

A rooted binary tree is a tree with a root node in which every node has at most two children.

Your task is to write a program which reads a rooted binary tree T and prints the following information for each node u of T:

node ID of u

parent of u

sibling of u

the number of children of u

depth of u

height of u

node type (root, internal node or leaf)

If two nodes have the same parent, they are siblings. Here, if u and v have the same parent, we say u is a sibling of v (vice versa).

The height of a node in a tree is the number of edges on the longest simple downward path from the node to a leaf.

Here, the given binary tree consists of n nodes and evey node has a unique ID from 0 to n-1.

Input

The first line of the input includes an integer n, the number of nodes of the tree.

In the next n lines, the information of each node is given in the following format:

id left right

id is the node ID, left is ID of the left child and right is ID of the right child. If the node does not have the left (right) child, the left(right) is indicated by -1.

Output

Print the information of each node in the following format:

node id: parent = p, sibling = s, degree = deg, depth = dep, height = h, type

p is ID of its parent. If the node does not have a parent, print -1.

s is ID of its sibling. If the node does not have a sibling, print -1.

deg, dep and h are the number of children, depth and height of the node respectively.

type is a type of nodes represented by a string (root, internal node or leaf. If the root can be considered as a leaf or an internal node, print root.

Please follow the format presented in a sample output below.

Constraints

1 ≤ n ≤ 25

Sample Input 1

9
0 1 4
1 2 3
2 -1 -1
3 -1 -1
4 5 8
5 6 7
6 -1 -1
7 -1 -1
8 -1 -1

Sample Output 1

node 0: parent = -1, sibling = -1, degree = 2, depth = 0, height = 3, root
node 1: parent = 0, sibling = 4, degree = 2, depth = 1, height = 1, internal node
node 2: parent = 1, sibling = 3, degree = 0, depth = 2, height = 0, leaf
node 3: parent = 1, sibling = 2, degree = 0, depth = 2, height = 0, leaf
node 4: parent = 0, sibling = 1, degree = 2, depth = 1, height = 2, internal node
node 5: parent = 4, sibling = 8, degree = 2, depth = 2, height = 1, internal node
node 6: parent = 5, sibling = 7, degree = 0, depth = 3, height = 0, leaf
node 7: parent = 5, sibling = 6, degree = 0, depth = 3, height = 0, leaf
node 8: parent = 4, sibling = 5, degree = 0, depth = 2, height = 0, leaf

Reference

Introduction to Algorithms, Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein. The MIT Press.

Solution in C# (judge verdict: Accepted)

// optional
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;

public static class Program {
    class Node {
        public int Parent { get; set; }
        public int Left { get; set; }
        public int Right { get; set; }
    }

    static int[] depths;
    static int[] heights;
    static Node[] nodes;

    public static void Main() {
        #region SetAutoFlushIsFalse
#if !DEBUG
        var sw = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false };
        Console.SetOut(sw);
#endif
        #endregion

        var n = ReadLib.ReadAsInt();
        nodes = new Node[n];
        depths = new int[n];
        heights = new int[n];
        for (int i = 0; i < n; i++) {
            nodes[i] = new Node() { Parent = -1, Left = -1, Right = -1 };
        }

        for (int i = 0; i < n; i++) {
            var a = ReadLib.ReadAsIntArray();
            var cur = nodes[a[0]];
            cur.Left = a[1];
            cur.Right = a[2];
            if (cur.Left != -1) {
                nodes[cur.Left].Parent = a[0];
            }

            if (cur.Right != -1) {
                nodes[cur.Right].Parent = a[0];
            }
        }

        var rootId = Array.FindIndex(nodes, node => node.Parent == -1);

        SetHeight(rootId);
        SetDepth(rootId, 0);

        for (int i = 0; i < n; i++) {
            var @class = "root";
            if (nodes[i].Parent != -1) {
                @class = "leaf";
            }

            var siblingId = -1;

            if (@class != "root") {
                var left = nodes[nodes[i].Parent].Left;
                var right = nodes[nodes[i].Parent].Right;

                siblingId = left == i ? right : left;
            }

            var degree = new int[] { nodes[i].Left, nodes[i].Right }.Count(j => j != -1); ;

            if (degree > 0 && nodes[i].Parent != -1) {
                @class = "internal node";
            }

            Console.WriteLine($"node {i}: parent = {nodes[i].Parent}, sibling = {siblingId}, degree = {degree}, depth = {depths[i]}, height = {heights[i]}, {@class}");
        }

        #region Flush
#if !DEBUG
        Console.Out.Flush();
        sw.Close();
#endif
        #endregion
    }

    static void SetDepth(int i, int d) {
        depths[i] = d;

        if (nodes[i].Left != -1) {
            SetDepth(nodes[i].Left, d + 1);
        }

        if (nodes[i].Right != -1) {
            SetDepth(nodes[i].Right, d + 1);
        }
    }

    static void SetHeight(int i) {
        int h1 = 0, h2 = 0;

        if (nodes[i].Left != -1) {
            SetHeight(nodes[i].Left);
            h1 = heights[nodes[i].Left] + 1;
        }

        if (nodes[i].Right != -1) {
            SetHeight(nodes[i].Right);
            h2 = heights[nodes[i].Right] + 1;
        }

        heights[i] = Math.Max(h1, h2);
    }
}

static class ReadLib {
    public static long ReadAsLong() {
        return long.Parse(Console.ReadLine());
    }

    public static int ReadAsInt() {
        return int.Parse(Console.ReadLine());
    }

    public static int[] ReadAsIntArray(int step = 0) {
        return Console.ReadLine().Split(' ').Select(s => int.Parse(s) + step).ToArray();
    }

    public static long[] ReadAsLongArray(int step = 0) {
        return Console.ReadLine().Split(' ').Select(s => long.Parse(s) + step).ToArray();
    }

    public static ulong[] ReadAsULongArray() {
        return Console.ReadLine().Split(' ').Select(s => ulong.Parse(s)).ToArray();
    }

    public static string[] ReadAsStringArray() {
        return Console.ReadLine().Split(' ');
    }

    public static int[] ReadNTimesAsInt(int n, int step = 0) {
        return Enumerable.Range(1, n).Select(s => int.Parse(Console.ReadLine()) + step).ToArray();
    }

    public static char[][] ReadNTimesAsCharArray(int n) {
        return Enumerable.Range(1, n).Select(i => Console.ReadLine().ToArray()).ToArray();
    }

    public static long[] ReadNTimesAsLong(int n, int step = 0) {
        return Enumerable.Range(1, n).Select(s => long.Parse(Console.ReadLine()) + step).ToArray();
    }

    public static int[][] ReadNTimesAsIntArray(int n, int step = 0) {
        return Enumerable.Range(1, n).Select(s => ReadAsIntArray(step)).ToArray();
    }

    public static string[] ReadNTimesAsString(int n) {
        return Enumerable.Range(1, n).Select(s => Console.ReadLine()).ToArray();
    }

    public static char[,] ReadMapAsChar(int hSize, int wSize) {
        var map = new char[hSize, wSize];
        for (int h = 0; h < hSize; h++) {
            var a = Console.ReadLine();
            for (int w = 0; w < wSize; w++) {
                map[h, w] = a[w];
            }
        }
        return map;
    }
}

namespace Heaps {
    /// <summary>優先度付きキュー</summary>
    public class PriorityQueue<T> : IEnumerable<T>, IReadOnlyCollection<T> where T : IComparable<T> {
        public List<T> HeapArray { get; }
        public T NIL { get; }
        public bool IsMax { get; }

        public int Count => HeapArray.Count;

        /// <param name="heapArray">ヒープ</param>
        /// <param name="NIL">NILの値</param>
        /// <param name="IsMax">True時：大きい方から取り出す、False時：小さい方から取り出す</param>
        public PriorityQueue(T NIL, IEnumerable<T> heapArray = null, bool IsMax = true) {
            this.HeapArray = heapArray == null ? new List<T>() : heapArray.ToList();
            this.NIL = NIL;
            this.IsMax = IsMax;
            Update();
        }

        /// <summary>KeyのValueを取得</summary>
        public T this[int id] {
            get {
                if (id <= 0 || id > HeapArray.Count) {
                    return NIL;
                }

                return HeapArray[id - 1];
            }
        }

        class Node {
            public int Key { get; }
            public T Value => outer[Key];
            readonly PriorityQueue<T> outer;

            public Node(int key, PriorityQueue<T> outer) {
                Key = key;
                this.outer = outer;
            }

            public Node Left {
                get {
                    var leftKey = 2 * Key;
                    return new Node(leftKey, outer);
                }
            }

            public Node Right {
                get {
                    var rightkey = 2 * Key + 1;
                    return new Node(rightkey, outer);
                }
            }

            public Node Parent {
                get {
                    var parentKey = Key / 2;
                    return new Node(parentKey, outer);
                }
            }
        }

        private Node GetNode(int key) {
            return new Node(key, this);
        }

        /// <summary>max Heap木とする O(N)</summary>
        public void Update() {
            var n = HeapArray.Count() / 2;
            /// <summary>葉から順に処理を行う</summary>
            for (int i = n; i >= 1; i--) {
                Heapify(i);
            }
        }

        /// <summary>idを根とする部分木について、Heap木を維持したまま、idを下降させる</summary>
        private void Heapify(int key) {
            var current = GetNode(key);

            var left = current.Left;
            var right = current.Right;

            var largest = current;
            var sign = IsMax ? 1 : -1;
            if (left.Value.CompareTo(NIL) != 0 && left.Value.CompareTo(current.Value) * sign > 0) {
                largest = left;
            }

            if (right.Value.CompareTo(NIL) != 0 && right.Value.CompareTo(largest.Value) * sign > 0) {
                largest = right;
            }

            if (largest.Key != key) {
                HeapArray.Swap(key - 1, largest.Key - 1);   // 元と異なるときはSwapする
                Heapify(largest.Key); // largest.Key は元のkeyの場所。葉で更にMaxHeaptyを行う。
            }
        }

        /// <summary>keyの値を挿入する</summary>
        public void Enqueue(T value) {
            HeapArray.Add(value);

            var inserted = GetNode(HeapArray.Count);    // 末尾に挿入
            var parent = inserted.Parent;
            var sign = IsMax ? 1 : -1;

            // 挿入Nodeが根になる or 親 > 子になるまで親とNodeを交換
            while (inserted.Key > 1 && value.CompareTo(parent.Value) * sign > 0) {
                HeapArray.Swap(inserted.Key - 1, parent.Key - 1);
                inserted = parent;
                parent = inserted.Parent;
            }
        }

        public T Dequeue() {
            if (HeapArray.Count == 0) {
                return NIL;
            }

            var popVal = HeapArray.First();
            var newRootVal = HeapArray.Last();  // 新しい根となる。

            HeapArray.RemoveAt(HeapArray.Count - 1);    // del last

            if (HeapArray.Count > 0) {
                HeapArray[0] = newRootVal;
                Heapify(1);
            }

            return popVal;
        }

        public T Peek() {
            return HeapArray.First();
        }

        public IEnumerator<T> GetEnumerator() {
            while (HeapArray.Count > 0) {
                yield return Dequeue();
            }
        }

        IEnumerator IEnumerable.GetEnumerator() {
            return GetEnumerator();
        }

        public void Clear() {
            HeapArray.Clear();
        }

        public bool Contains(T val) {
            return HeapArray.Contains(val);
        }

        public PriorityQueue<T> Clone() {
            return new PriorityQueue<T>(NIL, HeapArray.DeepCopy());
        }
    }
}

/// <summary>拡張メソッドs</summary>
static class MyExtention {
    /// <summary>iとjを入れ替える</summary>
    public static void Swap<T>(this T[] arr, int i, int j) {
        var buf = arr[i];
        arr[i] = arr[j];
        arr[j] = buf;
    }

    public static void Swap<T>(this List<T> list, int i, int j) {
        var buf = list[i];
        list[i] = list[j];
        list[j] = buf;
    }

    /// <summary>
    /// partition。末尾sArr[r]を基準として、左に基準より小さい数、右に基準より大きい数が存在するように配列を並び替える。
    /// </summary>
    /// <param name="sArr"></param>
    /// <param name="p">partitionの先頭の添字</param>
    /// <param name="r">partitionの末尾の添字</param>
    /// <returns>partitionの基準が存在する添字</returns>
    public static int Partition(this int[] sArr, int p, int r) {
        var x = sArr[r];
        var i = p - 1;
        for (int j = p; j < r; j++) {
            if (sArr[j] <= x) {
                i++;
                sArr.Swap(i, j);
            }
        }
        sArr.Swap(i + 1, r);
        return i + 1;
    }

    /// <summary>深いコピーをする</summary>
    public static T DeepCopy<T>(this T obj) {
        T result;
        var b = new BinaryFormatter();
        using (var mem = new MemoryStream()) {
            b.Serialize(mem, obj); //  binaryFormatterによりobjをmemにserial化して書き出す
            mem.Position = 0; // memのpotisionを先頭に戻す
            result = (T)b.Deserialize(mem); // memの内容を読み取る。
        }
        return result;
    }

    public static string Left(this string str, int length) {
        return str.Substring(0, length);
    }

    public static string Right(this string str, int length) {
        return str.Substring(str.Length - length, length);
    }

    public static int Find<T>(this T[] array, T obj) {
        return Array.IndexOf(array, obj);
    }

    public static int LastFind<T>(this T[] array, T obj) {
        return Array.LastIndexOf(array, obj);
    }

    /// <summary>同一の値で埋める</summary>
    public static void Initialize<T>(this T[,] array, T value) {
        for (int i = 0; i < array.GetLength(0); i++) {
            for (int j = 0; j < array.GetLength(1); j++) {
                array[i, j] = value;
            }
        }
    }
}

static class MyMath {
    /// <summary>階乗</summary>
    public static long Factorial(int n) {
        if (n == 0) {
            return 1;
        }

        return Permutation(n, 0);
    }

    public static long Permutation(int n, int k) {
        return Enumerable.Range(0, n - k).Select(i => n - i).Aggregate(1, (b, i) => b * i);
    }

    #region FullPower
    /// <summary>総乗、π</summary>
    public static long FullPower(IEnumerable<long> vs) {
        return vs.Aggregate(1L, (a, b) => a * b);
    }

    public static long FullPower(IEnumerable<int> vs) {
        return vs.Aggregate(1L, (a, b) => a * b);
    }
    #endregion

    /// <summary>最大公約数</summary>
    public static long Gcd(long item1, long item2) {
        var a = Math.Max(item1, item2);
        var b = Math.Min(item1, item2);

        while (true) {
            long mod;
            Math.DivRem(a, b, out mod);
            if (mod == 0) {
                break;
            }

            a = b;
            b = mod;
        }
        return b;
    }

    /// <summary>約分。最大公約数で割ったものを返す。</summary>
    public static Tuple<long, long> DeviedByGcd(long item1, long item2) {
        var gcd = Gcd(item1, item2);
        return new Tuple<long, long>(item1 / gcd, item2 / gcd);
    }

    public static long Lcm(int item1, int item2) {
        return Math.BigMul(item1, item2) / Gcd(item1, item2);
    }

    public static ModCombination ModCombination(int maxN, int mod = 1000000007) {
        return new ModCombination(maxN, mod);
    }

    /// <summary>a^n mod</summary>
    public static int ModPow(long a, long n, int mod = 1000000007) {
        long res = 1;
        long pow = a;

        while (n > 0) {
            if ((n & 1) == 1) {
                res = (pow * res) % mod;    // resにaをかける
            }

            pow = (pow * pow) % mod;    // 次のaはa^2
            n >>= 1;
        }

        return (int)res;
    }

    /// <summary>約数を列挙</summary>
    public static long[] Divisors(long n) {
        var ret = new List<long>();
        var sqrt = Math.Ceiling(Math.Sqrt(n));
        for (long i = 1; i <= sqrt; i++) {
            if (i * i > n) {
                continue;
            }

            if (n % i != 0) {
                continue;
            }

            ret.Add(i);

            if (n / i == i) {
                continue;
            }

            ret.Add(n / i);
        }

        return ret.ToArray();
    }
}

static class MyMethod {
    /// <summary>数え上げて、key, 出現回数に変換</summary>
    public static Dictionary<T, int> CountUp<T>(IEnumerable<T> str) {
        return
            str
            .GroupBy(c => c)
            .Select(g => new KeyValuePair<T, int>(g.Key, g.Count()))
            .ToDictionary(e => e.Key, e => e.Value);
    }

    /// <summary>ある要素を持つindexをすべて返す</summary>
    public static int[] InducesOfValue<T>(this T[] arr, T searchValue) {
        return arr
            .Select((v, i) => new { v, i })
            .Where(o => searchValue.Equals(o.v))
            .Select(o => o.i)
            .ToArray();
    }

    /// <summary>ある要素を持つindexをすべて返す</summary>
    public static int[] Induces<T>(this IEnumerable<T> arr, T searchValue) {
        return arr.ToArray().InducesOfValue(searchValue);
    }
}

/// <summary>組み合わせ</summary>
class ModCombination {
    private int mod;
    private int[] fac;
    private int[] finv;
    private int[] inv;

    public ModCombination(int max, int mod = 1000000007) {
        this.mod = mod;
        fac = new int[max]; // modが法のn!の列

        inv = new int[max]; // indexの逆元の列
        finv = new int[max]; // modを法とした、invの累積の累積

        fac[0] = 1; fac[1] = 1;
        finv[0] = 1; finv[1] = 1;
        inv[1] = 1;

        for (int i = 2; i < max; i++) {
            fac[i] = (int)(Math.BigMul(fac[i - 1], i) % mod);  // factorを求める
            var s = Math.BigMul(inv[mod % i], mod / i) % mod;
            inv[i] = (int)(mod - s);　 // iに対する逆元を求める
            finv[i] = (int)(Math.BigMul(finv[i - 1], inv[i]) % mod);
        }
    }
    /// <summary>nCk</summary>
    public int Calculation(int n, int k) {
        if (n < k) return 0;
        if (n < 0 || k < 0) return 0;

        return (int)(Math.BigMul(fac[n], (int)(Math.BigMul(finv[k], finv[n - k]) % mod)) % mod);
    }
}

/// <summary>あまりを計算するためのintのラッパー</summary>
struct ModInt {
    int Value { get; }
    int Mod { get; }

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

    public static explicit operator ModInt(long value) {
        return new ModInt(value);
    }

    public ModInt(long value, int mod = 1000000007) {
        this.Value = (int)(value % mod);
        this.Mod = mod;
    }

    public static explicit operator int(ModInt modInt) {
        return modInt.Value;
    }

    private static void CheckNotEqualMod(ModInt item1, ModInt item2) {
        if (item1.Mod != item2.Mod) {
            throw new Exception("mod not equal");
        }
    }

    public static ModInt operator +(ModInt item1, ModInt item2) {
        CheckNotEqualMod(item1, item2);
        long value = item1.Value + item2.Value;
        return new ModInt(value, item1.Mod);
    }

    public static ModInt operator *(ModInt item1, ModInt item2) {
        CheckNotEqualMod(item1, item2);
        long value = Math.BigMul(item1.Value, item2.Value);
        return new ModInt(value, item1.Mod);
    }

    public static ModInt operator -(ModInt item1, ModInt item2) {
        CheckNotEqualMod(item1, item2);
        long value = item1.Value - item2.Value;
        if (value < 0) {
            value += item1.Mod;
        }
        return new ModInt(value, item1.Mod);
    }

    public static ModInt operator +(ModInt item1, int item2) {
        return item1 + new ModInt(item2, item1.Mod);
    }

    public static ModInt operator *(ModInt item1, int item2) {
        return item1 * new ModInt(item2, item1.Mod);
    }

    public static ModInt operator -(ModInt item1, int item2) {
        return item1 - new ModInt(item2, item1.Mod);
    }


    public override int GetHashCode() {
        return this.Value;
    }

    public override bool Equals(object obj) {
        if (obj == null || this.GetType() != obj.GetType()) {
            return false;
        }

        var modIntObj = (ModInt)obj;
        return modIntObj.Value == this.Value;
    }

    public static bool operator ==(ModInt item1, ModInt item2) {
        return item1.Value == item2.Value;
    }

    public static bool operator !=(ModInt item1, ModInt item2) {
        return !(item1 == item2);
    }

    public static bool operator ==(ModInt item1, int item2) {
        return item1.Value == item2;
    }

    public static bool operator !=(ModInt item1, int item2) {
        return !(item1.Value == item2);
    }

    public static bool operator >=(ModInt item1, ModInt item2) {
        return item1.Value >= item2.Value;
    }

    public static bool operator <=(ModInt item1, ModInt item2) {
        return item1.Value <= item2.Value;
    }

    public static bool operator >=(ModInt item1, int item2) {
        return item1.Value >= item2;
    }

    public static bool operator <=(ModInt item1, int item2) {
        return item1.Value <= item2;
    }

    public static bool operator >(ModInt item1, ModInt item2) {
        return item1.Value > item2.Value;
    }

    public static bool operator <(ModInt item1, ModInt item2) {
        return item1.Value < item2.Value;
    }

    public static bool operator >(ModInt item1, int item2) {
        return item1.Value >= item2;
    }

    public static bool operator <(ModInt item1, int item2) {
        return item1.Value <= item2;
    }
}