71
CS 367 Introduction to Data Structures Lecture 5

CS 367 Introduction to Data Structures Lecture 5

Embed Size (px)

Citation preview

Page 1: CS 367 Introduction to Data Structures Lecture 5

CS 367

Introduction to Data Structures

Lecture 5

Page 2: CS 367 Introduction to Data Structures Lecture 5

A Useful Debugging Trick

For each ADT written in Java, define atoString() method. This allows you to use the standard print or println methods to see the contents of the ADT.

Page 3: CS 367 Introduction to Data Structures Lecture 5

The toString method for LinkedList is fairly simple. It walks the list, adding the string value of each element to the overall string representation:

public String toString() {String result = "(";for (int k = 0; k < itemCount; k++) {

if (k==0)result += get(0).toString() ;

else result += "," + get(k).toString();}return result + ")";

}

Page 4: CS 367 Introduction to Data Structures Lecture 5

An Iterator for Linked Lists

It would be useful if linked lists had an iterator to visit each list member.We need to create an iterator class,LinkedListIterator, that implements the standard iterator methods: hasNext(), next() and remove().

Page 5: CS 367 Introduction to Data Structures Lecture 5

public class LinkedListIterator<E> implements Iterator<E> {

private Listnode<E> curr; // current list node

public LinkedListIterator(Listnode<E> first) { curr = first; }

public boolean hasNext() { return curr != null; }

public E next() { if (!hasNext()) {

throw new NoSuchElementException(); } E data = curr.getData(); curr = curr.getNext(); return data; } public void remove() { throw new UnsupportedOperationException() ;} }

Page 6: CS 367 Introduction to Data Structures Lecture 5

We need to add an iterator() method to the LinkedList class to generate the iterator:

public Iterator<E> iterator() {return new LinkedListIterator(

head.getNext());}

Page 7: CS 367 Introduction to Data Structures Lecture 5

Now we can define a version of toString() that is independent of the details of how a list is implemented:

public String toString() {Iterator<E> iter = this.iterator();String result;if (!iter.hasNext())

return "()";result = "(" + iter.next().toString();while (iter.hasNext() ){

result += "," + iter.next().toString();

}return result + ")";

}

Page 8: CS 367 Introduction to Data Structures Lecture 5

Why are there different implementations of the same

interface?

Implementations can differ in the efficiency of various operations.

Page 9: CS 367 Introduction to Data Structures Lecture 5

Array List Linked List

Constructor O(1) O(1)

Add(E) O(N) O(1)

Add(int,E) O(N) O(N)

Contains(int) O(N) O(N)

Size O(1) O(1)

isEmpty O(1) O(1)

Get(int) O(1) O(N)

Remove(int) O(N) O(N)

Page 10: CS 367 Introduction to Data Structures Lecture 5

Linked List VariationsDoubly-linked List:

Page 11: CS 367 Introduction to Data Structures Lecture 5

Circular Singly-linked List:

Page 12: CS 367 Introduction to Data Structures Lecture 5

Circular Doubly-linked List:

Page 13: CS 367 Introduction to Data Structures Lecture 5

Doubly Linked Lists

Page 14: CS 367 Introduction to Data Structures Lecture 5

public class DblListnode<E> { private DblListnode<E> prev; private E data; private DblListnode<E> next; //*** constructors ***

public DblListnode() { this(null, null, null); } public DblListnode(E d) { this(null, d, null); } public DblListnode(DblListnode<E> p, E d, DblListnode<E> n) { prev = p; data = d; next = n; }

Page 15: CS 367 Introduction to Data Structures Lecture 5

public E getData() { return data; } public DblListnode<E> getNext() { return next; } public DblListnode<E> getPrev() { return prev; }

public void setData(E d) { data = d; } public void setNext(DblListnode<E> n) { next = n; } public void setPrev(DblListnode<E> p) { prev = p; }

Page 16: CS 367 Introduction to Data Structures Lecture 5

Removing a Node

To remove a given node from a doubly linked list, we need to:1. Change the prev field of the

node to its right 2. Change the next field of the

node to its left

Page 17: CS 367 Introduction to Data Structures Lecture 5
Page 18: CS 367 Introduction to Data Structures Lecture 5
Page 19: CS 367 Introduction to Data Structures Lecture 5
Page 20: CS 367 Introduction to Data Structures Lecture 5

Here’s the code:

// Step 1: change the prev field of// the node after n DblListnode<E> tmp = n.getNext(); tmp.setPrev(n.getPrev()); // Step 2: change the next field of// the node before n tmp = n.getPrev(); tmp.setNext(n.getNext());

Page 21: CS 367 Introduction to Data Structures Lecture 5

Special Cases

First or last nodes in list (because prev or next link is null).

Page 22: CS 367 Introduction to Data Structures Lecture 5

Circular Linked Lists

Page 23: CS 367 Introduction to Data Structures Lecture 5

1. Class definitions (and remove method) still work

2. Fewer special cases (head pointer may need to be updated).

Page 24: CS 367 Introduction to Data Structures Lecture 5

Position-Oriented ADTs• List – access ith item• Stack – access only at the top• Queue – access only at front

and rear

Page 25: CS 367 Introduction to Data Structures Lecture 5

PopPush

Stack Abstract Data Type

Page 26: CS 367 Introduction to Data Structures Lecture 5

Stack OperationsWith a stack you push and pop values.The most recently pushed item is always return by a pop. This is called LIFO (Last In First Out).

Page 27: CS 367 Introduction to Data Structures Lecture 5

A Stack Interface

public interface StackADT<E> { boolean isEmpty(); void push(E ob); E pop() throws EmptyStackException; E peek() throws EmptyStackException;}

Page 28: CS 367 Introduction to Data Structures Lecture 5

peek() lets you “peek” at the top stack element without removing it.

Is peek() absolutely need?No, it can be

implemented by doing a pop, then a push.

Page 29: CS 367 Introduction to Data Structures Lecture 5

How to implement a stack

Either an array or linked list may be used. One end of the array (or list) is designated as “top.”

Page 30: CS 367 Introduction to Data Structures Lecture 5

Class ArrayStackpublic class ArrayStack<E>

implements StackADT<E> private static final int INITSIZE = 10 private E[] items; private int numItems;

public ArrayStack() { items = (E[])(new Object[INITSIZE]); numItems = 0;

} ...}

