1 A Journey of a thousand miles begins with a single step
1.1 Important concepts from readings
1.2 Opening Questions
1.3 Solve sample Stack Exam problem
1.4 Fixed Capacity Queue Implementation
1.5 Fixed Capacity Circular Queue Implementation
1.6 What is a Linked List?
1.7 Linked List offers the perfect data structure for Bag
1.8 Linked List Queue Implementation Resolves Resizing
1.9 Stack Resizing Logic
1.10 Spreadsheet For Timing Computation
1.11 Lecture Takeaways
1.12 Daily Exercise
1.13 Version : 2018/ 03/ 19

CS 2223 Mar 19 2018

Lecture Path: 05
Back Next

Expected reading: pp. 142-157
Daily Exercise: Digits summing to 100
Sample Exam Question: Swap bottom two elements on stack
Classical selection: Beethoven Symphony No. 1 (1800)
Musical Selection: She Believes In Me, Kenny Rogers (1979)

Goals are the links in the chain that connect activity to accomplishment
Tom Ziglar

1 A Journey of a thousand miles begins with a single step

1.1 Important concepts from readings

1.2 Opening Questions

1.3 Solve sample Stack Exam problem

Here is pseudocode that would be suitable for an exam solution

function swapBottomTwo (Stack s) { if size of s < 2 return Stack backup = new Stack while s is not empty backup.push(s.pop()) oldest = backup.pop() next = backup.pop() s.push(next) s.push(oldest) while backup is not empty s.push(backup.pop() }

Bonus question

How would you rate the performance of this function, assuming there were N items in the original stack? That is, your job is to count the number of push and pop operations.

1.4 Fixed Capacity Queue Implementation

Ok, this is a bit of a straw-man presentation. The point is that it is possible to implement Queues using a fixed capacity, as we did with Stacks, although the performance is not what you would want.

public class FixedCapacityQueueOfStrings { private String[] a; // holds the items private int N; // number of items in queue // create an empty queue with given capacity public FixedCapacityQueueOfStrings(int capacity) { a = new String[capacity]; N = 0; } public boolean isEmpty() { return N == 0; } public boolean isFull() { return N == a.length; } public void enqueue(String item) { a[N++] = item; } public String dequeue() { String val = a[0]; for (int i = 0; i < N; i++) { // shift all elements down one a[i] = a[i+1]; } a[−−N] = null; // clear unused and reset size return val; // value that was dequeued } }

What is the performance of the operations of this data type? Do you see opportunities for improvement?

1.5 Fixed Capacity Circular Queue Implementation

You need to be aware of an alternate implementation of a fixed capacity queue that works very well in practice, and is easy to embed in hardware devices. Yes, we cannot always assume that we have infinite storage or storage that can be dynamically allocated.

The basic concept is a circular buffer. Instead of moving the elements in the queue around, we update two index values, first and last. When the queue is not full, last tells you the index location into which an element will be enqueued. When the queue is not empty, first tells you the location of the element to be dequeued. When enqueing a value (or dequeing a value), you need to increment last (or first, respectively). Code is available in the repository. To simplify computation, keep an extra variable, N, to count the number of elements in the queue. There are ways to avoid having to keep an extra value, but they either make the code too complicated or waste one of the spaces in the array.

public class FixedCapacityQueueOfStrings { private String[] a; // holds the items private int N; // number of items in queue private int first; // start of the queue private int last; // end of the queue // create an empty queue with given capacity public CircularBufferQueueOfStrings(int capacity) { a = new String[capacity]; N = 0; } public boolean isEmpty() { return N == 0; } public boolean isFull() { return N == a.length; } public void enqueue(String item) { if (isFull()) { throw new IllegalStateException("Queue is Full."); } a[last] = item; N++; last = (last + 1) % a.length; } public String dequeue() { if (isEmpty()) { throw new IllegalStateException("Queue is Empty."); } String val = a[first]; N−−; first = (first + 1) % a.length; return val; } }

This code makes use of the modulo (%) operator, which returns the remainder of a number when divided by a given integer. Doing so allows you to easily move to the "next" position and have that wrap around as necessary.

The following small example will help explain the idea:

Figure 1: Circular Queue

Try this for a programming exercise

Given the above implementation of Circular Queue buffer, try to write the Iterator as was done on page 141 of the book.

1.6 What is a Linked List?

A linked list is a recursive data structure that is either null or a reference to a node that stores some item and has a reference to a linked list (p. 142). This is the fundamental data structure used historically for dynamic storage. In this class we are often shielded from this low-level data structure, but it is critical that you know how to work and manipulate linked lists.

1.7 Linked List offers the perfect data structure for Bag

Recall the API for Bag:

Operation

Bag

Queue

Stack

add

add(Item)

enqueue(Item)

push(Item)

remove

--

dequeue()

pop()

size

size()

size()

size()

isEmpty

isEmpty()

isEmpty()

isEmpty()

We only want to offer three operations. The following class makes use of Java Generics to construct a Bag of Item objects.

/** * @param <Item> the type of elements in the Bag */ public class Bag<Item> { Node first; // first node in the list (may be null) class Node { Item item; Node next; } public void add (Item item) { Node oldfirst = first; first = new Node(); first.item = item; first.next = oldfirst; } }

As you can see from the depiction of linked objects in the book, you can use these structures to represent dynamically growing linear lists of objects.

Page 155 of the book completes the Bag implementation, with the ability to iterate over all of the elements in the Bag. Review and ask questions as needed.

1.8 Linked List Queue Implementation Resolves Resizing

Page 151 describes how to provide constant time performance for enqueue and dequeue operations. This is done using Linked Lists to structure the information.

public class Queue<Item> { private Node<Item> first; // beginning of queue private Node<Item> last; // end of queue private int N; // number of elements on queue // helper linked list class class Node<Item> { private Item item; private Node<Item> next; } public Queue() { first = null; last = null; N = 0; } public boolean isEmpty() { return first == null; } public int size() { return N; } /** Adds the item to this queue. */ public void enqueue(Item item) { Node<Item> oldlast = last; last = new Node<Item>(); last.item = item; last.next = null; if (isEmpty()) { first = last; } else { oldlast.next = last; } N++; } }

We need to go over the implementation, but the concepts should be clear from the earlier presentation of Circular Buffer.

Under what circumstances is the first reference updated during enqueue?

Under what circumstances is the last reference updated during enqueue?

Once enqueue is considered, you need to also have dequeue method:

public Item dequeue() { if (isEmpty()) throw new NoSuchElementException("Queue underflow"); Item item = first.item; first = first.next; N−−; if (isEmpty()) last = null; // to avoid loitering return item; }

Under what circumstances is the last reference updated during dequeue?

Figure 2: Linked List Queue

1.9 Stack Resizing Logic

A question came up in class on Mar 16 2018 regarding the constants in the resizing stack. What happens when you more-than-double upon resizing? What about the shrink factor?

The problem is complicated because it is hard to come up with the "average case" for a stack. How do you balance the number of pushes versus the number of pops? They can’t be drawn from the same random distribution, because if they did, then the stack wouldn’t grow enough to warrant being resized. Somehow you need to generate behavior with "spikes" to force the resize, followed by lots of needless operations that do not affect the size, followed by a sudden removal of a number of elements via pop.

1.10 Spreadsheet For Timing Computation

If time permits, I will discuss/share a spreadsheet that I created when evaluating the timing of algs.days.Day04.DoublingTest, introduced on Mar 16 2018.

Sample spreadsheet

1.11 Lecture Takeaways

1.12 Daily Exercise

See if you can do this one. You are given a reference to the first node in a linked list, L, that contains N elements. You are to only traverse the linked list ONCE. The result is to split L into two lists, L and back, where L is updated to only contain the first floor(N/2) elements (in their original order) and back contains the remaining N - floor(N/2) elements (in their original order).

To be more precise, do this operation within the context of Bag

public class Bag<Item> { /** This operation will return a new Bag that contains the remaining N - floor(N/2) elements while modifying the current bag to retain only the first floor(N/2) elements. */ public Bag cutInHalf() { } }

1.13 Version : 2018/03/19

(c) 2018, George Heineman