Presentation is loading. Please wait.

Presentation is loading. Please wait.

The List, Stack, and Queue ADTs

Similar presentations


Presentation on theme: "The List, Stack, and Queue ADTs"— Presentation transcript:

1 The List, Stack, and Queue ADTs
Abstract data type (ADT): An ADT is a set of objects together with a set operations to be performed on these objects. An ADT corresponds to (one or more) classes in an OO language such as C++ and Java. There may be more than one implementation for the same ADT, with different advantages in terms of efficiency, ease of use, ease of implementation. We will review the ADTs for lists, stacks, and queues, mainly based on array or linked list implementations. We will also study some typical applications of these ADTs to learn when to use them.

2 The List ADT: A list of objects A1, A2, …, An, where n is the size of the list. Some typical operations include find, insert, remove, findKth, printList, makeEmpty. An array implementation requires (n) time for insert and remove operations in the worst case. A linked list implementation offers the flexibility of dynamically growing the list and of O(1) time insert and remove operations (assuming the position in the list is known). insert remove

3 Programming Details in Linked List Implementation:
Use three classes for the List ADT: (1) ListNode (for the individual nodes, or objects, in the list); (2) LinkedList (for the list object itself consisting of the nodes); and (3) LinkedListItr (for the current location in a LinkedList object, in a particular application). Each class consists of one or more constructor functions. The ListNode class contains two member variables: element for the “data” field and next for the link field lining to the next node in the list.

4 Linked List Implementation (cont’d):
The LinkedListItr class contains functions isPastEnd, retrieve, advance; and contains a member variable current for the current location in a LinkedList object. The LinkedList class may use a dummy header node to be the first node in the list which somewhat simplifies the insert function (thus, an “empty” list contains the header node as the only node in the list). The functions included in the class are find, remove, findPrevious, and insert. Variations of linked list implementations include doubly linked lists – each node uses two link fields to link to the two neighboring nodes in the list, respectively; and circular linked lists – the last node in the list is linked to the front of the list, eliminating the header node.

