In graph theory, an m-ary tree (for nonnegative integers m) (also known as n-ary, k-ary or k-way tree) is an arborescence (or, for some authors, an ordered tree)[1] [2] in which each node has no more than m children. A binary tree is an important case where m = 2; similarly, a ternary tree is one where m = 3.
mh
N
See main article: tree traversal.
Traversing a m-ary tree is very similar to traversing a binary tree. The pre-order traversal goes to parent, left subtree and the right subtree, and for traversing post-order it goes by left subtree, right subtree, and parent node. For traversing in-order, since there are more than two children per node for m > 2, one must define the notion of left and right subtrees. One common method to establish left/right subtrees is to divide the list of children nodes into two groups. By defining an order on the m children of a node, the first nodes would constitute the left subtree and nodes would constitute the right subtree.
Using an array for representing a m-ary tree is inefficient, because most of the nodes in practical applications contain less than m children. As a result, this fact leads to a sparse array with large unused space in the memory. Converting an arbitrary m-ary tree to a binary tree would only increase the height of the tree by a constant factor and would not affect the overall worst-case time complexity. In other words, since .
First, we link all the immediate children nodes of a given parent node together in order to form a link list. Then, we keep the link from the parent to the first (i.e., the leftmost) child and remove all the other links to the rest of the children. We repeat this process for all the children (if they have any children) until we have processed all the internal nodes and rotate the tree by 45 degrees clockwise. The tree obtained is the desired binary tree obtained from the given m-ary tree.
m-ary trees can also be stored in breadth-first order as an implicit data structure in arrays, and if the tree is a complete m-ary tree, this method wastes no space. In this compact arrangement, if a node has an index i, its c-th child in range is found at index
m ⋅ i+c
O(mn)
Each node would have an internal array for storing pointers to each of its
m
O(m ⋅ n)
See main article: Enumerative Combinatorics.
Listing all possible m-ary trees is useful in many disciplines as a way of checking hypotheses or theories.Proper representation of m-ary tree objects can greatly simplify the generation process. One can construct a bit sequence representation using the depth-first search of a m-ary tree with n nodes indicating the presence of a node at a given index using binary values. For example, the bit sequence x=1110000100010001000 is representing a 3-ary tree with n=6 nodes as shown below.
The problem with this representation is that listing all bit strings in lexicographic order would mean two successive strings might represent two trees that are lexicographically very different. Therefore, enumeration over binary strings would not necessarily result in an ordered generation of all m-ary trees.[7] A better representation is based on an integer string that indicates the number of zeroes between successive ones, known as Simple Zero Sequence. is a Simple Zero Sequence corresponding to the bit sequence where j is the number of zeroes needed at the tail end of the sequence to make the string have the appropriate length. For example,
1110000100010001000\equiv100100104104103\equiv00433
024132
O(1)
n-1
nth
The table below shows the list of all valid simple zero sequences of all 3-ary trees with 4 nodes:
222 | 200 | 111 | 033 | 013 | |
221 | 132 | 110 | 032 | 012 | |
220 | 131 | 105 | 031 | 011 | |
213 | 130 | 104 | 030 | 010 | |
212 | 123 | 103 | 024 | 006 | |
211 | 122 | 102 | 023 | 005 | |
210 | 121 | 101 | 022 | 004 | |
204 | 120 | 100 | 021 | 003 | |
203 | 114 | 042 | 020 | 002 | |
202 | 113 | 041 | 015 | 001 | |
201 | 112 | 040 | 014 | 000 |
Once one has exhausted all possible positions in the backbone template, a new template will be constructed by shifting the 3rd node one position to the right as depicted below, and the same enumeration would occur until all possible positions labeled "X" is exhausted.
Going back to the table of enumeration of all m-ary trees, where
m=3
n=4
The pseudocode for this enumeration is given below:
Procedure NEXT(s1, s2, …, sn−1) if si = 0 for all i then finished else i ← max si ← si − 1 if i < n − 1 then si ← (i + 1) ⋅ (m − 1) − sum(sj) end if for j ← i + 2, i + 3, …, n − 1 sj ← k − 1 end if end
A generation algorithm that takes
O(1)
O(1)
a
d
t
a
d
b
a
m-1
d
a
d
d
Convert an m-ary tree to left-tree for i = 1...n: for t = 2...m: while t child of node at depth i ≠ 1: L-t rotation at nodes at depth i end while end for end for
A right-t rotation at d is the inverse of this operation. The left chain of T is a sequence of
x1,x2,...,xn
x1
xn
m[1]
xi
m-1
ci
Let the
m-1
c1,c2,...,cm-1
c(i-1)(m-1)+t-1
Capturing counts of left-rotations at each depth is a way of encoding an m-ary tree. Thus, enumerating all possible legal encoding would helps us to generate all the m-ary trees for a given m and n. But, not all
ci
(n-1) ⋅ (m-1)+1
Lexicographically smallest code-word representation of a m-ary with n nodes is all zeros and the largest is n−1 ones followed by m−1 zero on its right.
Initialization c[i] to zero for all i from 1 to n⋅(k − 1) p[i] set to n − 1 for i from 1 to n sum ← 0 j ← m − 1 Termination Condition Terminate when c[1] = n − 1 Procedure NEXT sum ← sum + 1 − c[''j'' + 1] c[j] ← c[''j''] + 1 if p[''q''[''j'']] > p[''q''[''j'' + 1]] + 1 then p[''q''[''j'']] ← p[''q''[''j'' + 1]] + 1 end if p[''q''[''j'' + ''c''[''j'']]] ← p[''q''[''j'']] c[''j'' + 1] ← 0 if sum = p[''q''[''j'']] then j ← j − 1 else p[''n''] ← sum j ← m − 1 end if end
One of the applications of m-ary tree is creating a dictionary for validation of acceptable strings. In order to do that, let m be equal to the number of valid alphabets (e.g., number of letters of the English alphabet) with the root of the tree representing the starting point. Similarly, each of the children can have up to m children representing the next possible character in the string. Thus, characters along the paths can represent valid keys by marking the end character of the keys as "terminal node". For example, in the example below "at" and "and" are valid key strings with "t" and "d" marked as terminal nodes. Terminal nodes can store extra information to be associated with a given key. There are similar ways to building such a dictionary using B-tree, Octree and/or trie.