<<

Stacks and Queues ¡ Abstract Data Types ¡ Stacks ¡ Queues ¡ Priority Queues

September 2004 John Edgar 2 September 2004 John Edgar 3 ¡ (RPN) § Also known as postfix notation § A mathematical notation ▪ Where every operator follows its operands § Invented by Jan Łukasiewicz in 1920 ¡ Example § Infix: 1 + 2 § RPN: 1 2 +

September 2004 John Edgar 4 ¡ More interesting example § Infix: 5 + ((1 + 2) * 4) − 3 § RPN: 5 1 2 + 4 * + 3 –

¡ Cleaner, no backets

September 2004 John Edgar 5 ¡ 5 1 2 + 4 * + 3 – store 5 store 1 store 2

Apply + to the last two operands 2 1 5

September 2004 John Edgar 6 ¡ 5 1 2 + 4 * + 3 – store 5 store 1 store 2

Apply + to the last two operands store result store 4 4 Apply * to the last two operands 3 5

September 2004 John Edgar 7 ¡ 5 1 2 + 4 * + 3 – store 5 store 1 store 2

Apply + to the last two operands

store result store 4

Apply * to the last two operands

store result 12 Apply + to the last two operands 5

September 2004 John Edgar 8 ¡ 5 1 2 + 4 * + 3 – store 5 store 1 store 2

Apply + to the last two operands

store result store 4

Apply * to the last two operands

store result 3 Apply + to the last two operands 17 store result store 3

Apply - to the last two operands

September 2004 John Edgar 9 ¡ 5 1 2 + 4 * + 3 – store 5 store 1 store 2

Apply + to the last two operands

store result store 4

Apply * to the last two operands store result store result Apply + to the last two operands 14 store result store 3 retrieve answer Apply - to the last two operands

September 2004 John Edgar 10 ¡ What are the storage properties of the that we used? § Specifically how are items inserted and removed? § The last item to be entered is the first item to be removed § Known as LIFO (Last In First Out) ¡ A stack

September 2004 John Edgar 11 September 2004 John Edgar 12 ¡ A stack only allows items to be inserted and removed at one end § We call this end the top of the stack § The other end is called the bottom ¡ Access to other items in the stack is not allowed

September 2004 John Edgar 13 ¡ A stack can be used to naturally store data for postfix notation § Operands are stored at the top of the stack § And removed from the top of the stack ¡ Notice that we have not (yet) discussed how a stack should be implemented § Just what it does ¡ An example of an Abstract Data Type

September 2004 John Edgar 14 September 2004 John Edgar 15 ¡ A collection of data § Describes the data but not how it is stored ¡ Set of operations on the data § Describes precisely what effect the operations have on the data but § Does not specify how operations are carried out ¡ An ADT is not a data structure

September 2004 John Edgar 16 ¡ The term concrete data type is usually used in contrast with an ADT ¡ An ADT is a collection of data and a set of operations on the data ¡ A concrete data type is an implementation of an ADT using a data structure § A construct that is defined in a to store a collection of data

September 2004 John Edgar 17 ¡ Mutators ¡ Accessors ¡ Other

September 2004 John Edgar 18 ¡ Often known as setters § Operations that change the contents of an ADT, usually subdivided into ▪ Adding data to a data collection and ▪ Removing data from a collection § Different ADTs allow data to be added and removed at different locations

September 2004 John Edgar 19 ¡ Often known as getters § Retrieve data from the collection § e.g. the item at the top of the stack § Ask questions about the data collection § Is it full? § How many items are stored? § …

September 2004 John Edgar 20 ¡ Information related to storage implementation should be hidden ¡ An ADT’s operations can be used in the design of other modules or applications § Other modules do not need to know the implementation of the ADT operations § Allowing the implementation of operations to be changed without affecting other modules

September 2004 John Edgar 21 ¡ Operations should be specified in detail without discussing implementation issues

§ In ++ an ADT class can be divided into header (.h) and implementation (.cpp) files ▪ More in later lectures

September 2004 John Edgar 22 Arrays

September 2004 John Edgar 23 ¡ A stack should implement at least the first two of these operations: § push – insert an item at the top of the stack § pop – remove and return the top item § peek – return the top item

¡ These operations are straightforward so should be performed efficiently