5 Two Applications of Linked Lists:
Polynomials (in a single variable x) – use nodes to represent the non-zero terms arranged in decreasing order of the exponents; typical operations include: constructor (initialize the zero polynomial in constant time); add (in O(m + n) time for two polynomials of m and n terms, respectively); and multiply (in O(mn(min(m,n)) time for two polynomials of m and n terms, respectively). Radix sort – sort n numbers in the range of [0..m–1], where m  bp for some constant p (of moderate size), based on a multi-pass bucket sort technique, resulting in O(p(n + b)) time using O(b+ n) space; this is a linear time O(n) algorithm when p is a constant and b = O(n). For example, m = 232 for 32-bit unsigned integers. Choose b = 211 = 2048, then m  b3 and n numbers can be sorted in O(3(n + 211) = O(n) time.

6 Algorithm for adding two polynomials in linked lists p, q:
set p, q to point to the two first nodes (no headers) initialize a linked list r for a zero polynomial while p != null and q != null if p.exp > q.exp create a node storing p.coeff and p.exp, then insert at the end of list r; advance p else if q.exp > p.exp create a node storing q.coeff and q.exp, then insert at the end of list r; advance q else if p.exp == q.exp if p.coeff + q.coeff != create a node storing p.coeff + q.coeff and p.exp, then insert at the end of list r advance p, q if p != null copy the remaining terms of p to end of r else if q != null copy the remaining terms of q to end of r Each iteration of the while loop takes O(1) time, moving at least one of the two pointers p and q one position over. Any remaining terms will be copied after the loop. Thus the total time is O(m+n) because there are a total of m+n terms in the two polynomials.

7 Algorithm for multiplying two polynomials in linked lists p, q:
set p to the first node (no header) of one polynomial initialize a linked list r for a zero polynomial while p != null copy list q to a new list t multiply the term pointed by p to each term of t by set t.exp += p.exp set t.coeff *= p.coeff add polynomial t to polynomial r (then discard t) advance p If polynomial p, q have m, n terms, respectively, the while loop runs m iterations. In each iteration, producing the polynomial t (copy and multiply) costs O(n) time since the length of t is O(n). Adding polynomial t to r runs in time O(n + length of r). Since the length of r in the beginning of iteration k is O((k –1)n), so the total time of the algorithm is The choice of m and n is arbitrary, so we can achieve O(mn(min(m,n)) time.

8 An Example demonstrating Radix Sort:
Given n = 10 numbers: 64, 8, 216, 512, 27, 729, 0, 1, 343, Use b = 10 buckets and sort the numbers using the least significant digits in the first pass (i.e., distribute the numbers into10 buckets based on the last digits), then sort the list of numbers resulting from the first pass using the next significant digits; finally, sort the list (of pass two) using the most significant digits. 10 buckets list after first pass list after second pass list after the third pass

9 The Stack ADT: A stack is a list with two operations insert and delete, such that the most recently inserted item is deleted first (hence the name LIFO, for a last-in-first-out structure). A stack is typically pictured as a “stack of items” arranged vertically where the insertion and deletion take place at the “top” The stack ADT supports operations isFull, isEmpty, makeEmpty, push, pop, top. pop (delete) push (insert) top

10 Application of Stacks: Expressions from Infix to Postfix.
An infix expression is what we typically use in arithmetic (or algebra), where an operator (+, –, *, etc.) is written between the two neighboring operands (for binary operators). For example, a + b, or a + b * c (multiply first before add due to higher precedence), (a + b) * c (use parentheses to alter precedence). As much as we are used to infix expressions, they are harder for the computer to process and evaluate. A postfix expression (also known as Polish notation since it was invented by a Polish logician in the 1920s), uses a different convention for writing arithmetic expressions, totally doing away the parentheses and making the expressions more efficient to evaluate by computers. The idea is to write each operator after the two corresponding operands. For example, ab+, abc*+, ab+c*, respectively, are the postfix expressions corresponding to the preceding examples.

11 The idea of converting infix to postfix using a stack:
Example. Convert infix a + b * c to postfix abc*+. We scan the input stream left to right, output each operand as they are scanned. The main idea is that when encountering an operator, it is pushed onto the stack if the stack is empty, or if it has a higher precedence than that of the stack top. Thus, the following chart demonstrates the snapshots of the input stream vs. the stack and the output during the conversion process: next token stack output comments (Initially) empty none a empty a always output operand a push when stack empty b + a b always output operand * + * a b push if higher precedence c + * a b c always output operand (at end) empty a b c * + pop everything off stack

12 Another example: Convert infix a*(b + c)/d to postfix abc+*d/.
next token stack output comments (Initially) empty none a empty a output operand * * a push operator if stack empty ( *( a always push ( b *( ab output operand + *(+ ab push operator if stack top ( c *(+ abc output operand ) * abc pop all operators until ( / / abc+* pop *, push / d / abc+*d output operand (at end) empty abc+*d/ pop everything off stack Note that the token ( is pushed onto stack when scanned; once it is in the stack all operators are considered with a higher precedence against (. Also, we need to resolve operators with left or right-associative properties. For example, a+b+c means (a+b)+c but a^b^c means a^(b^c).

13 We define the precedence levels of various operators including the parentheses, distinguishing whether the operators are currently inside the stack or they are incoming tokens. operator tokens in-stack precedence in-coming precedence (ISP) (ICP) ) (N/A) (N/A) ^ *, / , – ( $ – (N/A) The idea is that when encountering an in-coming operator, pop all operators in the stack that have a higher or equal ISP than the ICP of the new operator, then push the new operator onto the stack. The initial stack is marked with a “bottom marker” $ with a –1 precedence, which serves as a sentinel symbol.

14 Algorithm Converting Infix Expression E to Postfix Notation:
create a stack and push the bottom-marker $ onto stack perform the following steps forever (until exit out of it) set x = nextToken(E) if x = end-of-input pop all operators off the stack and output (except the marker $) exit out of the loop else if x = operand, output x else if x = ‘)’ pop all operators off stack and output each until ‘(‘; pop ‘(‘ off but don’t output it else pop all operators off stack as long as their ISP  ICP(x) push x onto stack Note that the time complexity of the algorithm is O(n), n = the number of input tokens. This algorithm assumes no input errors.

15 The Queue ADT: A queue is a fist-in-first-out (FIFO) structure in which the first (oldest) object inserted will be the first to be deleted. A linked list implementation is straightforward, with a “pointer” front pointing to the fist (oldest) object and another pointer back pointing to the most recent object in the list. Insertions take place at the back of the list, deletions at the front. Both operations can be done in O(1) time. front After deletion back After insertion (deleted object) New object

16 A circular (wrap-around) array implementation of queues:
When a fixed sized array is used to store the list of objects in a queue, as objects come and leave, the portion of the array that contains the current objects “drifts” towards the end of the array, demonstrated in the following snapshots: A clever solution is let the array wraparound, so that its end continues to the front. front back (drifting towards back) front back

17 A circular array implementation of queues (cont’d):
The class uses 4 private instance variables: the array (of some default size), currentSize (count of current objects), front, and back (indexes of the first and last objects in the array, respectively). Initially, front = currentSize = 0, back = –1. Some public class methods include isEmpty(), isFull(), makeEmpty(), getFront(), enqueue(), and dequeue(). Queues are used for scheduler applications, as in operating systems, event processing, and graph traversals. back front wrap-around


Download ppt "The List, Stack, and Queue ADTs"

Similar presentations


Ads by Google