class 2 recursion on lists - university of birmingham€¦ · class 2 – recursion on lists the...
TRANSCRIPT
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 1
Class 2 – Recursion on Lists
The list data type
Recursive methods on lists
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 2
Data structures
Contain large amounts of data
Allow for access to that data
Many different data structures, allowing
efficiency for various operations; more from
your data structures module.
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 3
Lists
Lists represent a simple data structure in
which data are stored in a row.
We will talk first about lists of integers:
x0, x1, ..., xn-1 (n >= 0)
Elements can be added and removed only at
the beginning (x0).
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 4
Lists (cont.)
Terminology:
First element (x0) is head of the list
List of remaining elements (x1, ..., xn-1) is tail of
the list
Adding a new element to the front is called
consing (for “constructing”)
The empty list is the list with no elements (n=0)
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 5
List operations
We assume a type List, for variables that contain such lists.
The List type will be defined by a class that also provides the following static methods: List cons (int i, List L)
- construct a list containing i at the front
List empty()
- the empty (zero-element) list
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 6
List operations (cont.)
List objects have the following instance
methods:
int getHead() – return the head of the list
List getTail() - return the tail of the list
boolean isEmpty()- is the list empty?
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 7
Examples of list operations
Suppose L is the list 2, 3, 5, 7
cons(2,cons(3,cons(5,cons(7,empty())))) )
L.getHead() returns 2
L.getTail() returns the list 3, 5, 7
L.isEmpty() returns false
cons(13, L) returns the list 13, 2, 3, 5, 7
cons(13, L.getTail()) returns the list
13, 3, 5, 7
L.getTail().getTail() returns the list 5, 7
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 8
Example
Assume we have defined the class List as
described above.
public static void main (String[] args) {
List L = List.empty();
L = List.cons(5, L);
L = List.cons(10, L);
System.out.println(L.getHead());
System.out.println(L.getTail().getHead());}
To avoid having to write “List.empty” and “List.cons”, use
import static List.empty;
import static List.cons;
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 9
Recursion on lists
Writing recursive methods on lists follows
same principle as for integers:
To compute f(L), assume f(L’) can be calculated
for lists L’ smaller than L, and use f(L’) to
calculate f(L).
Some lists are small enough for f to be calculated
directly
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 10
Example: printing lists
Print all the elements in a list, one per line:
Assume you can print the tail of L (i.e.
printList(L.getTail())), so how do you print L?
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 11
Example: printing lists (cont.)
static void printList (List L) {
if (L.isEmpty())
return;
else {
System.out.println(L.getHead());
printList(L.getTail());
}
}
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 12
Example: printing lists (cont.)
Print all the elements in a list, all on one line,
separated by a comma and a space:
Assume you can print the tail of L (i.e.
printList(L.getTail())), so how do you print
L?
Answer: print L.getHead(), followed by a
comma and space, but only if the tail of L is non-
empty.
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 13
Example: printing lists (cont.)
static void printList (List L) {
if (L.isEmpty()) return;
else if (L.getTail().isEmpty()))
System.out.print(L.getHead());
else {
System.out.print(L.getHead());
System.out.print(“, “);
printList(L.getTail());
}
}
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 14
Example: addtoend
The method
can be defined recursively. It adds the integer i at the end of the list L. For example, if L is the list 3, 5, 7, then addtoend(L, 2) returns the list 3, 5, 7, 2.
We will see how to define addtoend later, but we can use it for ...
List addtoend (List L, int i)
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 15
Example: reading integers
Given integer n, read n integers from the user
and place them in a list.
Assume you can read n-1 integers and place them in
a list; how can you read n integers?
Answer: read the n-1 integers, creating a list L, then
read one more integer and place it at the end of L.
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 16
Example: reading integers (cont.)
static List readList (int n) {
if (n == 0)
return List.empty();
else {
List L = readList(n-1);
int i = Keyboard.readInt();
return addtoend(L, i);
}
}
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 17
Recursion on lists (review)
Writing recursive methods on lists follows the
same principle as for integers:
To compute f(L), assume f(L’) can be calculated
for lists L’ smaller than L, and use f(L’) to
calculate f(L).
Some lists are small enough for f to be calculated
directly
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 18
Example: joining lists
Given lists L and M, create a list consisting of
the elements of L followed by the elements
of M.
Assume you can append the tail of L and M (i.e.
append(L.getTail(), M)); how can you
append L and M?
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 19
Example: joining lists (cont.)
static List append (List L, List M)
{
if (L.isEmpty())
return M;
else
return cons(L.getHead(),
append(L.getTail(), M));
}
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 20
Example: addtoend
Given list L and integer i, construct a list that
contains the elements of L with i at the end.
static List addtoend(List L, int i) {
return append(L,
cons(i, empty()));
}
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 21
Example: finding maximum element
Find the maximum integer in a non-empty list.
Assume you can find the maximum element of
the tail (if the tail is non-empty)
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 22
Example: finding maximum element (cont.)
static int max (List L) {
if ((L.getTail().isEmpty()))
return L.getHead();
else {
int m = max(L.getTail());
return (L.getHead() > m) ?
L.getHead(): m;
}
}
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 23
Example: list reversal
Given list L, construct the reversal of L.
Assume you can reverse the tail of L. How can
you place the head of L in the reversal of the
tail of L so as to get the reversal of L itself?
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 24
Example: list reversal (cont.)
static List reverse (List L) {
if (L.isEmpty())
return L;
else
return addtoend(reverse(L.getTail()),
L.getHead());
}
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 25
Analysis of list reversal
reverse is rather slow. Consider how many
method calls it makes in relation to the size
of its argument. Note that addtoend(L)
calls itself recursively n times, where n is
the length of L. How many method calls
does reverse(L) make?
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 26
Analysis of list reversal (cont.)
After recursive call to itself, it calls addtoend, on a list of length n-1. So this makes n-1 calls to addtoend.
Now consider the first recursive call. It in turn has a recursive call; after that inner recursive call, there is a call to addtoend, which entails n-2 calls to addtoend.
Continuing in this way, there is a call to addtoend that involves n-3 calls, one involving n-4 calls, etc.
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 27
Analysis of list reversal (cont.)
Thus, the total number of calls to addtoend is
(n-1) + (n-2) + … + 1,
which equals n(n-1)/2 n2. This is a much
larger number than n itself.
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 28
Faster list reversal
To avoid the quadratic cost of reversal, we can define another version, which we will call rev (just to distinguish it). The important trick is that rev uses an auxiliary (helper) method rev1 with type
List rev1 (List L, List M)
rev1(L,M) is defined to return the reversal of L appended to M.
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 29
Faster list reversal (cont.)
rev1 can be defined recursively. The key observation is this one:
Placing the reversal of L onto the front of M is the same thing as placing the reversal of the tail of L onto cons(getHead(L), M).
For example, if we want to place the reversal of
1,2,3 onto the front of list 4,5 - obtaining
3,2,1,4,5 - we can just as well place the reversal
of 2,3 onto the front of 1,4,5.
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 30
Faster list reversal (cont.)
static List rev1 (List L, List M) {
if (L.isEmpty())
return M;
else
return rev1(L.getTail(),
List.cons(L.getHead(), M));
}
This observation leads to:
Note that this requires only n method calls, where n is the
length of L.
2014-15 MSc Workshop © S.Kamin, U.S.Reddy 1B - Lists - 31
Faster list reversal (cont.)
We can easily program rev once we’ve got
rev1.
static List rev (List L) {
return rev1(L, List.empty);
}