Reputation: 5986
I am looking at the following algorithm to flatten a tree-like linked list system from the book "Programming Interviews Exposed":
void flattenList( Node *head, Node **tail)
Node *curNode = head;
while( curNode ){
/* The current node has a child */
if( curNode->child ){
append( curNode->child, tail );
}
curNode = curNode->next;
}
}
/* Appends the child list to the end of the tail and updates
* the tail.
*/
void append( Node *child, Node **tail ){
Node *curNode;
/* Append the child child list to the end */
(*tail)->next = child;
child->prev = *tail;
/*Find the new tail, which is the end of the child child
*list.
*/
for( curNode = child; curNode->next;
curNode = curNode->next )
; /* Body intentionally empty */
/* Update the tail pointer now that curNode is the new
* tail.
*/
*tail = curNode;
}
I understand that we are passing **tail
instead of *tail
into the append
function to make sure that changes to *tail
persist, but what I don't understand is why we don't do the same for child (meaning why don't we pass **child
instead of *child
)?
Upvotes: 2
Views: 317
Reputation: 11
struct node *flatten_nr(struct node *root)
{
struct node *temp1, *temp2;
if (NULL == root) return NULL;
temp2 = root;
while (root != NULL)
{
if( root->next != NULL)
{
temp1 = root;
/* go to the end of the list and append */
while (temp1->down != NULL)
{
temp1 = temp1->down;
}
temp1->down = root->next;
}
root = root->down;
}
return temp2;
}
Upvotes: 0
Reputation: 66244
To immediately answer your question. the child
members of a Node
are never updated. They're only used to walk the underlying child list.
And honestly, the choice of parameter names (child
) could not have been more dreadful (short of calling it parent
or some such). See if the following is any clearer at all. It exploits the fact that C is a byval-call system, and actually uses one of the parameters (the node pointer) to find the end of a list starting at a given node without any temp pointer (we use the node pointer itself for the walk, since it is byval):
void append( Node *node, Node **tail )
{
/* Append the child child list to the end */
(*tail)->next = node;
node->prev = *tail;
/*Find the new tail, which is the end of the child child
*list.
*/
while (node->next)
node = node->next;
/* Update the tail pointer now that curNode is the new
* tail.
*/
*tail = node;
}
I should node, this is not very good code anyway. it makes no checks against NULL for any of the parameters, etc. This should be significantly hardened. I chose to forego the error checking and non-null validation only because the original code left all that out as well. If you desire a significantly more robust version let me know.
Upvotes: 1
Reputation: 43788
C uses a "call by value" parameter passing semantics. This means, if you call a function like f(x)
, only the value of x
is passed to the function, not the variable x
itself. When you assign a value to the parameter inside the function the x
used to call the function does not change.
If you want to be able to change the variable itself, you must pass the address like in this call f(&x)
if you then make an assignment to *x
inside the function you have changed the variable x
.
The flattenList
is declared as it is because head
is passed by value (it does not change) but tail
is passed with its address because the function has to change it.
This is similar to the append
function. The first parameter does not change while the second one must be changed inside the function.
Upvotes: 1
Reputation: 15768
The comments in append
state it quite clearly
/* Update the tail pointer now that curNode is the new
* tail.
*/
*tail = curNode;
Because you are updating the tail pointer itself (and not just the node it refers to), you need to pass pointer to a pointer
in order for the update to be visible outside the function.
For the child
pointer, we are only updating the node it refers to, not the pointer itself. Thus there is no reason to complicate the code any further by adding another level of indirection.
Upvotes: 0