September 2004 John Edgar 24 ¡ Assume that we plan on using a stack to contain integers with these methods § void push (int) § int pop()

¡ We can design other modules that use these methods § Without having to know anything about how they, or the stack itself, are implemented

September 2004 John Edgar 25 ¡ We will use classes to encapsulate stacks § Encapsulate – enclose in

¡ A class is a programming construct that contains § Data for the class § Operations of the class § More about classes later …

September 2004 John Edgar 26 ¡ The stack ADT can be implemented using a variety of data structures, e.g. § Arrays § Linked Lists

¡ Both implementations must implement all the stack operations § And in constant time (time that is independent of the number of items in the stack)

September 2004 John Edgar 27 ¡ Let’s use an array to implement a stack ¡ We will need to keep track of the index that represents the top of the stack § When we insert an item increment this index § When we delete an item decrement this index

September 2004 John Edgar 28 index of top is current size – 1 //Java Code Stack st = new Stack();

0 1 2 3 4 5

September 2004 John Edgar 29 index of top is current size – 1 //Java Code Stack st = new Stack(); 6 6 st.push(6); //top = 0 0 1 2 3 4 5

September 2004 John Edgar 30 index of top is current size – 1 //Java Code Stack st = new Stack(); 6 6 1 7 8 st.push(6); //top = 0 st.push(1); //top = 1 0 1 2 3 4 5 st.push(7); //top = 2 st.push(8); //top = 3

September 2004 John Edgar 31 index of top is current size – 1 //Java Code Stack st = new Stack(); 6 6 1 7 st.push(6); //top = 0 st.push(1); //top = 1 0 1 2 3 4 5 st.push(7); //top = 2 st.push(8); //top = 3 st.pop(); //top = 2

September 2004 John Edgar 32 ¡ Java coding example

September 2004 John Edgar 33 ¡ Easy to implement a stack with an array § And push and pop can be performed in constant time ¡ But what happens when the array is full? ¡ Once the array is full § No new values can be inserted or § A new, larger, array has to be created ▪ And the existing items copied to this new array

September 2004 John Edgar 34 September 2004 John Edgar 35 ¡ Arrays contain identically typed values § These values are stored sequentially in main memory ¡ Values are stored at specific numbered positions in the array called indexes § The first value is stored at index 0, the second at index 1, the ith at index i-1, and so on § The last item is stored at position n-1, assuming that n values are stored in the array

September 2004 John Edgar 36 ¡ int[] arr = index value {3,7,6,8,1,7,2}; 0 3 § Creates an integer array with 7 elements 1 7 ¡ To access an element refer to the array name and the element's index 2 6 § int x = arr[3]; assigns the value of 3 8 the fourth array element (8) to x § arr[5] = 11; changes the sixth 4 1 element of the array from 7 to 11 5 117 § arr[7] = 3; results in an error because the index is out of bounds 6 2

In Java this results in an ArrayIndexOutOfBoundsException

September 2004 John Edgar 37 ¡ C++ coding example

September 2004 John Edgar 38 Declares an array int[] grade; variable Note: . C++ is int * grade; main memory is depicted below this line This symbol represents grade the “null pointer”

In Java arrays are objects, so this is a reference variable

September 2004 John Edgar 39 int[] grade; grade = new int[4]; Creates a new array of size 4

main memory is depicted below this line

grade 0 0 Stores 4 ints consecutively. refers to the 0 The ints are initialized to 0 newly created 0 array object

September 2004 John Edgar 40 int[] grade;

grade = new int[4]; Assigns 23 to the third grade[2] = 23; item in the array main memory is depicted below this line

grade 0 But how does 0 the system 230 “know” where grade[2] is? 0

September 2004 John Edgar 41 ¡ Access to array elements is very fast ¡ An array variable references the array § A reference is just a main § The address is stored as number, with each address referring to one byte of memory ▪ Address 0 would be the first byte ▪ Address 1 would be the second byte ▪ Address 20786 would be the twenty thousand, seven hundred and eighty seventh byte ▪ …

September 2004 John Edgar 42 ¡ Consider grade[2] = 23; § How do we find this element in the array? ¡ Consider what we know § The address of the first array element § The type of the values stored in the array ▪ And therefore the size of the array elements § The index of the element to be accessed ¡ We can calculate the address of the element to be accessed § address of first element + (index * type size)

