Suppose LIST is a variable of type Node* in our
program, and suppose that it points to the list 87, 42, 53, 4 as illustrated in
the following picture:
LIST is an pointer of type Node* and therefore we
say that it represents a list.
next-field of the node with data-value 87 (colored blue
above) also represents a list.
LIST->next is of type Node*.
LIST->next represents a list containing 42, 53, 4, i.e.
everything but the first element of the list that LIST is.
LIST represents = (the first data, i.e.,
LIST->data) + (the list that LIST->next represents)
|
Base case First we have to answer the question, "What should happen in the base case?" Another way of putting that is, "What should happen when we try to print an empty list?" And the answer is, nothing! So we can leave that part blank in the code.
Recursive case That's easy too — you print out the node's data field in step 1! |
|
void printlist(Node* L) {
if (L == NULL) {
// printing an empty list - nothing to do here!
} else {
cout << L->data << " ";
printlist(L->next);
}
}
See what I mean when I said that recursion can be really "natural" with linked
lists?
Node* add2back(int d, Node* L);
As with add2front() the function returns the pointer to the head
(first) node of the updated list.
add2back(d, L) as follows:
d.
next to point
to the new node.
Node* add2back(int d, Node* L)
{
if (L == NULL)
{
return new Node{d, NULL};
}
else
{
// non-empty L case
Node* last = L;
while(last->next != NULL)
last = last->next;
// update last->next
last->next = new Node{d, NULL};
// The head of the list didn't change
return L;
}
}
return new Node{d, NULL};
return new Node{d, NULL};
is a cool way to create, initialize, and return a node.
This means the following:
Node* T = new Node;
T->data = d;
T->next = NULL;
return T;
L is actually the same as adding to the
back of list L->next Moreover, if L is empty, you are
creating a new list containing a node with value d.
Node* add2back(int d, Node* L)
{
// base case:
// L is an empty list
// Note: if L is lastnode->next, L will also be NULL
if (L == NULL)
return new Node{d, NULL};
// recursive case:
// Tell the child list to add a node
// Make sure that L->next is correct (especially when L is the lastnode)
L->next = add2back(d, L->next);
return L;
}
next pointer in the first node, which has already been deleted,
will be used; it will crash your program).
void deletelist(Node* L)
{
// Base case: if the list is empty, there is nothing to delete
if (L == NULL)
return;
// Use recursion to delete all the second-to-last nodes
deletelist(L->next);
delete L; // Then delete the first node
}
Let me again emphasize that the purpose of recursion is not just to have
shorter, more "elegant" functions (although that's nice), it's to get
comfortable with multiple ways of programming so that you have more options
available when solving a new problem.
Sometimes recursion seems "better" for the problem at hand, and sometimes a loop is a more natural solution. But remember that, since you're more used to writing loops now, you won't get a good feel for this until you've written many small recursive functions.
int length(Node* );
Solution (drag your mouse):
int length(Node* L)
{
// BASE CASE
if (L == 0)
return 0;
// RECURSIVE CASE
return 1 + length(L->next);
}
bool inorder(Node* );
For example, if the list looks like
10 20 30 40 50The function will return true. On the other hand, if the list looks like
10 20 30 50 40The function will return false.
string's (use recursion).
string max(Node* );
Solution (drag your mouse):
string max(Node* L)
{
// base case: empty list or 1 node
if( L == NULL )
return "";
else if (L->next == NULL)
return L->data;
// recursive case
string maxC = max(L->next); // maximum from the child list
if( L->data > maxC )
return L->data;
else
return maxC;
}