Page 31: CS 367 Introduction to Data Structures Lecture 5

public void push(E ob) { if (items.length == numItems) { expandArray(); } items[numItems] = ob; numItems++;}

Page 32: CS 367 Introduction to Data Structures Lecture 5
Page 33: CS 367 Introduction to Data Structures Lecture 5

public E pop() throws EmptyStackException { if (numItems == 0) { throw new EmptyStackException(); } else { numItems--; return items[numItems]; }}

Page 34: CS 367 Introduction to Data Structures Lecture 5

But bbb is still there!

Page 35: CS 367 Introduction to Data Structures Lecture 5

peek is almost the same as pop:

public E pop() throws EmptyStackException { if (numItems == 0) { throw new EmptyStackException(); } else {

return items[numItems-1]; }}

Page 36: CS 367 Introduction to Data Structures Lecture 5

isEmpty is trivialpublic boolean isEmpty() { return(numItems == 0);}

Page 37: CS 367 Introduction to Data Structures Lecture 5

Worst case complexity of ArrayStack

• Push: O(N)• Pop: O(1)• Peek: O(1)• isEmpty: O(1)

Page 38: CS 367 Introduction to Data Structures Lecture 5

The “Shadow Array” Trick• Push is usually O(1)• But when the end of array is hit, we

need O(N) time to copy current data into a new, bigger array.

• An alternative is to allocate a second expanded array (the shadow array) when current array size hits a threshold.

Page 39: CS 367 Introduction to Data Structures Lecture 5

• Now each push copies pushed item into both arrays. In addition a few items are copied from old to new array.

• Gradually all data from old array are copied into new array before the array fills. When old array fills, we switch to bigger array, already allocated and initialized!

• Like a “Christmas Club” of years ago.

Page 40: CS 367 Introduction to Data Structures Lecture 5

Implementing Stack Using Linked-lists

Linked lists are a very reasonable way to implement stacks. The head of the list is the top stack element:

Page 41: CS 367 Introduction to Data Structures Lecture 5

We create a new class, LLStack, that implements the StackADT interface:

public class LLStack<E> implements

StackADT<E> private Listnode<E> items; private int numItems; ...

Page 42: CS 367 Introduction to Data Structures Lecture 5

The constructor and isEmpty method are both easy.For push, we use:public void push(E ob) { items = new Listnode<E>(ob, items); numItems++;}(we might want to add code to check for null)

Page 43: CS 367 Introduction to Data Structures Lecture 5

Graphically:

Page 44: CS 367 Introduction to Data Structures Lecture 5

Let's consider how to write the pop method. It will need to perform the following steps:• Check whether the stack is

empty; if so, throw an EmptyStackException.

• Remove the first node from the linked list by setting items = items.getNext().

• Decrement numItems.• Return the value that was in the

first node in the list.

Page 45: CS 367 Introduction to Data Structures Lecture 5

public E pop() throws EmptyStackException { if (isEmpty()) throw new EmptyStackException(); E tmp = items.getData(); items = items.getNext(); numItems--; return tmp; }

Page 46: CS 367 Introduction to Data Structures Lecture 5

Complexity of LLStack

Because we add and removes stack elements at the head of the list, all stack operations are O(1)!

Page 47: CS 367 Introduction to Data Structures Lecture 5

Graphically:

Page 48: CS 367 Introduction to Data Structures Lecture 5
Page 49: CS 367 Introduction to Data Structures Lecture 5

Queues

EnqeueDequeue

Page 50: CS 367 Introduction to Data Structures Lecture 5

