CSCI1933 Lecture10
CSCI1933 Lecture10
- Queues
- Queue functionality
- Linked list queue implementation
- Array-based queue implementation
- Variations on queues
Linked list:
• List is organized by keeping track of start (or end) only
• Each element of the list knows about the next element in the list
Comparison of array and linked list List
implementations
• Which is better?
– Space?
• Array List: wastes memory for each unfilled
slot in the array
• Linked List: only create one node per element,
BUT requires memory overhead of links
Comparison of array and linked list List
implementations
• Which is better?
Assume N elements– what’s the number of operations in the worst case?
– Time complexity?
• getEntry (int position)
• Array List: ?
• Linked List: ?
– Time complexity?
• getEntry (int position)
• Array List: O(1) (doesn’t depend on the size of the
array)
• Linked List: requires traversal of all preceding elements
(worst-case: last element, complexity ~ O(N))
Comparison of array and linked list List
implementations
• Which is better?
Assume N elements– what’s the number of operations in the worst case?
– Time complexity?
• remove(int position)
• Array List: ?
• Linked List: ?
– Speed?
• remove(int position)
• Array List: needs reshuffling (worst case: first
element, complexity ~ O(N))
• Linked List: simply “rewire” links in constant time,
but we still need to find this element (worst case:
last element, complexity ~O(N))
Summary of array-based List implementation
• Advantages?
• Retrieving an entry is fast
• Adding an entry at the end of the list is fast
• Arrays are a natural way of thinking about lists of elements
(easy to program?)
• Disadvantages?
• Adding or removing an entry that is between other
entries requires shifting elements in the array (potentially
slow)
• Increasing the size of the array or vector requires
copying elements (potentially slow)
Summary of Pros and Cons of a linked list
implementation
• The linked list can grow as large as necessary
• Can add and remove nodes without shifting existing
entries
But …
• Must traverse a chain to determine where to make
addition/deletion
• Retrieving an entry requires traversal
– As opposed to direct access in an array
• Requires more memory for links
– But does not waste memory for oversized array
Exception example
public class FileReader {
public FileReader() {
}
while (fileInput.hasNext()) {
result = result + fileInput.nextLine();
}
} catch(FileNotFoundException e) {
System.out.println("Sorry, can't find file!");
System.out.println(e.getMessage());
System.exit(1);
}
return result;
}
}
Main:
FileReader seqFile = new FileReader();
while (fileInput.hasNext()) {
result = result + fileInput.nextLine();
}
return result;
}
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Iterator.html
Array-Based Implementation
The array of list entries and nextIndex (a) just before the call to
next; (b) just after the call to next but before the call to remove;
(c) after the call to remove
Carrano and Henry, Data Structures and Abstractions with Java.
How would we implement an iterator?
• Could be implemented in a separate class,
pass a referece to the data structure on which
we want to iterate See Textbook Ch. 13
for an example of this
for(char c:chars) {
st.push(c);
}
- Queues
- Queue functionality
- Linked list queue implementation
- Array-based queue implementation
- Variations on queues
After complete traversal, if there are any starting brackets left in the stack–> not
balanced
Check our solution on our example
Create an empty stack
After complete traversal, if there are any starting brackets left in the stack–> not balanced
(a) A new node that references the top of the stack; (b) the
new node is now at the top of the stack.
Carrano and Henry, Data Structures and Abstractions with Java.
Linked Stack implementation: logic
for “pop”
Use java.util.EmptyStackException
public T peek()
{
}
public T pop()
{
}
public void push(T newEntry) Data member:
{ private Node topNode;
topNode = new Node(newEntry, topNode); (Node has getData, setData,
getNextNode, and setNextNode
}
methods)
Use java.util.EmptyStackException
public T peek()
{
}
public T pop()
{
}
public void push(T newEntry) Data member:
{ private Node topNode;
topNode = new Node(newEntry, topNode); (Node has getData, setData,
getNextNode, and setNextNode
}
methods)
public T peek()
{
if (isEmpty())
throw new EmptyStackException();
else
return topNode.getData();
}
public T pop()
{
}
public void push(T newEntry) Data member:
{ private Node topNode;
topNode = new Node(newEntry, topNode); (Node has getData, setData,
getNextNode, and setNextNode
}
methods)
Use java.util.EmptyStackException
public T peek()
{
if (isEmpty())
throw new EmptyStackException();
else
return topNode.getData();
}
public T pop()
{
T top = peek();
topNode = topNode.getNextNode();
return top;
}
Overview of today’s material
- Stacks
- More practice using stacks
- Linked list stack implementation
- Array-based stack implementation
- Analysis of time complexity of stack implementations
- Queues
- Queue functionality
- Linked list queue implementation
- Array-based queue implementation
- Variations on queues
(see ArrayStack.java)
...
ArrayStack class implementation
The “easy” methods:
public boolean isEmpty()
{
return topIndex < 0;
} // end isEmpty
}
ArrayStack class implementation
4
4
??
} // end push
(like ArrayList, ensureCapacity() method doubles the array when it’s full)
public T peek()
{
checkIntegrity();
??
public T pop()
{
checkIntegrity(); }
??
}
public void push(T newEntry)
{ Data members:
checkIntegrity(); private T[] stack;
ensureCapacity(); private int topIndex;
stack[topIndex + 1] = newEntry;
topIndex++;
} // end push
- Queues
- Queue functionality
- Linked list queue implementation
- Array-based queue implementation
- Variations on queues
Array Stack
- advantages?
-disadvantages?
Exercise: what is the time complexity
of various stack operations?
public T pop()
public T peek()
public T peek()
Linked list implementation {
if (isEmpty())
throw new EmptyStackException();
else
return topNode.getData();
}
public T pop()
{
T top = peek();
topNode = topNode.getNextNode();
return top;
}
public void push(T newEntry)
{ Data members:
checkIntegrity(); private T[] stack;
ensureCapacity(); private int topIndex;
stack[topIndex + 1] = newEntry;
topIndex++;
} // end push
- Queues
- Queue functionality
- Linked list queue implementation
- Array-based queue implementation
- Variations on queues
Op: dequeue
Op: dequeue
Exercise: draw the queue at each step, and write the output of the
print statements
Why are queues important?
One important example: CPU job scheduling
Scheduler
Job Queue
Jobs
J0
J1
CPUs/Cores
J2
.
.
.
- Queues
- Queue functionality
- Linked list queue implementation
- Array-based queue implementation
- Variations on queues
/** Task: Removes and returns the entry at the front of the queue.
* @return either the object at the front of the queue or, if the
* queue is empty before the operation, null */
public T dequeue();
public LinkedQueue()
{
firstNode = null;
lastNode = null;
} // end default constructor
} // end Node
} // end LinkedQueue
LinkedQueue class implementation
public void enqueue(T newEntry); Adds newEntry to the back of the queue
public T dequeue(); Removes and returns the entry at the front of the queue
(a) Before adding a new node to the end of the queue; (b) after adding it.
} // end dequeue
public void enqueue(T newEntry) Data members:
{ private Node firstNode;
Node newNode = new Node(newEntry, null); private Node lastNode;
(Node has getData, setData,
getNextNode, and setNextNode
if (isEmpty()) methods)
firstNode = newNode;
else
lastNode.setNextNode(newNode);
lastNode = newNode;
} // end enqueue
public T dequeue()
{
} // end dequeue
LinkedQueue “dequeue” operation logic
Case 1: removal of the element leaves an empty queue
(a) A queue of one entry; (b) after removing the queue's front.
(a) A queue of more than one entry; (b) after removing the queue's
front.
Carrano and Henry, Data Structures and Abstractions with Java.
public void enqueue(T newEntry) Data members:
{ private Node firstNode;
Node newNode = new Node(newEntry, null); private Node lastNode;
(Node has getData, setData,
getNextNode, and setNextNode
if (isEmpty()) methods)
firstNode = newNode;
else
lastNode.setNextNode(newNode);
lastNode = newNode;
} // end enqueue
public T dequeue()
{
} // end dequeue
public void enqueue(T newEntry) Data members:
{ private Node firstNode;
Node newNode = new Node(newEntry, null); private Node lastNode;
(Node has getData, setData,
getNextNode, and setNextNode
if (isEmpty()) methods)
firstNode = newNode;
else
lastNode.setNextNode(newNode);
lastNode = newNode;
} // end enqueue
public T dequeue()
{
T front = getFront(); //throws except. if empty
firstNode = firstNode.getNextNode();
if (firstNode == null)
lastNode = null;
return front;
} // end dequeue
public void enqueue(T newEntry) Data members:
{ private Node firstNode;
Node newNode = new Node(newEntry, null); private Node lastNode;
(Node has getData, setData,
getNextNode, and setNextNode
if (isEmpty()) methods)
firstNode = newNode;
else
lastNode.setNextNode(newNode);
lastNode = newNode;
} // end enqueue
public T dequeue()
{
T front = getFront(); //throws except. if empty
Pop quiz:
firstNode = firstNode.getNextNode();
What’s the time complexity of these
operations? if (firstNode == null)
lastNode = null;
return front;
} // end dequeue
Now: Array-based implementation
of queue
Full condition:
frontIndex ==
(backIndex + 2)% length
Allows us to distinguish
between empty and full queue
A seven-location
circular array that
contains at most six
entries of a queue.
Empty condition:
frontIndex ==
(backIndex + 1)% length
public void enqueue(T newEntry); Adds newEntry to the back of the queue
public T dequeue(); Removes and returns the entry at the front of the queue
} // end ArrayQueue
First, the easy ones
• isEmpty() ?
public boolean isEmpty()
{
checkIntegrity();
return frontIndex == ((backIndex + 1) % queue.length);
} // end isEmpty
Handling a full queue: ensureCapacity() method
(use ensureCapacity()
method )
} // end enqueue
public T dequeue()
{
} // end dequeue
public void enqueue(T newEntry) Data members:
{ private T[] queue;
checkIntegrity(); private int frontIndex;
ensureCapacity(); private int backIndex
backIndex = (backIndex + 1) %queue.length;
//Index of location after current back of queue (use ensureCapacity()
queue[backIndex] = newEntry;
method )
} // end enqueue
public T dequeue()
{
} // end dequeue
Dequeue logic
public T dequeue()
{
} // end dequeue
public void enqueue(T newEntry) Data members:
{ private T[] queue;
checkIntegrity(); private int frontIndex;
ensureCapacity(); private int backIndex
backIndex = (backIndex + 1) %queue.length;
//Index of location after current back of queue • use ensureCapacity()
queue[backIndex] = newEntry; method
• Assume we’ve defined an
} // end enqueue emptyQueueException
public T dequeue()
{
checkIntegrity();
if (isEmpty())
throw new EmptyQueueException();
else
{
T front = queue[frontIndex];
queue[frontIndex] = null;
frontIndex = (frontIndex + 1) % queue.length;
return front;
} // end if
} // end dequeue
Overview of today’s material
- Stacks
- More practice using stacks
- Linked list stack implementation
- Array-based stack implementation
- Analysis of time complexity of stack implementations
- Queues
- Queue functionality
- Linked list queue implementation
- Array-based queue implementation
- Variations on queues
- Queues
- Queue functionality
- Linked list queue implementation
- Array-based queue implementation
- Variations on queues
“key-value” pair
“key-value” pair
} // end DictionaryInterface
Dictionary exercise
DictionaryInterface<Name,String> phoneBook = new ArrayDictionary<Name,String>();
phoneBook.add(new Name(“John”,”Doe”),”555-1234”);
phoneBook.add(new Name(“Jane”,”Doe”),”555-8888”);
phoneBook.add(new Name(“Bill”,”Smith”),”555-5566”);
phoneBook.add(new Name(“Average”,”Joe”),”555-1234”);
phoneBook.add(key,”555-1111”);
System.out.println(phoneBook.getValue(new Name(“John”,”Doe”)));
phoneBook.remove(key);
if(phoneBook.contains(key)) {
System.out.println(phoneBook.getValue(key));
}
Dictionary exercise
DictionaryInterface<Name,String> phoneBook = new ArrayDictionary<Name,String>();
phoneBook.add(new Name(“John”,”Doe”),”555-1234”);
phoneBook.add(new Name(“Jane”,”Doe”),”555-8888”);
phoneBook.add(new Name(“Bill”,”Smith”),”555-5566”);
phoneBook.add(new Name(“Average”,”Joe”),”555-1234”);
phoneBook.add(key,”555-1111”);
System.out.println(phoneBook.getValue(new Name(“John”,”Doe”)));
phoneBook.remove(key); Output:
if(phoneBook.contains(key)) {
System.out.println(phoneBook.getValue(key));
555-1234
} 555-5566
555-1234
555-1111
Dictionary exercise #2
• Assume you’re given a genome sequence in a
String
String sequence = “ATACGGAATTCCTATACGGGATTATACCCG…” )
if(patterns.contains(currString))
patterns.add(currString,patterns.get(currString)+1);
else
patterns.add(currString,1);
}
//now just get all of the keys from the dictionary, ask for
//corresponding values one-by-one
Iterator<String> keys = patterns.getKeyIterator();
while(keys.hasNext()) {
String curr = keys.next();
System.out.println(curr+”:”+patterns.get(curr));
}
Midterm 2
• Thursday, April 7, 6:30-8:30pm
• Closed book, closed computer
• You are allowed 1 page (8.5” x 11”)
(2 sides) of handwritten notes