September 2004 John Edgar 43 … 1280 1290 The int stored 1281 1291 at grade[2] is grade 1282 1292 located at byte: 1283 1293 1284 1294 1282 + 2 * 4 = 1285 1295 Stores a reference 1286 1296 1290 to the start of the 1287 1297 array, in this case 1288 1298 byte 1282 1289 1299 …

September 2004 John Edgar 44 ¡ Array variables are reference variables § An array variable argument to a method passes the address of the array ▪ And not the elements of the array ¡ Changes made to the array by a method are made to the original array object § If this is not desired, a copy of the array should be made within the method

September 2004 John Edgar 45 ¡ What if array positions carry meaning? § An array that is sorted by name, or grade or some other value § Or an array where the position corresponds to a position of some entity in the world ¡ The ordering should be maintained as elements are inserted or removed

September 2004 John Edgar 46 ¡ When an item is inserted either § Write over the element at the given index or § Move the element, and all elements after it, up one position ¡ When an item is removed either § Leave gaps in the array, i.e. array elements that don't represent values or § Move all the values after the removed value down one index

September 2004 John Edgar 47 ¡ The size of an array must be specified when it is created § And cannot then be changed ¡ If the array is full values can’t be inserted § There are, time consuming, ways around this § To avoid this problem we can make arrays much larger than they are needed § However this wastes space

September 2004 John Edgar 48 ¡ Good things about arrays § Fast, random access, of elements using a simple offset calculation § Very memory efficient, as little memory is required other than the data itself § Easy to use ¡ Bad things about arrays § Slow deletion and insertion of elements for ordered arrays § Size must be known when the array is created

September 2004 John Edgar 49 September 2004 John Edgar 50 ¡ It would be nice to have a data structure that is § Dynamic § Does fast insertions/deletions in the middle ¡ We can achieve this using linked lists …

September 2004 John Edgar 51 ¡ A linked list is a dynamic data structure that consists of nodes linked together ¡ A node is a data structure that contains § data § the location of the next node 7 7

September 2004 John Edgar 52 ¡ A linked list is a chain of nodes where each node stores the address of the next node

start This symbol 6 6 2 indicates a null reference

7 7 8

September 2004 John Edgar 53 // Java class Node { public int data; public Node next; } 7 // C++ class Node { public: int data; Node *next; };

September 2004 John Edgar 54 // Java class Node { public int data; public Node next; } 7 // C++ class Node { public: A node points to int data; another node, so the reference must be of Node *next; type Node };

September 2004 John Edgar 55 Node a = new Node(7, null);

Assume we’ve added a constructor to the Node class that lets us initialize a data and next like this.

7 7

// C++ Node *a = new Node(7, null);

September 2004 John Edgar 56 Node a = new Node(7, null); a.next = new Node(3, null); a a

7 7 3

a.data a.next.data a->data a.next a->next->data a->next // C++ Node *a = new Node(7, null); a->next = new Node(3, null); 57 Node a = new Node(7, null); a.next = new Node(3, null); Node p = a; a a

7 7 3

p p

// C++ Node *a = new Node(7, null); a->next = new Node(3, null);

Node *p = a; 58 Node a = new Node(7, null); a.next = new Node(3, null); Node p = a; p = p.next; // go to the next node

We can walk through a linked list by a a going from node to node.

7 7 3

p p Node *a = new Node(7, null); a->next = new Node(3, null); Node *p = a; p = p->next;// go to the next node September 2004 John Edgar 59 Node a = new Node(7, null); a.next = new Node(3, null); Node p = a; p = p.next; // go to the next node p = p.next; Eventually, p reaches the end of the a a list and becomes null.

7 7 3

p p Node *a = new Node(7, null); a->next = new Node(3, null); Node *p = a; p = p->next; // go to the next node

September 2004 John Edgar p = p->next; 60 ¡ The previous example showed a list built out of nodes ¡ In practice a linked list is encapsulated in its own class § Allowing new nodes to be easily inserted and removed as desired § The linked list class has a pointer to the (node at the) head of the list ¡ Implementations of linked lists vary

September 2004 John Edgar 61 Linked Lists

