英文:
level order tree traversal without using additional memory
问题
我了解层次遍历树的算法。 (我认为每个人都知道这个)该算法使用队列来存储树的节点。是否有一种不使用额外内存的算法?该算法不能使用递归(这样我们会使用栈)。请注意,树以左子右兄弟表示法给出。不允许使用额外的指针。
C中用于树的结构如下:
struct node {
int data;
struct node *left-child;
struct node *right-sibling;
}
树通过指向根节点的指针来表示。当然,根节点不能有右兄弟。
英文:
I know about algorithm to level order traversal of a tree. (I think everybody knows about that) That algorithm uses queue to store the nodes of the tree. Is there a algorithm that do not uses additional memory? That algorithm must not use recursion (in that way we are using stack). Note, that tree is given in left-child right-sibling representation. No additional pointers are allowed.
The structures in C, for the tree are:
struct node {
int data;
struct node *left-child;
struct node *right-sibling;
}
Tree is represented with a pointer to the root node. Of course, root cannot have right-sibling.
答案1
得分: 1
以下是您要翻译的代码部分:
function * traverse(node) {
let lead = node; // ...walks somewhere ahead of node
let lag = null; // ... always follows one step behind node
while (node) {
yield node.data; // output
lead.rightSibling = node.leftChild;
while (lead.rightSibling) lead = lead.rightSibling;
// rotate: point node to next right-sibling, and reverse direction of sibling edge
[node.rightSibling, lag, node] = [lag, node, node.rightSibling]
}
// Restore tree
lead = node = lag.rightSibling; // backwards
lag.rightSibling = null;
while (lead !== null && lead.leftChild === null) lead = lead.rightSibling; // actually going left!
while (node) {
if (lead !== null && lead.leftChild === lag) {
// When lag is the leftChild of some node (lead), then lag should not be the target of a rightSibling
[node.rightSibling, lag, node] = [null, node, node.rightSibling];
// Find previous parent
lead = lead.rightSibling;
while (lead !== null && lead.leftChild === null) lead = lead.rightSibling; // actually going left!
} else {
// walk back and restore sibling pointers
[node.rightSibling, lag, node] = [lag, node, node.rightSibling];
}
}
}
// Create node, given its data and child nodes
function Node(data, ...children) {
// Link the children as siblings
if (children.length > 1) children.reduceRight((a, b) => (b.rightSibling = a, b))
// Create the node itself. For now, without any siblings
return {
data,
leftChild: children.length ? children[0] : null,
rightSibling: null
};
}
// Example tree
let tree = Node(1,
Node(2,
Node(5), Node(6,
Node(14), Node(15), Node(16)
), Node(7)
), Node(3,
Node(8), Node(9)
), Node(4,
Node(10), Node(11,
Node(17), Node(18), Node(19)
), Node(12), Node(13)
)
);
// Apply the algorithm and output the yielded values
console.log(...traverse(tree));
希望这对您有所帮助。
英文:
One way could be to use the right-sibling
pointers which are null, to make all nodes siblings of each other (temporarily).
You could use a slow and fast pointer. The fast one would always be at the last sibling (that has a null pointer as right-sibling
). The left-child
pointer of the slow node would then be copied into that right-sibling
, after which the fast pointer runs further to the end again. The slow pointer goes one step to the right and the same repeats. When the slow pointer also reaches the end, all nodes will be siblings. Either the slow or fast pointer can be used to output the values in the level-order. This will do the job, but the tree will have been destroyed as a consequence.
To restore the tree, I would suggest that during the above process the direction of all sibling edges is reversed. This means you need to have another pointer that lags behind the slow pointer. This will allow the reversal to be performed between those two. This is a bit obscure, because the right-sibling
will then in fact point to something that is mostly a left sibling.
After the above process, the pointers will be at the end of the node list, but because we have reversed the sibling edges, we can also walk back and reverse the edges again. One difficulty is to know which sibling pointers should become null again (for when a node was originally a right most child). This can be done by having again a fast pointer moving ahead (in the left direction) to find nodes that have child. If the pointer that lags behind the slow pointer hits such a child, we know that the slow pointer's node should get a null pointer as right-sibling
. When this fix is applied, the fast pointer should again run ahead to find yet another parent node, ...etc.
Note that the left-child
pointers are not altered by this algorithm.
So, in total this solution uses three pointers and the structure of the tree itself.
Here is a sample tree I have used in an implementation below:
1
/
2 ------------ 3 ---------4
/ / /
5 -- 6 -- 7 8 -- 9 10 -- 11 -- 12 -- 13
/ /
14 -- 15 -- 16 17 -- 18 -- 19
Implementation in JavaScript -- runnable snippet:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
function * traverse(node) {
let lead = node; // ...walks somewhere ahead of node
let lag = null; // ... always follows one step behind node
while (node) {
yield node.data; // output
lead.rightSibling = node.leftChild;
while (lead.rightSibling) lead = lead.rightSibling;
// rotate: point node to next right-sibling, and reverse direction of sibling edge
[node.rightSibling, lag, node] = [lag, node, node.rightSibling]
}
// Restore tree
lead = node = lag.rightSibling; // backwards
lag.rightSibling = null;
while (lead !== null && lead.leftChild === null) lead = lead.rightSibling; // actually going left!
while (node) {
if (lead !== null && lead.leftChild === lag) {
// When lag is the leftChild of some node (lead), then lag should not be the target of a rightSibling
[node.rightSibling, lag, node] = [null, node, node.rightSibling];
// Find previous parent
lead = lead.rightSibling;
while (lead !== null && lead.leftChild === null) lead = lead.rightSibling; // actually going left!
} else {
// walk back and restore sibling pointers
[node.rightSibling, lag, node] = [lag, node, node.rightSibling];
}
}
}
// Create node, given its data and child nodes
function Node(data, ...children) {
// Link the children as siblings
if (children.length > 1) children.reduceRight((a, b) => (b.rightSibling = a, b))
// Create the node itself. For now, without any siblings
return {
data,
leftChild: children.length ? children[0] : null,
rightSibling: null
};
}
// Example tree
let tree = Node(1,
Node(2,
Node(5), Node(6,
Node(14), Node(15), Node(16)
), Node(7)
), Node(3,
Node(8), Node(9)
), Node(4,
Node(10), Node(11,
Node(17), Node(18), Node(19)
), Node(12), Node(13)
)
);
// Apply the algorithm and output the yielded values
console.log(...traverse(tree));
<!-- end snippet -->
Version in C
I am not so fluent in C, but I think this should do it:
#include <stdio.h>
#include <stdlib.h>
// define Node as a pointer to a node struct
typedef struct node {
int data;
struct node *leftChild;
struct node *rightSibling;
} * Node;
// Some helper functions to ease the creation of a tree:
Node sibling(Node leftSibling, Node rightSibling) {
leftSibling->rightSibling = rightSibling;
return leftSibling;
}
Node parent(Node parent, Node child) {
parent->leftChild = child;
return parent;
}
Node create(int data) {
Node node = malloc(sizeof(struct node));
node->data = data;
return node;
}
// end - helper functions
void traverse(Node node) {
Node lead = node; // ...walks somewhere ahead of node
Node lag = NULL; // ... always follows one step behind node
while (node) {
printf("%d\n", node->data); // output
lead->rightSibling = node->leftChild;
while (lead->rightSibling) lead = lead->rightSibling;
// rotate: point node to next right-sibling, and reverse direction of sibling edge
Node temp = node->rightSibling;
node->rightSibling = lag;
lag = node;
node = temp;
}
// Restore tree
lead = node = lag->rightSibling; // backwards
lag->rightSibling = NULL;
while (lead != NULL && lead->leftChild == NULL) lead = lead->rightSibling; // actually going left!
while (node != NULL) {
if (lead != NULL && lead->leftChild == lag) {
// When lag is the leftChild of some node (lead), then lag should not be the target of a rightSibling
lag = node;
node = node->rightSibling;
lag->rightSibling = NULL;
// Find previous parent
lead = lead->rightSibling;
while (lead != NULL && lead->leftChild == NULL) lead = lead->rightSibling; // actually going left!
} else {
// walk back and restore sibling pointers
Node temp = node->rightSibling;
node->rightSibling = lag;
lag = node;
node = temp;
}
}
}
int main(void) {
// Create the example tree
Node root = parent(create(1),
sibling(parent(create(2),
sibling(create(5), sibling(parent(create(6),
sibling(create(14), sibling(create(15), create(16)))
), create(7)))
), sibling(parent(create(3),
sibling(create(8), create(9))
), parent(create(4),
sibling(create(10), sibling(parent(create(11),
sibling(create(17), sibling(create(18), create(19)))
), sibling(create(12), create(13))))
)))
);
traverse(root);
return 0;
}
To print the tree in a very basic format, you can use this function:
void printTree(Node node, int indent) {
if (!node) return;
for (int i = 0; i < indent; i++) printf(" ");
printf("%d\n", node->data);
printTree(node->leftChild, indent+1);
printTree(node->rightSibling, indent);
}
This will help to verify that indeed the tree is the same before and after the traversal.
答案2
得分: 0
如果您可以在树的每个节点中存储一个额外的指向每个级别中下一个节点的下一个指针,那么您可以在常量空间中执行级别顺序遍历。
英文:
If you can store an extra next pointer in each node of the tree which points to the next node in level order for each level, then you can do the level order traversal in constant space.
答案3
得分: 0
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论