CS 261 Lab #5

February 21st

Goals:

This week it's lots of practice building and modifying linked structures!

Partners

In each lab this semester you will work with a randomly assigned partner. (I'll have Zoom randomly set up breakout rooms.) Please be kind in your interactions with your partners! Keep in mind that students in this class have a range of previous programming experience, and that some have been college students for longer than others. We're all in this together, and you have something to learn from your partner, no matter who they are or what their previous experiences have been. I expect that group members will collaborate and work together on each step of the lab.

Exercise 1: Exploring Linked Structures

Start by downloading the lab project, extract its contents, and open it in BlueJ. It continues working with the code we've been discussing: BasicDie is back, as well as a version of our DieNode class — the building block for our linked lists of die instances. You'll add code to LabCode as you work through the exercises this week, and you can use some of the tools in ListBuilder to help you construct and print some sample lists, but don't look at how those methods are implemented yet — I want you to figure out some of those techniques for yourselves first.
  1. Take a look at the main method in LabCode. At the moment, it contains code that calls ListBuilder's build method to construct a three-node linked list (shown below), then calls printList to print out the contents of the list (the BasicDie instances it contains). Run the code to see what it does.

    Fig 1.1. A three-node linked list of BasicDie instances
  2. Next, let's use BlueJ's interactive features to explore a linked structure — you'll see that you can use the "inspect" functionality to reconstruct the "picture" above. Start by using BlueJ's point-and-click interface to reproduce the three-node list: Call ListBuilder's build method by clicking on the ListBuilder class. (You can pass it {6,4,8} as an input, rather than having to construct the array of die sides first.) When the result is displayed, click the "get" button to save it to the object workbench (call it "head" since it'll be a reference to the first node in the list).

    Once it's on the workbench, double-click the new DieNode object and an inspection window will open, showing the internal state of the object. Both of the fields in the DieNode are object references, so they appear as arrows, but double-clicking on those arrows brings up inspector windows for those objects as well. Inspect all of the arrows until there are no more to follow and you'll get something like the display below. (I dragged the windows around a bit so it looked more like the picture above.) The take-away here is that if you've got a linked structure, you can explore all of it via pointing and clicking.

    Fig 1.2. Inspecting structure via BlueJ
  3. Extend the code in main so that, after the three-node list is constructed and printed, the variable node2 points to the second node in the list as shown below. (You should leave the existing code the way it is, but add a new assignment statement after it such that the variable node2 refers to the second node.) Call printList again and pass it node2 and see what happens. What if you call printList on node1?

    Fig 1.3. A second pointer in the middle of the list
  4. Add even more code at the end of main so that node2 is then moved to point to the third node, as shown below. Call printList again and pass it node2 after this latest update and see what happens.

    Fig 1.4. A second pointer at the end of the list

Exercise 2: Updating Linked Structures

In the previous exercise you got some practice moving pointers around and inspecting structures. Now we'll take the next step: Rearranging the guts of existing lists. Some of these steps don't make much sense from the perspective of building or updating linked lists, but they'll be good practice.
  1. In LabCode's main method, comment out everything you added below the line that says "Add more code below this line". The goal is to go back to the initial code that built the three-node list as in Fig 1.1. (You can always grab a fresh copy of the lab project if you prefer.)

    Once you've got the three-node list being constructed and printed properly, add code at the bottom of main that turns it into the two-node list shown below. That requires setting the second node's next pointer to null. Test your change by calling printList on node1 after the change. (What used to be the third node isn't shown in the picture below, but you're not responsible for making it disappear — all you need to do is cut it loose from the list by changing the second node's next link.)

    Fig 2.1. A two-node list, created by removing the final node from Fig 1.1.
  2. Leave the code in main that you added for the previous step. Below it, add code that turns the two-node list into the one shown below by creating a new BasicDie with two sides, and storing that in the second node. (The old die is shown floating around disconnected, but it will eventually be erased and reclaimed automatically by Java's runtime system.) Print this updated list to make sure your change was correct.

    Fig 2.2. Replacing the 4-sided die with a 2-sided one
  3. Just for practice, add some additional code that creates the situation shown below, where both nodes refer to the same die instance. What should happen when you print the list? How can you tell that the die is shared, rather than having two separate two-sided Die instances?

    Fig 2.3. Two list nodes sharing a die instance

Exercise 3: Constructing Lists

Ok! After those warmup exercises, you're ready for the main event: Writing code to construct some simple linked lists. For these exercises, do not use the ListBuilder tool to make lists — I want you to do these yourselves.
  1. Write some code that constructs the linked structure shown below. You can put this in a new static method in LabCode if you want, or comment out the code in main and add your new code there. What will printList do when passed node1? Call printList to test your hypothesis.

    Fig 3.1. A short list with a "loop"
  2. Now modify the code from the previous step so that you build the following list instead:

    Fig 3.2. A two-node list
  3. Insert a list node between the two nodes from above, to produce the list shown below. Think carefully about which fields need updating, and the order of those updates, before you start writing code.

    Fig 3.3. Our old friend, back again

Exercise 4: Writing Code that Uses Lists

You knew this was coming: Now that you've mastered your list construction and manipulation skills, it's time to write some code that works with linked structures.
  1. In LabCode, write your own static printList method that works like the one I gave you. (Don't peek in ListBuilder — try to write this yourself!) It should take a DieNode reference as its argument, traverse the list, and print out each of the die objects as it goes. You should stop once you hit the null link at the end of the list, but make sure you print out the final die object before stopping.
  2. In LabCode, write a get method that takes a DieNode reference (the head of a list) and an integer index as its argument, and returns the die instance at the specified position in the list.
  3. Next, add a method lotsOfDice that takes two parameters, the number of dice required and the number of sides they should each have, and builds a linked list containing the specified number of die instances. (Each die in the list will have the same number of sides, but they should be separate instances — don't just have the list nodes all share a single die instance.)

    > DieNode head = LabCode.lotsOfDice(3,8);
    > LabCode.printList(head);
    Die has 8 sides and has rolled 0 times
    Die has 8 sides and has rolled 0 times
    Die has 8 sides and has rolled 0 times
    

Extras

If you've got extra time, consider trying the following:
  1. Write a method that counts the number of nodes in a list.
  2. Write an indexOf method that takes the head of a list and a number of sides, and returns the first die instance in the list that has the specified number of sides.
  3. Create an actual linked list class that keeps track of the head of the list, and contains methods like the printList and get methods.
  4. What's the complexity of your lotsOfDice method? Come up with a T(n) estimate of the number of computational steps required for a problem of size n. (In this case, what's the measure of the problem size?) Once you've got your T(n), give your Big-O estimate f(n) of the method's performance. Justify your answer: Find a constant c and an n0 such that T(n) <= c*f(n) for all n > n0.


Brad Richards, 2023