Class 2 Recursion on Lists r The list

  • Slides: 31
Download presentation
Class 2 – Recursion on Lists r The list data type r Recursive methods

Class 2 – Recursion on Lists r The list data type r Recursive methods on lists 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 1

Data structures r Contain large amounts of data r Allow for access to that

Data structures r Contain large amounts of data r Allow for access to that data r Many different data structures, allowing efficiency for various operations; more from your data structures module. 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 2

Lists represent a simple data structure in which data are stored in a row.

Lists represent a simple data structure in which data are stored in a row. We will talk first about lists of integers: x 0, x 1, . . . , xn-1 (n >= 0) Elements can be added and removed only at the beginning (x 0). 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 3

Lists (cont. ) Terminology: m First element (x 0) is head of the list

Lists (cont. ) Terminology: m First element (x 0) is head of the list m List of remaining elements (x 1, . . . , xn-1) is tail of the list m Adding a new element to the front is called consing (for “constructing”) m The empty list is the list with no elements (n=0) 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 4

List operations r We assume a type List, for variables that contain such lists.

List operations r We assume a type List, for variables that contain such lists. r The List type will be defined by a class that also provides the following static methods: m List cons (int i, List L) - construct a list containing i at the front m List empty() - the empty (zero-element) list 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 5

List operations (cont. ) objects have the following instance methods: r List m int

List operations (cont. ) objects have the following instance methods: r List m int get. Head() – return the head of the list m List get. Tail() - return the tail of the list m boolean is. Empty()- is the list empty? 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 6

Examples of list operations Suppose L is the list 2, 3, 5, 7 cons(2,

Examples of list operations Suppose L is the list 2, 3, 5, 7 cons(2, cons(3, cons(5, cons(7, empty())))) ) m L. get. Head() returns 2 m L. get. Tail() returns the list 3, 5, 7 m L. is. Empty() returns false m cons(13, L) returns the list 13, 2, 3, 5, 7 m cons(13, L. get. Tail()) returns the list 13, 3, 5, 7 m L. get. Tail() returns the list 5, 7 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 7

Example Assume we have defined the class List as described above. public static void

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. get. Head()); System. out. println(L. get. Tail(). get. Head()); } To avoid having to write “List. empty” and “List. cons”, use import static List. empty; import static List. cons; 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 8

Recursion on lists Writing recursive methods on lists follows same principle as for integers:

Recursion on lists Writing recursive methods on lists follows same principle as for integers: m To compute f(L), r assume that f(L’) can be calculated for lists L’ smaller than L, and r use f(L’) to calculate f(L). m Some lists are small enough for f to be calculated directly (base cases) 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 9

Example: printing lists Print all the elements in a list, one per line: m

Example: printing lists Print all the elements in a list, one per line: m Assume you can print the tail of L (i. e. print. List(L. get. Tail())). m So how do you print L? 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 10

