Report

Data Structures Part 2 Stacks, Queues, Trees, and Graphs Briana B. Morrison CSE 1302C Spring 2010 Topics • Stacks • Queues • Trees and Graphs CSE 1302C 2 Classic Data Structures • Now we'll examine some classic data structures • Classic linear data structures include queues and stacks • Classic nonlinear data structures include trees and graphs CSE 1302C 3 A Stack Using a Linked List • A stack is a linear data structure that organizes items in a last in, first out (LIFO) manner. • Items are added and removed from only one end of a stack • Analogies: cafeteria trays are typically organized in a stack, a stack of bills to be paid • The tray at the top of the stack was put on the stack last, and will be taken off the stack first. CSE 1302C 4 Stacks • Stacks often are drawn vertically: push CSE 1302C pop 5 Stacks • Some stack operations: – push - add an item to the top of the stack – pop - remove an item from the top of the stack – peek (or top) - retrieves the top item without removing it – empty - returns true if the stack is empty • A stack can be represented by a singlylinked list • A stack can be represented by an array, but the new item should be placed in the next available place in the array rather than at the end CSE 1302C 6 A Stack can be Implemented using a Linked List • In a stack implemented as a linked list: – To push, we insert at the beginning of the linked list. – To pop, we delete the first item in the linked list. Thus, deletion is not based on value; we always delete the first item in the list. CSE 1302C 7 PlayerStackLinkedList Methods Return value Method name and argument list void push( Player p ) inserts Player p at the top of the stack Player pop( ) returns and removes the first Player of the list. If the list is empty, the method throws a DataStructureException. Player peek( ) returns a copy of the first Player on the list without deleting it. If the list is empty, the method throws a DataStructureException. CSE 1302C 8 A Stack Can be Implemented using an Array • If we know in advance the maximum number of objects on the stack, we can represent the stack using an array. – This is easier to implement than a linked list. • We add items to the stack starting at index 0. • We maintain an index top, short for top of the stack. • The last element inserted is at index top. CSE 1302C 9 Array Representing a Stack • There are three items on the stack: top CSE 1302C index Player object 2 (8, Gino, Diablo ) 1 ( 7, Sarah, Mario ) 0 ( 2, Jin, Golf ) 10 Stack after Inserting Player (6, Steve, NFL ) • Now there are four items on the stack: top CSE 1302C index Player object 3 ( 6, Steve, NFL ) 2 (8, Gino, Diablo ) 1 ( 7, Sarah, Mario ) 0 ( 2, Jin, Golf ) 11 Our Stack After Popping Once • Now there are three items on the stack. Player ( 6, Steve, NFL ) is not on the stack. top CSE 1302C index Player object 3 ( 6, Steve, NFL ) 2 (8, Gino, Diablo ) 1 ( 7, Sarah, Mario ) 0 ( 2, Jin, Golf ) 12 Instance Variables for Stack • We will have three instance variables: – A constant STACK_SIZE, representing the capacity of the stack. – An array stack, storing the elements of the stack. – An int top, representing the top of the stack. public class ArrayStack { private static final int STACK_SIZE = 100; private Player [] stack; private int top; …. } CSE 1302C 13 Constructor for our Stack • The constructor does two things: – It instantiates the array, stack. – It initializes top to -1 to reflect that the stack is empty. If top were initialized to 0, then there would be an element on the stack, at index 0. public ArrayStack( ) { stack = new Player[STACK_SIZE]; top = -1; // stack is empty } CSE 1302C 14 Utility Methods for our Stack • We will have three utility methods: – isEmpty, returning true if the stack is empty. – isFull, returning true if the stack is full. – toString, returning a String representation of the stack. public bool isEmpty( ) { return ( top == -1 ); } public bool isFull( ) { return ( top == ( STACK_SIZE – 1 ) ); } CSE 1302C 15 toString Method for our Stack public String toString( ) { String stackString = ""; for ( int i = top; i >= 0; i-- ) stackString += ( i + ": " + stack[i] + "\n" ); return stackString; } CSE 1302C 16 push Method for our Stack public boolean push( Player p ) { if ( !isFull( ) ) { stack[++top] = p; return true; } else return false; } CSE 1302C 17 pop Method for our Stack public Player pop { if ( !isEmpty( ) ) return ( stack[top--] ); else throw new DSException ( "Stack empty: cannot pop" ); } CSE 1302C 18 Common Error Trap • Do not confuse the top of the stack with the last index in the array. Array elements with array indexes higher than top are not on the stack. CSE 1302C 19 Queues • A queue is similar to a list but adds items only to the rear of the list and removes them only from the front • It is called a FIFO data structure: First-In, FirstOut • Analogy: a line of people at a bank teller’s window •enqueue CSE 1302C •dequeue 20 Queues • We can define the operations for a queue – enqueue - add an item to the rear of the queue – dequeue (or serve) - remove an item from the front of the queue – empty - returns true if the queue is empty • As with our linked list example, by storing generic Object references, any object can be stored in the queue • Queues often are helpful in simulations or any situation in which items get “backed up” while awaiting processing CSE 1302C 21 Queues • A queue can be represented by a singlylinked list; it is most efficient if the references point from the front toward the rear of the queue • A queue can be represented by an array, using the remainder operator (%) to “wrap around” when the end of the array is reached and space is available at the front of the array CSE 1302C 22 A Queue can be Implemented using a Linked List • In a queue implemented as a linked list, we enqueue by inserting at the end of the linked list. • In a stack implemented as a linked list, we dequeue by deleting the first item in the linked list. Again, deletion is not based on value; it is based on position. CSE 1302C 23 A Linked List Class Implementing a Queue • Because a queue inserts items at the end of the list, we will add an instance variable, tail, (a PlayerNode reference), representing the last node in the list. In this way, we do not have to traverse the list every time we insert an item. • We will need to update that reference every time an item is inserted. CSE 1302C 24 A Linked List Implementing a Queue • Here is an example of a queue of Player objects represented by a linked list. 7 Sarah Mario head CSE 1302C 5 Ajay Sonic 8 Gino Diablo 2 Jin Golf null tail 25 Return value Method name and argument list void enqueue( Player p ) inserts Player p at the end of the list Player dequeue( ) returns and removes the first Player of the list. If the list is empty, the method throws a DataStructureException Player peek( ) returns a copy of the first Player on the list without deleting it. If the list is empty, the method throws a DataStructureException CSE 1302C 26 Inserting in a (non-empty) Linked List Representing a Queue • Our original list: 7 Sarah Mario head CSE 1302C 5 Ajay Sonic 8 Gino Diablo 2 Jin Golf null tail 27 Inserting in a (non-empty) Linked List Representing a Queue • Step 1: Instantiate a new node: PlayerNode pn = new PlayerNode( p ); 7 Sarah Mario // here, p is Player( 6, Steve, NFL ) 5 Ajay Sonic 8 Gino Diablo 2 Jin Golf head null tail •6 •Steve •NFL null pn CSE 1302C 28 Inserting in a Linked List Representing a Queue • Step 2: Attach the new node at the end of the list: tail.setNext( pn ); 7 Sarah Mario head CSE 1302C 5 Ajay Sonic 8 Gino Diablo 2 Jin Golf 6 Steve NFL null tail 29 Inserting in a Linked List Representing a Queue • Step 3: Update tail: tail = tail.getNext( ); 7 Sarah Mario head CSE 1302C 5 Ajay Sonic 8 Gino Diablo 2 Jin Golf 6 Steve NFL null tail 30 A Linked List Class Implementing a Queue • A queue has a front and a back, represented by head and tail, respectively. • Could they be inverted, i.e. could head represent the back of the queue and tail represent the front of the queue? • This would be highly inefficient because when we delete, we would need to traverse the list in order to update tail. • Indeed, we cannot go backward in the list from tail in order to access the next-to-last node (which becomes tail after the deletion). CSE 1302C 31 Array Representation of Queues • We can also represent a queue using an array. • The first (inefficient) idea is to represent the queue with a standard array; two indexes, front and back, represent the front and back of the queue. • To enqueue, we could increment back by 1 and store the player at array index back. • To dequeue, we could return the element at index front and increment front by 1. • The problem with this approach is that the number of available elements in the array will shrink over time. CSE 1302C 32 Queue after Inserting First 5 Elements • // Player( 5, Ajay, Sonic ) was enqueued first. index 7 6 back front CSE 1302C 5 4 3 2 1 0 Player object ( 6, Steve, NFL ) (8, Gino, Diablo ) ( 7, Sarah, Mario ) ( 2, Jin, Golf ) ( 5, Ajay, Sonic ) 33 Queue after Dequeueing Once • Element at index 0 is no longer usable. index 7 6 back front CSE 1302C Player object 5 4 3 2 ( 7, Sarah, Mario ) 1 ( 2, Jin, Golf ) 0 ( 5, Ajay, Sonic ) ( 6, Steve, NFL ) (8, Gino, Diablo ) 34 Using a Circular Array • The solution is to consider the array as a circular array. • After back reaches the last array index, we start enqueueing again at index 0. • If the array has size 8, the index "after" 7 is 0. • The useful capacity of the array never shrinks. If the array has size 8, the useful capacity of the array is always 8. CSE 1302C 35 An Empty Queue CSE 1302C 36 Enqueueing One Element CSE 1302C 37 Enqueueing a Second Element CSE 1302C 38 Enqueueing a Third Element CSE 1302C 39 Enqueueing a Fourth Element CSE 1302C 40 Dequeueing Once CSE 1302C 41 Dequeueing Again CSE 1302C 42 Full Queue • In a queue implemented as a circular array, how do we know when the queue is full? • When the queue is full, we have the following relationship between front and back: ( back + 1 – front ) % QUEUE_SIZE == 0 CSE 1302C 43 A Full Queue CSE 1302C 44 Empty Queue • When the queue is empty, we also have the same relationship between front and back! ( back + 1 – front ) % QUEUE_SIZE == 0 CSE 1302C 45 An Empty Queue CSE 1302C 46 Queue Full or Empty? • So when ( back + 1 – front ) % QUEUE_SIZE == 0 • Is the queue full or empty? We cannot tell. • There is an easy way to solve this problem: Keep track of the number of items in the queue. CSE 1302C 47 Instance Variables for our Queue • We will have five instance variables: – – – – – A constant representing the capacity of the queue. An array, storing the elements of the queue. An int, front, representing the front of the queue. An int, back, representing the back of the queue. An int, numberOfItems, storing the number of items in the queue. public class ArrayQueue { private static final int QUEUE_SIZE = 8; private Player [] queue; private int front; private int back; private int numberOfItems; … } CSE 1302C 48 Constructor for our Queue • The constructor does four things: – – – – instantiates the array, queue. initializes front to 0 initializes back to QUEUE_SIZE – 1 initializes numberOfItems to 0 to reflect that the queue is empty. public ArrayQueue( ) { queue = new Player[QUEUE_SIZE]; front = 0; back = QUEUE_SIZE - 1; numberOfItems = 0; } CSE 1302C 49 Utility Methods for our Queue • We will have three utility methods: – isEmpty, returning true if the the queue is empty. – isFull, returning true if the the queue is full. – toString, returning a String representation of the queue. public boolean isEmpty( ) { return ( numberOfItems == 0 ); } public boolean isFull( ) { return ( numberOfItems == QUEUE_SIZE ); } CSE 1302C 50 public String toString( ) { String queueString = ""; if ( back >= front ) { for ( int i = front; i <= back; i++ ) queueString += queue[i] + "\n"; } else { for ( int i = front; i < QUEUE_SIZE; i++ ) queueString += queue[i] + "\n"; for ( int i = 0; i <= back; i++ ) queueString += queue[i] + "\n"; } return queueString; } toString Method for our Queue CSE 1302C 51 enqueue Method for our Queue public boolean enqueue( Player p ) { if ( !isFull( ) ) { queue[( back + 1 )% QUEUE_SIZE] = p; back = ( back + 1 ) % QUEUE_SIZE; numberOfItems++; return true; } else return false; } CSE 1302C 52 dequeue Method for our Queue public Player dequeue throws DataStructureException { if ( !isEmpty( ) ) { front = ( front + 1 ) % QUEUE_SIZE; numberOfItems--; return queue[( QUEUE_SIZE + front – 1 ) % QUEUE_SIZE]; } else throw new DataStructureException ( "Queue empty: cannot dequeue" ); } CSE 1302C 53 Common Error Trap • Do not confuse array index 0 and QUEUE_SIZE - 1 with front and back. In a queue represented by a circular array, the indexes 0 and QUEUE_SIZE - 1 are irrelevant. CSE 1302C 54 Implementing a Stack or a Queue as an Array vs as a Linked List Array Linked List Easily expandable No Yes Direct access to every item Yes No Easy to code Yes No CSE 1302C 55 Trees • A tree is a non-linear data structure that consists of a root node and potentially many levels of additional nodes that form a hierarchy • Nodes that have no children are called leaf nodes • Nodes except for the root and leaf nodes are called internal nodes • In a general tree, each node can have many child nodes CSE 1302C 56 Binary Trees • In a binary tree, each node can have no more than two child nodes • A binary tree can be defined recursively. Either it is empty (the base case) or it consists of a root and two subtrees, each of which is a binary tree • Trees are typically are represented using references as dynamic links, though it is possible to use fixed representations like arrays • For binary trees, this requires storing only two links per node to the left and right child CSE 1302C 57 Tree Example - Starcraft CSE 1302C 58 Dungeon Siege 2 CSE 1302C 59 Diablo 2 CSE 1302C 60 Fallout 3 CSE 1302C 61 Visiting Every Node void PrintTree (Node current) { if (current != null) { Console.WriteLine(current.data) PrintTree(current.left); PrintTree(current.right); } } • Of course we could order the 3 statements of work in any order, visiting the nodes in a different order. • Not to be missed: http://oracleofbacon.org/ CSE 1302C 62 Graphs • A graph is a non-linear structure • Unlike a tree or binary tree, a graph does not have a root • Any node in a graph can be connected to any other node by an edge • Analogy: the highway system connecting cities on a map CSE 1302C 63 Digraphs • In a directed graph or digraph, each edge has a specific direction. • Edges with direction sometimes are called arcs • Analogy: airline flights between airports CSE 1302C 64 Representing Graphs • Both graphs and digraphs can be represented using dynamic links or using arrays. • As always, the representation should facilitate the intended operations and make them convenient to implement CSE 1302C 65 Collection Classes • The C# standard library contains several classes that represent collections, often referred to as the Generic Collection • Their underlying implementation is implied in the class names such as ArrayList and LinkedList CSE 1302C 66 Stacks • The System.Collections.Generic; package contains a Stack class • Like ArrayList operations, the Stack operations operate on Object references CSE 1302C 67 Questions? CSE 1302C 68