Download Implementing Queues: Vector vs Linked List and more Assignments Data Structures and Algorithms in PDF only on Docsity!
09- 1
Queues and List-Based ADT
Implementations
Problem Set: PS3 due Wednesday, March 7
Wellesley College CS
Lecture 09
Monday, February 26
Handout
09- 2
Overview of Today’s Lecture
1. Queues = first-in first-out (FIFO) collections.
2. Vector implementations of stacks and queues.
Efficient vector implementations of queues are
complex.
3. An elegant implementation of queues based on linked
lists.
4. Implementations of a mutable linked list abstraction.
09- 3 A Queue is a FIFO Collection “bat”
A queue is like a tube. Different ends are used to
enqueue (insert) and dequeue (remove) elements.
enqueue elements here dequeue^ elements here
The enq() method enqueues an element and deq() dequeues an element.
front() returns the element at the front of the queue without removing it
back end front end “ant” “bat” “dog” “ant” “bat” “dog” “dog” “ant” Suppose q is an empty queue q.enq(“bat”) q.enq(“ant”) q.enq(“dog”) q.deq() → “bat” q.deq() → “ant” q.deq() → “dog” 09- 4 Queue Interface
import java.util.*; // imports Iterator and Iterable
/** Interface to mutable queues with elements of type T.
A queue is a first-in-first-out collection.
WARNING: the CS230 Queue interface is different than
the java.util.Queue interface. */
public interface Queue extends Iterable {
public void enq(T elt); // Add elt to back of queue.
public T deq(); // Remove and return element from front of queue.
// Throw RuntimeExecption if queue is empty.
public T front(); // Return front element of queue without removing it.
// Throw RuntimeExecption if queue is empty.
public boolean isEmpty(); // Return true if this queue is empty; else false
public int size(); // Return the number of elements in this queue
public void clear(); // Remove all elements from this queue
public Queue copy(); // Return a shallow copy of this queue
public Iterator iterator(); // Return an iterator that yields
// this queue's elements from front to back.
09- 7 A Good Array Implementation of Queues Draw pictures from class here: 09- 8 A Good Linked List Implementation of Queues “bat” “ant” “dog” back front QueueTwoEndedLinkedList q “bat” “ant” “dog” back front QueueTwoEndedLinkedList q “cat” “ant” “dog” back front QueueTwoEndedLinkedList q “cat” q.enq(“cat”) q.deq() → “bat”
09- 9 What is an Empty Queue? back front QueueTwoEndedLinkedList “dog” back front QueueTwoEndedLinkedList
We define a queue to be empty if front contains the empty list;
what back contains is irrelevant. E.g., both of the following are empty:
This simplifies the implementation of clear(), which can just
set front to the empty list.
09- 10 Equeuing into an Empty Queue back front QueueTwoEndedLinkedList “bat” back front QueueTwoEndedLinkedList
Enqueuing an element into an empty queue is a special case:
??? (can be anything)
q.enq(“bat”);
09- 13 Linked-List Queue Code: Skeleton
public class QueueTwoEndedLinkedList implements Queue {
// Instance Variables:
private ListNode front, back; // Initially contain null by default
// Constructor Methods:
public QueueTwoEndedLinkedList() {
// front is null by default, and back is irrelevant at beginning, so do nothing.
// Instance Methods
public boolean isEmpty () {
return front == null; // By design, queue is empty if front is empty
public void clear() {
front = null; // by design, don't need to modify back.
// Remaining instance methods go here
09- 14 Linked-List Queue Code: enq() public void enq (T elt) { if (isEmpty()) { // Enqueuing onto the empty list is a special case: front = new ListNode(elt, null); back = front; // initializes back for empty front } else { back.next = new ListNode(elt, null); back = back.next; } }
09- 15 Linked-List Queue Code: deq() and front()
public T deq () {
if (isEmpty()) {
throw new RuntimeException("Attempt to deq() an empty queue");
} else {
T result = front.value;
public T front () {
if (isEmpty()) {
throw new RuntimeException("Attempt to find front() of an empty queue");
} else {
return front.value;
09- 16 Linked-List Queue Code: size() and copy() public int size() { // Calculate the length of the linked list in front. int len = 0; for (ListNode current = front; current != null; current = current.next) { len++; } return len; } public Queue copy() { QueueTwoEndedLinkedList qCopy = new QueueTwoEndedLinkedList(); if (front != null) { qCopy.front = new ListNode(front.value, null); ListNode prevCopy = qCopy.front; for (ListNode current = front.next; current != null; current = current.next) { prevCopy.next = new ListNode(current.value, null); prevCopy = prevCopy.next; } } return qCopy; }
09- 19 Linked-List Queue Code: main()
public static void main (String[] args) {
// QueueTester.test performs simple tests on any queue implementation:
QueueTester.test(new QueueTwoEndedLinkedList());
q = QueueTwoEndedLinkedList[]; q.size() = 0; q.front() = Attempt to find front() of an empty queue q.enq(B); q = QueueTwoEndedLinkedList[B]; q.size() = 1; q.front() = B q.enq(C); q = QueueTwoEndedLinkedList[B,C]; q.size() = 2; q.front() = B q.enq(B); q = QueueTwoEndedLinkedList[B,C,B]; q.size() = 3; q.front() = B q.enq(A); q = QueueTwoEndedLinkedList[B,C,B,A]; q.size() = 4; q.front() = B q.deq() = B; q = QueueTwoEndedLinkedList[C,B,A]; q.size() = 3; q.front() = C q.deq() = C; q = QueueTwoEndedLinkedList[B,A]; q.size() = 2; q.front() = B q.deq() = B; q = QueueTwoEndedLinkedList[A]; q.size() = 1; q.front() = A q.enq(D); q = QueueTwoEndedLinkedList[A,D]; q.size() = 2; q.front() = A q.enq(A); q = QueueTwoEndedLinkedList[A,D,A]; q.size() = 3; q.front() = A q.enq(C); q = QueueTwoEndedLinkedList[A,D,A,C]; q.size() = 4; q.front() = A q2 = (Queue) q.copy(); q2 = QueueTwoEndedLinkedList[A,D,A,C]; q2.size() = 4; q2.front() = A q.clear(); q = QueueTwoEndedLinkedList[]; q.size() = 0; q.front() = Attempt to find front() … q2 = QueueTwoEndedLinkedList[A,D,A,C]; q2.size() = 4; q2.front() = A Iterating the elements of q2: 0:A 1:D 2:A 3:C 09- 20 An Improved Queue: Caching the Size “bat” “ant” “dog” back front QueueTwoEndedLinkedList q “bat” “ant” “dog” back front QueueTwoEndedLinkedList q “cat” q.enq(“cat”) size 3
Can change size() from a linear-time to a constant time
operation by storing it in an instance variable. For example:
size 4
09- 21 Code Modifications for Queue with Cached Sizes
public class QueueTwoEndedLinkedListBetter implements Queue {
// Instance Variables:
private ListNode front, back; // Initially contain null by default
private int size = 0;
public int size() { return size; } // Constant time, not linear time in # of elts
public void clear() { front = null; size = 0; }
public void enq (T elt) { … usual enq() code goes here … size++; }
public void deq (T elt) { … put size--; before return result … }
public Queue copy() { … put qCopy.size = size; before return qCopy … }
// All other methods are unchanged.
09- 22 MList : A Mutable Linked-List Abstraction // Static methods from CS111 lists: public static MList empty(); public static boolean isEmpty(MList L) ; public static MList prepend(T elt, MList L) ; public static T head(MList L) ; public static MList tail(MList L) // New methods for changing head and tail public static void setHead (MList L, T newHead); public static void setTail (MList L, MList newTail) // Other static methods: public static String toString (MList L); public static boolean equals (MList L1, MList L2); public static int length (MList L) public static T nth (int n, MList L); public static MList lastNode (MList L); public static MList append(MList L1, MList L2) public static MList appendD (MList L1, MList L2); public static MList postpend(MList L, T elt) public static MList postpendD (MList L, T elt); public static MList reverse (MList L) ); public static MList copy (MList L)