Example: printing lists (cont. ) static void print. List (List L) { if (L.

Example: printing lists (cont. ) static void print. List (List L) { if (L. is. Empty()) return; else { System. out. println(L. get. Head()); print. List(L. get. Tail()); } } 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 11

Example: printing lists (cont. ) Print all the elements in a list, all on

Example: printing lists (cont. ) Print all the elements in a list, all on one line, separated by a comma and a space: m Assume you can print the tail of L (i. e. print. List(L. get. Tail())). m So how do you print L? m Answer: print L. get. Head(), followed by a comma and space, but only if the tail of L is nonempty. 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 12

Example: printing lists (cont. ) static void print. List (List L) { if (L.

Example: printing lists (cont. ) static void print. List (List L) { if (L. is. Empty()) return; else if (L. get. Tail(). is. Empty())) System. out. print(L. get. Head()); else { System. out. print(L. get. Head()); System. out. print(“, “); print. List(L. get. Tail()); } } 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 13

Example: addtoend The method List addtoend (List L, int i) can be defined recursively.

Example: addtoend The method List addtoend (List L, int i) 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. . . 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 14

Example: reading integers r Given integer n, read n integers from the user and

Example: reading integers r Given integer n, read n integers from the user and place them in a list. m Assume you can read n-1 integers and place them in a list; how can you read n integers? m Answer: read the n-1 integers, creating a list L, then read one more integer and place it at the end of L. 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 15

Example: reading integers (cont. ) static List read. List (int n) { if (n

Example: reading integers (cont. ) static List read. List (int n) { if (n == 0) return List. empty(); else { List L = read. List(n-1); int i = Keyboard. read. Int(); return addtoend(L, i); } } 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 16

Recursion on lists (review) Writing recursive methods on lists follows the same principle as

Recursion on lists (review) Writing recursive methods on lists follows the same principle as for integers: m To compute f(L), r assume f(L’) can be calculated for lists L’ smaller than L, and r use f(L’) to calculate f(L). m Some lists are small enough for f to be calculated directly 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 17

Example: joining lists Given lists L and M, create a list consisting of the

Example: joining lists Given lists L and M, create a list consisting of the elements of L followed by the elements of M. m Assume you can append the tail of L and M (i. e. append(L. get. Tail(), M)); how can you append L and M? 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 18

Example: joining lists (cont. ) static List append (List L, List M) { if

Example: joining lists (cont. ) static List append (List L, List M) { if (L. is. Empty()) return M; else return cons(L. get. Head(), append(L. get. Tail(), M)); } 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 19

Example: addtoend Given list L and integer i, construct a list that contains the

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())); } 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 20

Example: finding maximum element Find the maximum integer in a non-empty list. m Assume

Example: finding maximum element Find the maximum integer in a non-empty list. m Assume you can find the maximum element of the tail (if the tail is non-empty) 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 21

Example: finding maximum element (cont. ) static int max (List L) { if ((L.

Example: finding maximum element (cont. ) static int max (List L) { if ((L. get. Tail(). is. Empty())) return L. get. Head(); else { int m = max(L. get. Tail()); return (L. get. Head() > m) ? L. get. Head(): m; } } 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 22

Example: list reversal Given list L, construct the reversal of L. m Assume you

Example: list reversal Given list L, construct the reversal of L. m Assume you can reverse the tail of L. m 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? 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 23

Example: list reversal (cont. ) static List reverse (List L) { if (L. is.

Example: list reversal (cont. ) static List reverse (List L) { if (L. is. Empty()) return L; else return addtoend(reverse(L. get. Tail()), L. get. Head()); } 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 24

Analysis of list reversal reverse is rather slow. Consider how many method calls it

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? 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 25

Analysis of list reversal (cont. ) m After recursive call to itself, it calls

Analysis of list reversal (cont. ) m After recursive call to itself, it calls addtoend, on a list of length n-1. So this makes n-1 calls to addtoend. m 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. m Continuing in this way, there is a call to addtoend that involves n-3 calls, one involving n-4 calls, and so on… 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 26

Analysis of list reversal (cont. ) Thus, the total number of calls to addtoend

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 n 2. This is a much larger number than n itself. We say that reverse takes quadratic time, i. e. , time proportional to n 2. 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 27

Faster list reversal To avoid the quadratic cost of reversal, we can define another

Faster list reversal To avoid the quadratic cost of reversal, we can define another version, which we will call rev (just to distinguish it from the original one). The important trick is that rev uses an auxiliary method (“helper”) rev 1 with type List rev 1 (List L, List M) rev 1(L, M) is defined to return the reversal of L appended to M. 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 28

Faster list reversal (cont. ) rev 1 can be defined recursively. The key observation

Faster list reversal (cont. ) rev 1 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(get. Head(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. 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 29

Faster list reversal (cont. ) This observation leads to: static List rev 1 (List

Faster list reversal (cont. ) This observation leads to: static List rev 1 (List L, List M) { if (L. is. Empty()) return M; else return rev 1(L. get. Tail(), List. cons(L. get. Head(), M)); } Note that this requires only n method calls, where n is the length of L. 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 30

Faster list reversal (cont. ) We can easily program rev once we’ve got rev

Faster list reversal (cont. ) We can easily program rev once we’ve got rev 1. static List rev (List L) { return rev 1(L, List. empty()); } 2016 -17 MSc Workshop © S. Kamin, U. S. Reddy 1 B - Lists - 31