APCS Problem Set 4b: Algorithms and Basic Flow Control

Michael Ferraro
mferraro@balstaff.org
Balboa High School
1 October 2015


Contents

(Re)introduction

Due Date

This portion of the problem set -- PS #4b -- is tentatively due before 5th Period on 14 October 2015.

5 Recursion

5.1 Required Reading

$\rightarrow$ Litvin & Litvin, Java Methods A & AB: §§13.1$-$13.2

5.2 Recursive Addition

Recursion is one of the most powerful ideas in Computer Science. In a nutshell, it is the practice of solving a problem by breaking the problem into simpler subproblems. Each subproblem resembles the original, larger problem.

Consider this simple arithmetic expression:

\begin{displaymath}52 + 118 + 917 + 36 + 661 + 98 \end{displaymath}

In Java, such an expresssion can be evaluated and stored in a variable in this manner:
    int sum = 52 + 118 + 917 + 36 + 661 + 98;
In this case, Java runs its own arithmetic evaluator over the original arithmetic expression. What we will do here is develop a way to evaluate a sum using a recursive algorithm.

Recall that a recursive solution breaks a problem into simpler subproblems, each one resembling the original. You can think of the problem recursively in these terms:

\fbox{
\begin{minipage}{3.25in}
The sum of the elements in the list is the {\bf ...
...
\end{enumerate}{\em Note: The {\bf sum of} an empty list is 0.}
\end{minipage}}


Here is pseudocode describing the process:

Input: LIST, a list of integers to be added together

If LIST is empty:
    return 0

sum $\leftarrow$ (first element in LIST) + (result of running this algorithm on rest of LIST)

Output: sum


If we run this algorithm over our sum from earlier, you will notice below that the sum is effectively calculated from right to left -- opposite of our iterative approach from §3.3 of PS4a.

  1. $ \fbox{52 + 118 + 917 + 36 + 661 + 98}$
  2. $ \fbox{52 + \fbox{118 + 917 + 36 + 661 + 98}} $
  3. $ \fbox{52 + \fbox{118 + \fbox{917 + 36 + 661 + 98}}} $
  4. $ \fbox{52 + \fbox{118 + \fbox{917 + \fbox{36 + 661 + 98}}}} $
  5. $ \fbox{52 + \fbox{118 + \fbox{917 + \fbox{36 + \fbox{661 + 98}}}}} $
  6. $ \fbox{52 + \fbox{118 + \fbox{917 + \fbox{36 + \fbox{661 + \fbox{98}}}}}} $
  7. $ \fbox{52 + \fbox{118 + \fbox{917 + \fbox{36 + \fbox{661 + \fbox{98 + \fbox{\rule{.1in}{0pt}\rule{0pt}{.1in}}}}}}}} $
  8. $ \fbox{52 + \fbox{118 + \fbox{917 + \fbox{36 + \fbox{661 + \fbox{98 + \fbox{{\bf 0}}}}}}}} $
  9. $ \fbox{52 + \fbox{118 + \fbox{917 + \fbox{36 + \fbox{661 + \fbox{{\bf 98}}}}}}} $
  10. $ \fbox{52 + \fbox{118 + \fbox{917 + \fbox{36 + \fbox{{\bf 759}}}}}} $
  11. $ \fbox{52 + \fbox{118 + \fbox{917 + \fbox{{\bf 795}}}}} $
  12. $ \fbox{52 + \fbox{118 + \fbox{{\bf 1712}}}} $
  13. $ \fbox{52 + \fbox{{\bf 1830}}} $
  14. $ \fbox{{\bf 1882}} $

If our pseudocode were implemented in Java as a method called sumOf(), calling that method with our list of numbers from earlier would resemble

sumOf( \fbox{52, 118, 917, 36, 661, 98} ),
returning this:
52 + sumOf( \fbox{118, 917, 36, 661, 98} ).
Of course, Java will have to evaluate the remaining sumOf() method call first. Here is the entire execution:
sumOf( \fbox{52, 118, 917, 36, 661, 98} )

52 + sumOf( \fbox{118, 917, 36, 661, 98} )

52 + ( 118 + sumOf( \fbox{917, 36, 661, 98} ) )

52 + ( 118 + ( 917 + sumOf( \fbox{36, 661, 98} ) ) )

52 + ( 118 + ( 917 + ( 36 + sumOf( \fbox{661, 98} ) ) ) )

52 + ( 118 + ( 917 + ( 36 + ( 661 + sumOf( \fbox{98} ) ) ) ) )

52 + ( 118 + ( 917 + ( 36 + ( 661 + ( 98 + sumOf() ) ) ) ) )

52 + ( 118 + ( 917 + ( 36 + ( 661 + ( 98 + 0 ) ) ) ) )

52 + ( 118 + ( 917 + ( 36 + ( 661 + 98 ) ) ) )

52 + ( 118 + ( 917 + ( 36 + 759 ) ) )

52 + ( 118 + ( 917 + 795 ) )

52 + ( 118 + 1712 )

52 + 1830

1882

Notice the widest printed line above? That execution occurs just as this recursive procedure is using the most memory. In fact, that's a key weakness of recursive procedures: They tend to use more memory than their iterative counterparts.

The key features of a recursive procedure are

  1. A call to itself when it runs (e.g., sumOf() calls itself with a shorter list each time); and
  2. A base case, which is responsible for stopping the recursion.
The base case in our sumOf() procedure happens when it checks to see if LIST is empty. See the bold text in the execution? That is the base case: The call to sumOf() with no arguments always evaluates to (or returns) 0.

What would a Java implementation of the recursive algorithm look like? Consider this program:1

    import java.util.ArrayList;
    
    public class SumOfRec {
        
        public static int sumOf(ArrayList<Integer> list) {
            if ( list.isEmpty() ) { //base case
                return 0;
            }
            
            //strip off first element
            int firstElt = list.remove(0); 
            
            //recursive call to sumOf()
            return firstElt + sumOf(list);
        }
        
        public static void main(String[] args) {
            
            // create a list of integers to be added together
            ArrayList<Integer> myList = new ArrayList<Integer>();
            myList.add(52);    //first element
            myList.add(118);
            myList.add(917);
            myList.add(36);
            myList.add(661);
            myList.add(98);    //last element
            
            int sum = sumOf(myList);

            System.out.print("The sum of the elements in " + myList 
              + " is " + sum + ".");
        }
    }

5.3 Necessity of the Base Case

Download the code for SumOfRec.java and import it into a new project called ps4b in workspace0. Note that there is no separate driver class; the main() is included in the sumOfRec class itself.

Run the class. Once you verify that it's working, comment out the base case by putting the multi-line comment tags around it:

    /*
    if ( list.isEmpty() ) { //base case
        return 0;
    }
    */
  1. What exception is generated when you run the class now? Specify the name of the exception, not the reason for it (which is the next question).
    #INPUT:IOOB1#
  2. Why is that exception generated?
    #INPUT:IOOB2#

5.4 A Recursive mysterySum()

In the space below, show the recursive evaluation of mysterySum(5) from Ch. 13, #2. Put a triangle ($\Delta$) around the final result that would be returned.
#INPUT:trace_mysterySum#

6 Operating on Lists

6.1 Required Reading

$\rightarrow$ Litvin & Litvin, Java Methods A & AB (1st Ed.): PDF version of §4.6 -- download from http://feromax.com/apcs/problemsets/PS04b/downloads/protected/

6.2 Questions about Lists

  1. In programming, what is a list?
    #INPUT:define_list#
  2. Define list traversal.
    #INPUT:define_list_traversal#
  3. Sequential Search: If you have a list of 600 unique integers (i.e., no repeats) in random order, on average how many elements will you need to search through sequentially, starting from the 0$^{th}$ element, moving up toward the 599$^{th}$, before finding the number you set out to find?
    #INPUT:sequential_search_600ints#
  4. Now imagine that the integers from the last question are sorted from least to greatest.
    (a)
    What is the name of a method that would probably improve your chances of finding a desired value more quickly? (Hint: Refer to figure 4-10 in Litvin 1st edition, §4.6.)
    #INPUT:improved_method_binary_search#
    (b)
    Explain how the method you named above works.
    #INPUT:explain_binary_search#

7 File Manager: Counting Files in a Filesystem

7.1 Overview

A file manager, in modern terms, is a GUI application that gives you a view on a filesystem's directory structure, typically allowing you to add, remove (delete), rename, and open files and directories.

All of the directories (folders) on a computer can be thought of as a tree with a root, branches, subbranches, sub-subbranches, etc. A directory may contain subdirectories and/or files. All directories have the special entries ``.'' and ``..'' except the root directory, which is missing the ``..'' entry. (Why do you think the root directory is missing ``..''?)

Consider this problem: You want to write a program that represents a filesystem consisting of directories and files. Once you've represented a directory tree in your program, how could you go about determining the entire number of files contained in a directory -- including the files contained in the subdirectories? (That directory could be the root directory, allowing you to find the total number of files in the entire filesystem.) A similar problem could be how to find the total space taken up by a directory and all its subdirectories.

7.2 Required Reading

$\rightarrow$ Litvin & Litvin, Java Methods A & AB: §13.4 -- read the notes below to help you understand the reading.

Notes:

7.3 Download Sources

Download and import these files into the ps4b project in Eclipse:

7.4 Class Hierarchy and Polymorphism

In the reading (Litvin §13.4), you saw a diagram showing the class hierarchy for this program, where FilingItem is a superclass of both File and Folder. When you create an instance of type File, that instance (object) IS-A FilingItem, meaning that you can refer to it as a FilingItem object. The same relationship is true for Folders: An instance of Folder IS-A FilingItem, too.

If you look at the source for the Folder class, you'll notice this line:

    private ArrayList<FilingItem> items = new ArrayList<FilingItem>();
Every folder will have a collection of items stored within it, a collection of FilingItems. These items might be Files or Folders, but they're all considered to be FilingItems. Hence, we will simply have an ArrayList populated with FilingItems.

Look at the fileCount() method from the Folder class:

    public int fileCount() {
        int count = 0;

        for ( FilingItem curItem : items ) {
            count = count + curItem.fileCount();
        }

        return count;
    }

fileCount() looks through the list of items contained within the Folder, which could be a combination of sub-Folders and Files. For each item, its fileCount() method is called. But since the item might be a Folder or a File, how does Java know which fileCount() method to call: The one from Folder.java, File.java, or even FilingItem.java? The answer is that it will call the most specific version available that applies to the current object.

For example, if the object is a FilingItem, but more specifically a File, Java will see if there is a fileCount() method available for use in the File class. If not, Java uses the version of the method available in the FilingItem class. This feature is called polymorphism, which we will explore in greater detail later.

Try running the FileManager class. Then look through the class' main() to see what it's actually doing.

  1. When you call fileCount() on a Folder object, and that object has at least one item contained within it, is this an example of recursion or iteration? For credit, you must explain why you chose either answer.
    #INPUT:fileCount_iterative_or_recursive#

  2. Create this file and the necessary folder(s) to contain it:
        /home/student/Documents/ps1.doc
    
    In order to do that, you'll need to create 1 additional folder, a file, and then make sure the file and folder are properly connected to each other and the existing file system provided to you.

    Add code to main() in FileManager.java to create /home/student/Documents/ps1.doc. Once you're convinced it's working properly, copy-and-paste the code you added below.
    #INPUT:create_ps1_doc#

8 Wrap-Up

8.1 Required Reading

$\rightarrow$ Litvin & Litvin, Java Methods A & AB: §13.5

8.2 Book Questions

  1. Adapted from a Litvin 1st edition problem:
    (a)
    Suppose you have a method printStars, such that printStars(n) prints n stars on one line. For example, the statement

            printStars(5);

    displays the line

            *****

    Using the printStars method, write an iterative method printTriangle() so that printTriangle(n) prints a triangle with one star in the first row, two stars in the second row, and so on, up to n stars in the last row. For example, the statement

            printTriangle(5)

    should display

          *
          **
          ***
          ****
          *****


    Hint: You would like your method, if called with an argument of, for example, 4 -- as in printTriangle(4) -- to make calls to printStars() in this way:
        printStars(1);
        printStars(2);
        printStars(3);
        printStars(4);
    
    #INPUT:old_ch4_8a#

    (b)
    Extra credit: Write a recursive version of the printTriangle() method, without iterations.
    #INPUT:old_ch4_8b-bonus#

    (c)
    Extra credit: Modify the recursive version of printTriangle(), so that printTriangle(n) displays

          *****
          ****
          ***
          **
          *


    #INPUT:old_ch4_8c-bonus#

  2. Adapted from a Litvin 1st edition problem:
    Let's pretend for a moment that Java does not support multplcation. Write a recursive version of the following method:

        // Returns the product of a and b
        // Precondition:  a>=0, b>=0
        public int product(int a, int b)
        {
            ...
        }
    
    #INPUT:old_ch4_10#
  3. Ch. 13, #12a. Show your scratch work.
    #INPUT:ch13_12a#



Footnotes

... program:1
SumOfRec.java is available here: http://feromax.com/apcs/problemsets/PS04b/downloads/


Michael Ferraro 2015-09-30