September 2004 John Edgar 62 ¡ Nodes should be inserted and removed at the head of the list § New nodes are pushed onto the front of the list, so that they become the top of the stack § Nodes are popped from the front of the list ¡ Straight-forward linked list implementation § Both push and pop affect the front of the list ▪ There is therefore no need for either algorithm to traverse the entire list

September 2004 John Edgar 63 Java public void push(int x){ // Make a new node whose next reference is // the existing list Node newNode = new Node(x, head); head = newNode; //head points to new node }

public int pop(){ // Return the value at the head of the list int temp = head.data; head = head.next; return temp; //head points to new node }// Garbage collection deallocates old head

September 2004 John Edgar 64 C++ void LinkedList::push(int x){ // Make a new node whose next reference is // the existing list Node *newNode = new Node(x, head); head = newNode; //head points to new node }

int LinkedList::pop(){ // Return the value at the head of the list int temp = head->data; Node *ht = head; head = head->next; delete ht; return temp; //head points to new node } September 2004 John Edgar 65 C++ Code Stack st; st.push(6); top

6 6

September 2004 John Edgar 66 Java Code Stack st = new Stack(); st.push(6); st.push(1); top

1 1

6 6

September 2004 John Edgar 67 C++ Code Stack st; st.push(6); st.push(1); top 7 st.push(7);

1 1

6 6

September 2004 John Edgar 68 C++ Code 8 8 Stack st; st.push(6); st.push(1); top 7 st.push(7); st.push(8); 1 1

6 6

September 2004 John Edgar 69 Java Code 8 8 Stack st = new Stack(); st.push(6); st.push(1); top 7 st.push(7); st.push(8); st.pop(); 1 1

6 6

September 2004 John Edgar 70 Java Code Stack st = new Stack(); st.push(6); st.push(1); top 7 st.push(7); st.push(8); st.pop(); 1 1

6 6

September 2004 John Edgar 71 September 2004 John Edgar 72 ¡ Programs typically involve more than one function call § A main function § Which calls other functions as required ¡ Each function requires space in main memory for its variables and parameters § This space must be allocated and de-allocated in some organized way ¡ Most programming languages use a call stack to implement function calling § When a method is called, its line number and other data are pushed onto the call stack § When a method terminates, it is popped from the call stack § Execution restarts at the indicated line number in the method currently at the top of the stack

September 2004 John Edgar 74 This is a display of the call stack (from the Eclipse Debug window)

Top of the stack: Bottom of the stack: least most recently recently called method called method.

September 2004 John Edgar 75 ¡ When a function is called space is allocated for it on the call stack § This space is allocated sequentially ¡ Once a function has run the space it used on the call stack is de-allocated § Allowing it to be re-used ¡ Execution returns to the previous function § Which is now at the top of the call stack public double biggerArea(double side, double r){ double sq; double circ; sq = squareArea(side); circ = circleArea(r); if (sq > circ) public double square(double x){ return sq; return x * x; else } return circ; } public double squareArea(double side){ return square(side) } public double circleArea(double r){ return Math.PI * square(r); } double biggerArea(double side, double r){ double sq; double circ; sq = squareArea(side); circ = circleArea(r); if (sq > circ) double square(double x){ return sq; return x * x; else } return circ; } double squareArea(double side){ return square(side) } double circleArea(double r){ return 3.14 * square(r); } biggerArea(4, 2) // executing biggerArea sq = squareArea(side)

// executing squareArea 4 square return square(side) x // return to biggerArea 4 squareArea side 4 4 2 16 biggerArea side r sq circ

… … stack biggerArea(4, 2) // executing biggerArea sq = squareArea(side)

// executing squareArea 2 square return square(side) x // return to biggerArea 2 circleArea circ = circleArea(r) r // executing circleArea 4 2 16 12.5… biggerArea return pi * square(r) side r sq circ

// return to biggerArea … if (sq > circ) return sq stack ¡ In the example, functions returned values assigned to variables in other functions § They did not affect the amount of memory required by previously called functions § That is, functions below them on the call stack ¡ Stack memory is sequentially allocated § It is not possible to increase memory assigned to a function previously pushed onto the stack ¡ Let's say we want to call a function to insert some values in an existing array § If the calling function has made an array with of the right size this is not a problem ▪ The new values are stored in the existing array § But the calling function might not know how many values are to be inserted ¡ Memory on the call stack is allocated sequentially § So we can’t change the size of a variable belonging to a function already on the stack § Suggesting that we can’t change the size of an array when the program is running § Either make an array large enough so that it doesn’t overflow ¡ Or use dynamic memory ¡ What happens if we want to allocate memory at run time? § It can’t be allocated to stack memory so will need to be put somewhere else § Let’s call somewhere else the heap ¡ We still need stack variables that refer or point to the dynamically allocated memory § Which we will call reference or pointer variables ¡ Create a variable to store an address § A reference of pointer variable § Addresses have a fixed size § If there is initially no address assign it a special value (null) ¡ Create new data in the heap when needed § This may be done at run time ¡ Assign the address of the data to variable ¡ This does involve more administration // in function, f … // Java code int[] getOrdArray(int n){ int arr[]; int arr[] = new int[n]; for (int i = 0; i < n; ++i){ arr[i] = i * 2 + 1; arr = getOrdArray(5); } // … return arr; }

5 5 getOrdArray arr n … … null f f 1 3 5 7 9 … … arr

… …

stack heap // in function, f … // C++ code int * getOrdArray(int n){ int *arr; int *arr = new int[n]; for (int i = 0; i < n; ++i){ arr[i] = i * 2 + 1; arr = getOrdArray(5); } // … return arr; }

5 5 getOrdArray arr n … … null f f 1 3 5 7 9 … … arr

… …

stack heap ¡ When a function call is complete its stack memory is released and can be re-used ¡ Dynamic memory should also be released § Failing to do so results in a memory leak ¡ It is not as easy to determine when dynamic memory should be released § Data might be referred to by more than one address variable ▪ Memory should only be released when it is no longer required by any variable ¡ When should a data object be created in dynamic memory? § When the object is required to change size, or § If it is not known if the object will be required ¡ Languages have different approaches to using static and dynamic memory § In Java this is determined by the data type § In C++ the programmer can choose whether to assign data to static or dynamic memory September 2004 John Edgar 90 ¡ Assume that we want to store data for a print queue for a student printer § Student ID § Time § File name ¡ The printer is to be assigned to file in the order in which they are received § A fair algorithm

September 2004 John Edgar 91 ¡ To maintain the print queue we would require at least two classes § Request class § Collection class to store requests ¡ The collection class should support the desired behaviour § FIFO (First In First Out) § The ADT is a queue

September 2004 John Edgar 92 ¡ In a queue items are inserted at the back and removed from the front § As an aside queue is just the British (i.e. correctJ) word for a line (or line-up) ¡ Queues are FIFO (First In First Out) data structures – fair data structures

September 2004 John Edgar 93 ¡ Server requests § Instant messaging servers queue up incoming messages § Database requests ¡ Print queues ¡ Operating systems often use queues to schedule CPU jobs ¡ Various algorithm implementations

September 2004 John Edgar 94 ¡ A queue should implement at least the first two of these operations: § insert – insert item at the back of the queue § remove – remove an item from the front § peek – return the item at the front of the queue without removing it ¡ Like stacks, it is assumed that these operations will be implemented efficiently § That is, in constant time

September 2004 John Edgar 95 Arrays

September 2004 John Edgar 96 ¡ Consider using an array as the underlying structure for a queue, we could § Make the back of the queue the current size of the array, much like the stack implementation § Initially make the front of the queue index 0 § Inserting an item is easy

front = 0 backback = = 0 2 back = 1 4 18 0 1 2 3 4 5 6 7

September 2004 John Edgar 97 ¡ What happens when items are removed? § Either move all remaining items down – slow § Or increment the front index – wastes space

front = 0 4 18 25 2 5 back = 5 0 1 2 3 4 5 6 7

front = 0 18 25 2 5 back = 4 0 1 2 3 4 5 6 7

front = 1 4 18 25 2 5 back = 5 0 1 2 3 4 5 6 7

September 2004 John Edgar 98 ¡ Neat trick: use a circular array to insert and remove items from a queue in constant time ¡ The idea of a circular array is that the end of the array “wraps around” to the start of the array 7 7 0

6 6 1

0 1 2 3 4 5 6 7 5 5 2

4 4 3

September 2004 John Edgar 99 ¡ The mod operator (%) calculates remainders: § 1%5 = 1, 2%5 = 2, 5%5 = 0, 8%5 = 3 ¡ The mod operator can be used to calculate the front and back positions in a circular array § Thereby avoiding comparisons to the array size § The back of the queue is: ▪ (front + num) % queue.length ▪ where num is the number of items in the queue § After removing an item the front of the queue is: ▪ (front + 1) % queue.length;

September 2004 John Edgar 100 front 0 //C++ Code Queue q; num 10 q.insert(6);

6 6 0 1 2 3 4 5

insert item at (front + num) % queue.length

September 2004 John Edgar 101 front 0 //Java Code Queue q = new Queue(); num 12345 q.insert(6); q.insert(4); 6 6 4 7 3 8 q.insert(7); q.insert(3); 0 1 2 3 4 5 q.insert(8);

insert item at (front + num) % queue.length

September 2004 John Edgar 102 front 120 // C++ Code Queue q; num 543 q.insert(6); q.insert(4); 6 6 4 7 3 8 9 q.insert(7); q.insert(3); 0 1 2 3 4 5 q.insert(8); make front = (0 + 1) % 6 = 1 q.remove();//front to 1 make front = (1 + 1) % 6 = 2 q.remove();//front to 2 q.insert(9);

September 2004 John Edgar 103 front 2 //Java Code Queue q = new Queue(); num 45 q.insert(6); q.insert(4); 5 5 7 3 8 9 q.insert(7); q.insert(3); 0 1 2 3 4 5 q.insert(8); q.remove();//front to 1 q.remove();//front to 2 insert at (front + num) % 6 = (2 + q.insert(9); 4) % 6 = 0 q.insert(5);

September 2004 John Edgar 104 Linked Lists

September 2004 John Edgar 105 ¡ Removing items from the front of the queue is straightforward ¡ Items should be inserted at the back of the queue in constant time § So we would like to avoid traversing through the list § Use a second node reference to keep track of the node at the back of the queue ▪ Requires a little extra administration

September 2004 John Edgar 106 // C++ Code 6 6 Queue q; q.insert(6); front back

September 2004 John Edgar 107 //Java Code 6 6 Queue q = new Queue(); q.insert(6); front 4 q.insert(4); back

September 2004 John Edgar 108 // C++ Code 6 6 Queue q; q.insert(6); front 4 q.insert(4); q.insert(7); back 7 7

September 2004 John Edgar 109 //Java Code 6 6 Queue q = new Queue(); q.insert(6); front 4 q.insert(4); q.insert(7); back q.insert(3); 7 7

3 3

September 2004 John Edgar 110 //Java Code 6 6 Queue q = new Queue(); q.insert(6); front 4 q.insert(4); q.insert(7); back q.insert(3); 7 7 q.remove();

3 3

September 2004 John Edgar 111 September 2004 John Edgar 112 ¡ Items in a priority queue are given a priority value § Which could be numerical or something else ¡ The highest priority item is removed first ¡ Uses include § System requests § Data structure to support Dijkstra’s Algorithm

September 2004 John Edgar 113 ¡ Can items be inserted and removed efficiently from a priority queue? § Using an array, or § Using a linked list? ¡ Note that items are not removed based on the order in which they are inserted

September 2004 John Edgar 114 September 2004 John Edgar 115 ¡ Abstract Data Types (ADTs) § Specify data and operations ▪ But not how they are stored nor how they are done ▪ Allow usage without knowing all the details § Examples: Stacks and Queues ▪ Array-based implementations ▪ Linked list-based implementations

September 2004 John Edgar 116 ¡ Linked lists § Chain of Node data structures ▪ Each Node contains data and reference to next Node § Can be used to store an a priori unknown amount of data ▪ Allocate Nodes one at a time § Efficient insertion/deletion § Some overhead vs. array

September 2004 John Edgar 117 ¡ Memory § Arrays stored as contiguous block of memory ▪ Fast access via offset calculations § Static memory (stack memory) versus dynamic memory (heap memory) ▪ Variable in stack memory is gone after a function / method call is complete ▪ Variable in heap memory sticks around

September 2004 John Edgar 118 ¡ Carrano § Ch. 3, 4, 6, 7

September 2004 John Edgar 119