I have this tree structure, where each node has a next
and child
pointer. I am trying to flatten such a tree to a linked list where each node only uses the next
pointer.
Example input:
1 --> 2 --> 3 --> 4 --> 5 --> 6
|
V
7 --> 8 -- > 9 --> 10
|
V
11 --> 12
Expected output:
1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10 -> 11 -> 12
This is my code:
struct ListNode {
int val;
struct ListNode* next;
struct ListNode* child;
ListNode(int x) {
val = x;
next = NULL;
child = NULL;
}
};
ListNode* flatten(ListNode* head) {
ListNode *temp = head, *r = NULL;
while (temp) {
if (temp->child) {
r = temp->next;
temp->next = flatten(temp->child);
}
if (!temp->next && r) {
temp->next = r;
r = NULL;
}
temp = temp->next;
}
return head;
}
When I provide it the example input, my code gets into infinite recursion.
Where is my mistake?
Your code does not get into infinite recursion, but into an infinite loop.
There are these issues:
The child
pointers are never cleared to nullptr
, and so you get nodes that have both their child
and next
pointing to the same node. As you also link back tail nodes to nodes in upper "levels", this eventually leads to revisits of the same child
nodes over and over again. Things would not get into such cycles if you would clear child
pointers with temp->child = nullptr;
right after the recursive call comes back.
Although r
saves a pointer to be reused later, there is no provision for when multiple nodes in the same "horizontal" list have a non-null child
pointer: your loop can only remember one of them in r
, and so you will lose information.
The logic gives precedence to linking child lists, coming back to the "calling" list after child lists have been wired into the current list. Yet your desired output gives precedence to the next
lists, wanting to put child nodes after those. I'll assume this part of the code is wrong, and your description of the expected output is right.
You can do this without recursion by just keeping track of the current tail of the part of the list that is being flattened, while the temporary pointer walks behind it to move and clean up the child
pointers it encounters on its way.
ListNode* getTail(ListNode* head) { // Assumes non-null argument
while (head->next) {
head = head->next;
}
return head;
}
ListNode* flatten(ListNode* head) {
ListNode *tail = getTail(head);
for (ListNode *node = head; node; node = node->next) {
tail->next = node->child;
tail = getTail(tail);
node->child = nullptr;
}
return head;
}
Unrelated to your question, but in C++:
NULL
but nullptr
struct ListNode
can just be ListNode
So:
struct ListNode {
int val;
ListNode* next = nullptr;
ListNode* child = nullptr;
ListNode(int x): val(x) {}
}
Finally, flatten
will always return the argument given to it, so you could consider making it a void function.