Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
CSC 207 (Fall 2015)—Recursive Linked Lists Recursive Linked Lists Lab This week, we’ll be discussing hierarchical structures, i.e., trees. Trees have a beautiful inductive definition, but to appreciate that definition and turn it into a data structure, we’ll need to embrace recursion. Before jumping into all of that, however, we’ll review programming with recursion. Part 1: Recursive Linked Lists It turns out that the linked lists that we explored in the last two weeks also have a beautiful inductive definition that is closely related to the definition of a tree. It is precisely the definition of list that you learned in CSC 161 with Racket. A list is either: • Nil, empty, or • Cons of some element and the rest of the list. Using this definition of a List in Java is not that different from the imperative version we studied previously. We use the same setup for a List as before: public class LinkedList<T> { private class Node { public T value; public Node next; public Node(T value, Node next) { this.value = value; this.next = next; } public Node(T value) { this(value, null); } } private Node first; } However, rather than using while-loops, we use recursion to do all of the work. Recall that with recursion, we are trying to decompose our problem into a smaller version of itself. Most commonly, when we solve a problem with recursion, we end up with two cases: • A base case describing what to do at the base of the recursion, e.g., an empty list, the integer is zero. • A recursive case describing what to do when we’re not at a base case and we must decompose the problem appropriately. Usually the recursive case consists of two parts: (1) breaking down the problem and solving those smaller sub-problems with recursive calls and (2) taking the results from those recursive calls and computing a solution for the overall problem. For example, consider recursively adding an element to the end of the list: We may solve the problem as follows: • When the list is empty, we produce a new list of size one that contains that element. • When the list is non-empty, we recursively add to the remainder of the list. 1 CSC 207 (Fall 2015)—Recursive Linked Lists In a project called Trees, in a file called LinkedList.java, use the definition of a LinkedList class above and implement the following methods using recursion: • add(v): adds an element onto the end of the list. • size(): returns the size of the list. • contains(v): returns true if the element v is in the list. • toString(): returns a string representation of the list, e.g., [0, 1, 2, 3, 4, 5]. • (Optional: remove(v): removes the first occurrence of v from the list, returning true if it is successful; false otherwise). Part 2: Trees As mentioned above, a tree has a beautiful recursive definition as well. A (binary) tree is either: • Nil, empty, or • A Leaf of some element and a left sub-tree and a right sub-tree. Pictorially, a tree looks as follows: v <left subtree> <right subtree> How does a (recursively defined) linked list differ from a tree? Using your linked list definition as a starting point, define a class called Tree that implements a binary tree data structure as defined above. Try implementing the basic container operations—add, size, contains, and toString—over your Tree data structure. What differs between the linked list and tree operations? Do you need any additional information to complete these methods? 2