Problem: Consider a complete k-ary tree with l levels, with nodes labelled by their rank in a breadth first traversal. Compute the list of labels in the order that they are traversed in the depth first traversal.
For example, for a binary tree with 3 levels, the required list is: [0 1 3 7 8 4 9 10 2 5 11 12 6 13 14]
One way to do this is to actually construct a tree structure and traverse it twice; The first traversal is breadth first, labelling the nodes 0,1,2,.. as you go. The second traversal is depth first, reporting the labels 0,1,3,7,.. as you go.
I'm interested in a method that avoids constructing a tree in memory. I realize that the size of such a tree is comparable to the size of the output, but I'm hoping that the solution will allow for a "streaming" output (ie one that needs not be stored entirely in memory).
I am also interested in the companion problem; start with a tree labelled according to a depth first traversal and generate the labels of a breadth first traversal. I imagine that the solution to this problem will be, in some sense, dual to the first problem.
You don't actually need to construct the tree. You can do the depth first traversal using just the BFS labels instead of pointers to actual nodes.
Using BFS position labels to represent the nodes in k-ary tree:
- The root is
0
- The first child of any node
n
isk*n + 1
- The right sibling of a node
n
, if it has one, isn+1
in code it looks like this:
class Whatever
{
static void addSubtree(List<Integer> list, int node, int k, int levelsleft)
{
if (levelsleft < 1)
{
return;
}
list.add(node);
for (int i=0; i<k; i++)
{
addSubtree(list, node*k+i+1, k, levelsleft-1);
}
}
public static void main (String[] args) throws java.lang.Exception
{
int K = 2, LEVELS = 4;
ArrayList<Integer> list = new ArrayList<>();
addSubtree(list, 0, K, LEVELS);
System.out.println(list);
}
}
This is actually used all the time to represent a binary heap in an array -- the nodes are the array elements in BFS order, and the tree is traversed by performing these operations on indexes.
You can use the standard DFS and BFS algorithms, but instead of getting the child nodes of a particular node from a pre-built tree structure, you can compute them as needed.
For a BFS-numbered, complete K-ary tree of height H, the i-th child of a node N at depth D is:
K*N + 1 + i
A derivation of this formula when i = 0
(first child) is provided here.
For a DFS-numbered, complete K-ary tree of height H, the i-th child of a node N at depth D is given by a much uglier formula:
N + 1 + i*step where step = (K^(H - D) - 1) / (K - 1)
Here is a rough explanation of this formula:
For a node N at depth D in a DFS-numbered K-ary tree of height H, its first child is simply N+1 because it is the next node to be visited in a depth-first traversal. The second child of N will be visited directly after visiting the entire sub-tree rooted at the first child (N+1), which is itself a complete K-ary tree of height
H - (D + 1)
. The size of any complete, K-ary tree is given by the sum of a finite geometric series as explained here. The size of said sub-tree is the distance between the first and second children, and, in fact, it is the same distance between all siblings since each of their sub-trees are the same size. If we call this distancestep
, then:1st child is
N + 1
2nd child isN + 1 + step
3rd child isN + 1 + step + step
...and so on.
Below is a Python implementation (note: the dfs
function uses the BFS formula, because it is converting from DFS to BFS, and vice-versa for the bfs
function.):
def dfs(K, H):
stack = list()
push, pop = list.append, list.pop
push(stack, (0, 0))
while stack:
label, depth = pop(stack)
yield label
if depth + 1 > H: # leaf node
continue
for i in reversed(range(K)):
push(stack, (K*label + 1 + i, depth + 1))
def bfs(K, H):
from collections import deque
queue = deque()
push, pop = deque.append, deque.popleft
push(queue, (0, 0))
while queue:
label, depth = pop(queue)
yield label
if depth + 1 > H: # leaf node
continue
step = (K**(H - depth) - 1) // (K - 1)
for i in range(K):
push(queue, (label + 1 + i*step, depth + 1))
print(list(dfs(2, 3)))
print(list(bfs(2, 3)))
print(list(dfs(3, 2)))
print(list(bfs(3, 2)))
The above will print:
[0, 1, 3, 7, 8, 4, 9, 10, 2, 5, 11, 12, 6, 13, 14]
[0, 1, 8, 2, 5, 9, 12, 3, 4, 6, 7, 10, 11, 13, 14]
[0, 1, 4, 5, 6, 2, 7, 8, 9, 3, 10, 11, 12]
[0, 1, 5, 9, 2, 3, 4, 6, 7, 8, 10, 11, 12]
Here's some javascript that seems to solve the problem.
var arity = 2;
var depth = 3;
function look(rowstart, pos, dep) {
var number = rowstart + pos;
console.log(number);
if (dep < depth-1) {
var rowlen = Math.pow(arity, dep);
var newRowstart = rowstart + rowlen;
for (var i = 0; i < arity; i++) {
look(newRowstart, pos*arity + i, dep+1);
}
}
}
look(0, 0, 0);
It's a depth-first search that calculates the BFS label of each node on the way down.
It calculates the label of a node using the current depth dep
, the horizontal position in the current row (pos
) and the label of the first node in the row (rowstart
).
来源:https://stackoverflow.com/questions/39070461/functions-to-convert-between-depth-first-and-breadth-first-traversals-of-a-compl