CS 2223 Nov 03 2015
Expected reading: pp. 142-157
Daily Exercise: Digits summing to 100
Sample Exam Question: Swap bottom two elements on stack
Note: I updated Figure 1 after the lecture, plus fixed some typos
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
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
A Bag can be readily implemented from linked list
Maintain two pointers (first and last) to properly implement Queue data type
1.2 Opening Questions
Review the daily exercise My Solution
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
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 move around two index values, first and last which refer to the first element in the queue and the last one in the queue. Code is available in the repository.
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:
Try this for a programming exercise
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?
1.9 Lecture Takeaways
Be sure to review Rubric for homework HW1
Need practice in pointer/reference manipulation
1.10 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.11 Version : 2015/11/04
(c) 2015, George Heineman