CSI 312: Data Structures Linked Lists, Stacks and Queues Dr. Yousef Qawqzeh Data Structure • A construct that can be defined within a programming language to store a collection of data – one may store some data in an array of integers, an array of objects, or an array of arrays 7/1/2016 Abstract Data Type (ADT) • Definition: a collection of data together with a set of operations on that data – specifications indicate what ADT operations do, but not how to implement them – data structures are part of an ADT’s implementation • Programmer can use an ADT without knowing its implementation. 7/1/2016 Typical Operations on Data • Add data to a data collection • Remove data from a data collection • Ask questions about the data in a data collection. E.g., what is the value at a particular location, and is x in the collection? 7/1/2016 Why ADT • • • • • Hide the unnecessary details Help manage software complexity Easier software maintenance Functionalities are less likely to change Localised rather than global changes 7/1/2016 Illustration 7/1/2016 Linked Lists Lists • List: a finite sequence of data items a1, a2, a3, …, an • Lists are pervasive in computing – e.g. class list, list of chars, list of events • Typical operations: – – – – – – – 7/1/2016 Creation Insert / remove an element Test for emptiness Find an item/element Current element / next / previous Find k-th element Print the entire list Array-Based List Implementation • One simple implementation is to use arrays – A sequence of n-elements • Maximum size is anticipated a priori. • Internal variables: – – – – Maximum size maxSize (m) Current size curSize (n) Current index cur Array of elements listArray listArray cur curSize n 7/1/2016 a 1 a2 a3 an 0 n-1 1 2 unused m Inserting Into an Array • While retrieval is very fast, insertion and deletion are very slow – Insert has to shift upwards to create gap Example : insert(2, it, arr) Size arr 8 a 1 a 2 a3 a 4 a 5 a 6 a 7 a8 Step 2 : Write into gap Size 9 8 Step 3 : Update Size 7/1/2016 Step 1 : Shift upwards arr a1 a2 it a3 a4 a5 a6 a7 a8 Coding typedef int int int } LIST struct { arr[MAX]; max; size; void insert(int j, int it, LIST *pl) { // pre : 1<=j<=size+1 int i; for (i=pl->size; i>=j; i=i-1) // Step 1: Create gap { pl->arr[i+1]= pl->arr[i]; }; pl->arr[j]= it; } 7/1/2016 // Step 2: Write to gap pl->size = pl->size + 1; // Step 3: Update size Deleting from an Array • Delete has to shift downwards to close gap of deleted item Example: deleteItem(4, arr) size 9 arr a1 a2 it a3 a4 a 5 a6 a7 a8 Step 1 : Close Gap size 98 arr a1 a2 it a3 a5 a6 a7 a8 a8 Step 2 : Update Size 7/1/2016 Not part of list Coding void delete(int j, LIST *pl) { // pre : 1<=j<=size for (i=j+1; i<=pl->size; i=i+1) // Step1: Close gap { pl->arr[i-i]=pl->arr[i]; }; // Step 2: Update size pl->size = pl->size - 1; } 7/1/2016 Linked List Approach • Main problem of array is the slow deletion/insertion since it has to shift items in its contiguous memory • Solution: linked list where items need not be contiguous item next with nodes of the form ai • Sequence (list) of four items < a1,a2 ,a3 ,a4 > can be represented by: head represents null a1 7/1/2016 a2 a3 a4 Pointer-Based Linked Lists • A node in a linked list is usually a struct struct Node A node { int item Node *next; }; //end struct • A node is dynamically allocated Node *p; p = malloc(sizeof(Node)); 7/1/2016 Pointer-Based Linked Lists • The head pointer points to the first node in a linked list • If head is NULL, the linked list is empty – head=NULL • head=malloc(sizeof(Node)) 7/1/2016 A Sample Linked List 7/1/2016 Traverse a Linked List • Reference a node member with the -> operator p->item; • A traverse operation visits each node in the linked list – A pointer variable cur keeps track of the current node for (Node *cur = head; cur != NULL; cur = cur->next) x = cur->item; 7/1/2016 Traverse a Linked List The effect of the assignment cur = cur->next 7/1/2016 Delete a Node from a Linked List • Deleting an interior/last node prev->next=cur->next; • Deleting the first node head=head->next; 7/1/2016 Delete a Node from a Linked List Deleting a node from a linked list Deleting the first node 7/1/2016 Insert a Node into a Linked List • To insert a node between two nodes newPtr->next = cur; prev->next = newPtr; Inserting a new node into a linked list CSI 312 Insert a Node into a Linked List • To insert a node at the beginning of a linked list newPtr->next = head; head = newPtr; Inserting at the beginning of a linked list Stacks What is a Stack? • A stack is a list with the restriction that insertions and deletions can be performed in only one position, namely, the end of the list, called the top. • The operations: push (insert) and pop (delete) push(o) pop Top 7/1/2016 3 2 7 6 Stack ADT Interface • The main functions in the Stack ADT are (S is the stack) boolean isEmpty(); // return true if empty boolean isFull(S); // return true if full void push(S, item); // insert item into stack void pop(S); // remove most recent item void clear(S); // remove all items from stack Item top(S); // retrieve most recent item Item topAndPop(S); // return & remove most recent item 7/1/2016 Sample Operation Stack S = malloc(sizeof(stack)); push(S, “a”); s push(S, “b”); push(S, “c”); d top a push(S, “e”); b pop(S); c d=top(S); pop(S); 7/1/2016 Implementation by Linked Lists • Can use a Linked List as implementation of stack StackLL lst Top of Stack = Front of Linked-List LinkedListItr head a1 7/1/2016 a2 a3 a4 Code struct Node { int element; Node * next; }; typedef struct Node * STACK; 7/1/2016 More code 7/1/2016 More Code 7/1/2016 Implementation by Array • use Array with a top index pointer as an implementation of stack StackAr arr 0 1 2 3 4 A A B C D E 5 top 7/1/2016 6 F 7 8 9 Code 7/1/2016 More code 7/1/2016 More code 7/1/2016 Effects 7/1/2016 Applications • Many application areas use stacks: – line editing – bracket matching – postfix calculation – function call stack 7/1/2016 Line Editing • A line editor would place characters read into a buffer but may use a backspace symbol (denoted by ) to do error correction • Refined Task – read in a line – correct the errors via backspace – print the corrected line in reverse Input : abc_defgh2klpqrwxyz : abc_defg2klpwxyz Reversed Output : zyxwplk2gfed_cba Corrected Input 7/1/2016 The Procedure • Initialize a new stack • For each character read: – if it is a backspace, pop out last char entered – if not a backspace, push the char into stack • To print in reverse, pop out each char for output Input : fghryz r z h g y Corrected Input : fyz f Reversed Output : zyf Stack 7/1/2016 Bracket Matching Problem • Ensures that pairs of brackets are properly matched • An Example: {a,(b+f[4])*3,d+f[5]} • Bad Examples: 7/1/2016 (..)..) // too many closing brackets (..(..) // too many open brackets [..(..]..) // mismatched brackets Informal Procedure Initialize the stack to empty For every char read if open bracket then push onto stack if close bracket, then return & remove most recent item from the stack if doesn’t match then flag error if non-bracket, skip the char read Example {a,(b+f[4])*3,d+f[5]} [ ] ([ ]) { } Stack 7/1/2016 Postfix Calculator • Computation of arithmetic expressions can be efficiently carried out in Postfix notation with the help of a stack. Infix Prefix Postfix - arg1 op arg2 - op arg1 arg2 - arg1 arg2 op postfix infix (2*3)+4 2 3 * 4 + 2*3+4 2*(3+4) 7/1/2016 2 3 4 + * Informal Procedure Initialise stack S For each item read. If it is an operand, push on the stack If it is an operator, pop arguments from stack; perform operation; push result onto the stack Expr 2 3 4 + * 7/1/2016 push(S, 2) push(S, 3) push(S, 4) arg2=topAndPop(S) arg1=topAndPop(S) push(S, arg1+arg2) arg2=topAndPop(S) arg1=topAndPop(S) push(S, arg1*arg2) 4 3+4=7 3 2*7=14 2 Stack Summary • The ADT stack operations have a lastin, first-out (LIFO) behavior • Stack has many applications – algorithms that operate on algebraic expressions – a strong relationship between recursion and stacks exists • Stack can be implemented using arrays or linked lists 7/1/2016 Queues What is a Queue? • Like stacks, queues are lists. With a queue, however, insertion is done at one end whereas deletion is done at the other end. • Queues implement the FIFO (first-in first-out) policy. E.g., a printer/job queue! • Two basic operations of queues: – dequeue: remove an item/element from front – enqueue: add an item/element at the back dequeue 7/1/2016 enqueue Queue ADT • Queues implement the FIFO (first-in first-out) policy – An example is the printer/job queue! enqueue(o) dequeue() isEmpty() getFront() 7/1/2016 createQueue() Sample Operation Queue *Q; enqueue(Q, “a”); q enqueue(Q, “b”); enqueue(Q, “c”); d=getFront(Q); dequeue(Q); enqueue(Q, “e”); dequeue(Q); 7/1/2016 d back front a b c e Queue ADT interface • The main functions in the Queue ADT are (Q is the queue) void enqueue(o, Q) // insert o to back of Q void dequeue(Q); // remove oldest item Item getFront(Q); // retrieve oldest item boolean isEmpty(Q); // checks if Q is empty boolean isFull(Q); // checks if Q is full void clear(Q); // make Q empty } 7/1/2016 Implementation of Queue (Linked List) • Can use LinkedListItr as underlying implementation of Queues Queue lst addTail LinkedList head tail a1 7/1/2016 a2 a3 a4 Code struct Node { int element; Node * next; }; struct QUEUE { Node * front; Node * rear; }; 7/1/2016 More code 7/1/2016 More code CELL is a list node 7/1/2016 Implementation of Queue (Array) • use Array with front and back pointers as implementation of queue Queue arr 0 A front 7/1/2016 1 2 3 4 5 6 B C D E F G 7 8 9 back Circular Array • To implement queue, it is best to view arrays as circular structure 0 A 1 2 3 4 5 6 B C D E F G 7 8 9 back front front 0 9 Circular view of arrays.8 A back 1 B C 2 7 G D F E 3 6 5 7/1/2016 4 How to Advance • Both front & back pointers should make advancement until they reach end of the array. Then, they should re-point to beginning of the array front = adv(front); back = adv(back); int adv(int p) { int r = p+1; if (r<maxsize) return r; else return 0; } upper bound of the array 7/1/2016 Alternatively, use modular arithmetic: int adv(int p) { return ((p+1) % maxsize); } mod operator Sample Queue *Q; enqueue(Q, “a”); Q enqueue(Q, “b”); enqueue(Q, “c”); dequeue(Q); dequeue(Q); enqueue(Q, “d”); enqueue(Q, “e”); dequeue(Q); 7/1/2016 F=front B=back d a e b c F F F F B B B B Checking for Full/Empty State What does (F==B) denote? Queue Empty State size e f c d F F B 0 B size 4 Alternative - Leave a Deliberate Gap! No need for size field. Full Case : (adv(B)==F) 7/1/2016 e c d B F Queue Full State Summary • The definition of the queue operations gives the ADT queue first-in, first-out (FIFO) behavior • The queue can be implemented by linked lists or by arrays • There are many applications – Printer queues, – Telecommunication queues, – Simulations, – Etc. 7/1/2016