In a queue, items enter at the rear, via the enqueue operation.

Items are removed from the front of the queue, via the dequeue operation.

The isEmpty operation tells you if the queue is empty.

Page 51: CS 367 Introduction to Data Structures Lecture 5

The QueueADT Interface

public interface QueueADT<E> { boolean isEmpty(); void enqueue(E ob); E dequeue() throws EmptyQueueException;}

Page 52: CS 367 Introduction to Data Structures Lecture 5

Implementing a Queue using an Array

The fact that we add data at one end and remove it from the other end makes queues harder to implement than stacks.

As with stacks, we keep a count of items in the queue and an array to hold them.

Page 53: CS 367 Introduction to Data Structures Lecture 5

public class ArrayQueue<E> implements QueueADT<E> { private static final int INITSIZE = 10; private E[] items; private int numItems;

. . .

Page 54: CS 367 Introduction to Data Structures Lecture 5

Like stacks, we enter new items to the right, at the first unused position.

We remove the leftmost item at position 0. But after this dequeue operation, the leftmost item is now at position 1. If we shift all data left one location, each dequeue has an O(N) complexity.Alternatively, we can track where the leftmost valid item is, but then “gaps” of unused array locations form:

Page 55: CS 367 Introduction to Data Structures Lecture 5
Page 56: CS 367 Introduction to Data Structures Lecture 5

This gap continues to grow until the whole array is filled. We could expand the array, but the gap of unused array space at the left persists.

The solution is to left data “wrap around” from the rightmost array position to position 0, then 1, etc.

Page 57: CS 367 Introduction to Data Structures Lecture 5
Page 58: CS 367 Introduction to Data Structures Lecture 5

In effect we have a “circular array:”

Page 59: CS 367 Introduction to Data Structures Lecture 5

Incrementing the Index in a Circular Array

We must provide for a possible “wrap around” when we increment an array index. The code is:

private int incrementIndex(int index) { if (index == items.length-1) return 0; else return index + 1;}

Page 60: CS 367 Introduction to Data Structures Lecture 5

The enqueue method

Adding to a queue (at the rear) is easy if we ignore expanding a full array:

public void enqueue(E ob) { if (items.length == numItems) { /*code missing here */ } rearIndex = incrementIndex(rearIndex); items[rearIndex] = ob; numItems++;}

Page 61: CS 367 Introduction to Data Structures Lecture 5

We can’t use expandArray() for queues!

Page 62: CS 367 Introduction to Data Structures Lecture 5

Here’s what we need to do:

1. Allocate a new array of twice the size.2. Copy the values in the range

items[frontIndex] to items[items.length-1] into the new array (starting at position frontIndex in the new array).

3. Copy the values in the range items[0] to items[rearIndex] into the new array (starting at position items.length in the new array). Note: if the front of the queue was in items[0], then all of the values were copied by step 2, so this step is not needed.

4. Set items to point to the new array.5. Fix the value of rearIndex.

Page 63: CS 367 Introduction to Data Structures Lecture 5
Page 64: CS 367 Introduction to Data Structures Lecture 5
Page 65: CS 367 Introduction to Data Structures Lecture 5

Here’s the missing code for enqueue:

if (items.length == numItems) { E[] tmp = (E[])(new Object[items.length*2]); System.arraycopy(items, frontIndex, tmp, frontIndex, items.length-frontIndex); if (frontIndex != 0) { System.arraycopy(items, 0, tmp, items.length, frontIndex);} items = tmp;

rearIndex = frontIndex + numItems - 1; }

Page 66: CS 367 Introduction to Data Structures Lecture 5

Implementing dequeue

Dequeue is far simpler to implement because we don’t have to handle expanding the array (dequeue removes elements).We return the data at frontIndex and increment that index using incrementIndex (to get the wrap around effect).

Page 67: CS 367 Introduction to Data Structures Lecture 5

Implementing Queues using Linked-lists

With linked-lists, queues are much easier to implement.The data needed is:public class LLQueue<E> implements QueueADT<E> { private Listnode<E> qFront; private Listnode<E> qRear; private int numItems; ...

Page 68: CS 367 Introduction to Data Structures Lecture 5

Items are added to the end of the list and removed from the front of the list.

Page 69: CS 367 Introduction to Data Structures Lecture 5

Complexity of Queue Operations

In the linked-list implementation, all operations are O(1).In the array implementation, enquque has a worst case complexity of O(N) when the array has to be expanded. This can be avoided using a shadow array.

Page 70: CS 367 Introduction to Data Structures Lecture 5

A Hybrid Implementation

The main draw back of the simple linked-list queue is that we maintain a link for each data item.

What if we linked subarrays instead?

Page 71: CS 367 Introduction to Data Structures Lecture 5

...

Both the front and rear indices always move to the right. When the rear index hits the end of a subarray, a new subarray is allocated and linked. When the front index hits the end of the subarray, the subarray is deleted and the next subarray is used.

Array 1 Array N

Rear IndexFront Index