DATA STRUCUTRES A data structure is a particular way of storing and organizing data in a computer so that it can be used efficiently. An algorithm, which is a finite sequence of instructions, each of which has a clear meaning and can be performed with a finite amount of effort in a finite length of time. 1
Types of data structures Primitive data structures. (int, float, integer, pointer) Non Primitive data structures. (1) Linear data structures. (Array, Stack, Linked list, Queue) (2) Non linear data structures. (trees, graph) 2
Primitive data structures The integers, reals, logical data, character data, pointer and reference are primitive data structures. Data structures that normally are directly operated upon by machine-level instructions are known as primitive data structures. 3
Non Primitive data structures These are more complex data structures. These data structures are derived from the primitive data structures. They stress on formation of sets of homogeneous and heterogeneous data elements. 4
Stack- Works in first in last out order. The element inserted first in stack is removed last. Queue- First in First out order. The element inserted first is removed first. Linked list- Stored data in a linear fashion. Trees- Stores data in a non linear fashion with one root node and sub nodes. 5
ARRAY Array means collection. An array is used to store elements of the same type. It is a very popular and useful data structure and stores data elements in contiguous locations. 6
IMPLEMENTATION OF LIST Array implementation of list Linked list implementation of list Cursor implementation of list 7
Types of Linked list Singly linked list Doubly linked list Circular linked list (1) Circular singly linked list (2) Circular doubly linked list 8
Singly Linked List A singly linked list is a linked list in which each node contains Data and only one link field pointing to the next node in the list. 9
Doubly Linked List A Doubly linked list is a linked list in which each node has three fields namely data field, forward link (FLINK) or Next and Backward Link (BLINK) or previous. FLINK points to the successor node in the list whereas BLINK points to the predecessor node. 10
Circular Linked List It is a doubly linked list In circular linked list, the pointer of the last node points to the first node and first node pointing to last node. Circular linked list can be implemented as Singly linked list and Doubly linked list with or without headers. 11
Advantages of Circular Linked List It allows to traverse the list starting at any point. It allows quick access to the first and last records. 12
Advantages of doubly linked list * Deletion operation is easier. * Finding the predecessor & Successor of a node is easier. Disadvantage * More Memory Space is required since it has two pointers. 13
Chapter 3: Lists, Stacks and Queues Concept of Abstract Data Type (ADT) How to efficiently perform operations on list Stack ADT and its applications Queue ADT and its applications Cursor implementation of Linked list 14
3.1 Abstract Data Type Modular program Advantages: (1) debugging (2) team work (3) easy to modify 15
3.1 Abstract Data Type Abstract Data Type Some data, associated with a set of operations mathematical abstractions just as integer, boolean Basic idea write once, use many Any changes are transparent to the other modules 16
3.2 List ADT A general list of elements: A 1, A 2,, A N, associated with a set of operations: Insert: add an element Delete: remove an element Find: find the position of an element (search) FindKth: find the kth element 17
3.2 List ADT Different implementations: Array Linked list Doubly linked list Circularly linked list (or, Circular list) 18
3.2 List ADT Array Insert: O(N) Delete: O(N) Find: O(N) FindKth: O(1) The array size is usually unknown 19
3.2 List ADT Linked list Insert: O(1) Delete: O(1) Find: O(N) FindKth: O(N) Linked list with a header A 1 A 2 A 3 header A 1 A 2 A 3 20
3.2 List ADT Common errors memory allocation segmentation violation check for NULL pointer free memory 21
Doubly linked list struct node { int Number; struct node *left; struct node *right; } 3.2 List ADT A 1 A 2 A 3 22
Applications of Linked List 1. Polynomial ADT 2. Radix Sort 3. Multilist 23
3.2.1 List ADT: Example1 Single-variable Polynomials N F ( X ) i 0 A i X i typedef struct { int CoeffArray[MaxDegree+1]; int HighPower; } *polynomial; 24
3.2.1 List ADT: Example 1 Initialize a polynomial void ZeroPolynomial(Polynomial Poly) { int j; for (j = 0; j < MaxDegree; j++) poly->coeffarray[j] = 0; Poly->HighPower = 0; } 25
3.2.1 List ADT: Example 1 Add two polynomials void AddPolynomial(const Polynomial Poly1, const Polynomial Poly2, Polynomial PolySum) { int j; } ZeroPolynomial(PolySum); PolySum->HighPower = Max(Poly1-> HighPower, Poly2 -> HighPower); for (j=polysum->highpower; j>=0; j--) PolySum->CoeffArray[j] = Poly1-> CoeffArray [j] + Poly2-> CoeffArray[j]; 26
3.2.1 List ADT: Example 1 Multiply two polynomials void MultPolynomial(const Polynomial Poly1, const Polynomial Poly2, Polynomial PolyProd) { int j,k; } ZeroPolynomial(PolyProd); PolyProd->HighPower = Poly1-> HighPower+ Poly2 -> HighPower; for (j=0; j<=poly1->highpower; j++) for (k=0; k<=poly2->highpower; k++) PolyProd->CoeffArray[j+k] += Poly1-> CoeffArray [j]* Poly2-> CoeffArray[k]; 27
3.2.1 List ADT: Example 1 Multiply two polynomials: limitation Consider the following situation P 1 (X) = 10 X 1000 + 5X 14 + 1 P 2 (X) = 3X 1990-2X 1492 +11X +5 Most of the time is spent multiplying zeros 28
3.2.1 List ADT: Example 1 Multiply two polynomials: better structure struct node { int Coefficient; int Exponent; struct node *Next; }; 29
Radix Sort Radix sort is sometimes known as card sort. 0 1 512 343 64 125 216 27 8 729 -------------------------------------------------------------------- 0 1 2 3 4 5 6 7 8 9 Buckets after first step of radix sort 8 729 1 216 27 0 512 125 343 64 ---------------------------------------------------------------------- 0 1 2 3 4 5 6 7 8 9 Buckets after the second pass of radix sort 30
64 27 8 1 0 125 216 343 512 729 ------------------------------------------------------------------------ 0 1 2 3 4 5 6 7 8 9 Buckets after the last pass of radix sort OUTPUT: 0 1 8 27 64 125 216 343 512 729 31
3.2.1 List ADT: Example 1 Linked list representation of the previous structure 32
3.2.2 List ADT: Example 2 Multilists A university with 40,000 students and 2,500 subjects needs to generate 2 reports: 1. Lists of registration for each class 2. Classes that each student registered. Implementation: construct 2D array (40Kx2.5K) = 100M entries if each student takes 3 subjects => only 120K entries (~0.1% of 100M) => waste of resources. 33
3.2.2 List ADT: Example 2 Multilists 34
The Stack ADT A stack is a list with the restriction that inserts and deletes can be performed in only one position, namely the end of the list called the top. The fundamental operations on a stack are push, which is equivalent to an insert, and pop,which deletes the most recently inserted element. 35
Stack ADT Stacks are sometimes known as LIFO (last in, first out) lists. 36
3.3 Stack ADT Stack model 37
3.3 Stack ADT LIFO structure Access from the top Basic Operations empty (s) pop (s) push (s, i) top (s) 38
Stack implementation Array implementation of stack Linked list implementation of stack 39
3.3.1Implementation of stack using Linked List struct Node; typedef struct Node *PtrToNode; typedef PtrToNode Stack; struct Node { ElementType Element; PtrToNode Next; }; 40
3.3.1Implementation of stack using Linked List int IsEmpty(Stack S); Stack CreatStack(void); void DisposeStack(Stack S); void MakeEmpty(Stack S); void Push(ElementType X, Stack S); void Pop(Stack S); ElementType Top(Stack S); 41
3.3.1Implementation of stack using Linked List Test for an empty stack int Isempty(Stack S) { return (S Next == NULL); } 42
3.3.1Implementation of stack using Linked List Create an empty stack stack CreatStack(void) { Stack S; S=malloc(sizeof(struct Node)); if (S==NULL) FatalError( Out of space!!! ); S->Next = NULL; return(s); } void MakeEmpty(Stack S) { if (S==NULL) printf( Error message! ); else } while (!IsEmpty(S)) pop(s); 43
3.3.1Implementation of stack using Linked List Push onto a stack void Push(ElementType X, Stack S) { PtrtoNode TmpCell; TmpCell = malloc(sizeof(struct Node)); } if (TmpCell == NULL) fatalerror( message ); else { TmpCell Element = X; TmpCell Next = S Next; S } Next = TmpCell; 44
3.3.1Implementation of stack using Linked List Return top element in a stack ElementType Top(Stack S) { if (!IsEmpty(S)) return (S Next Element); Error( Empty stack ); return 0; } 45
3.3.1Implementation of stack using Linked List Pop from a stack void Pop(Stack S) { PtrToNode FirstCell; if (ISEmpty(S); Error ( Empty stack ); else { FirstCell = S Next; S Next =S Next Next; free(firstcell); } } 46
3.3.2 Implementation of stack using Array struct StackRecord { int Capacity; int TopOfStack; ElementType *Array; }; typedef struct StackRecord *Stack; 47
3.3.2 Implementation of stack using Array Stack creation Stack (CreateStack( int MaxElements ) { Stack S; if (MaxElements < MinStackSize) Error( Stack size is too small ); S = malloc(sizeof(struc StackRecord ) ); if ( S == Null) FatalError( Out ofspace!!! ); S Array = malloc(sizeof(elementtype) * MaxElements); 48
3.3.2 Implementation of stack using Array If (S Array == Null) FatalError( Out of space!!! ); S Capacity = MaxElements; MakeEmpty(S); return S; } 49
3.3.2 Implementation of stack using Array Test for empty stack Int Isempty (stack S) { return S TopOfStack == EmptyTOS; } 50
3.3.2 Implementation of stack using Array Create an empty stack void MakeEmpty (stack S) { S TopOfStack = EmptyTOS; } 51
3.3.2 Implementation using Array Push an element onto the stack void Push (ElementType X, Stack S) { if (IsFull(S) ) Error( Full Stack ); else S Array[ ++S TopOfStack] = X ; } 52
3.3.2 Implementation using Array Return top of stack ElementType Top( Stack S) { if (!IsEmpty(S)) return S Array[S TopOfStack]; Error( Empty stack ); return 0 ; } 53
3.3.2 Implementation using Array Pop element from stack void Pop (Stack S) { if (Isempty (S)) Error( Empty stack ); else } S TopOfStack--; 54
3.3.2 Implementation using Array Top element and pop a stack ElementType TopAndPop( Stack S) { if (!IsEmpty(S)) return S Array[S TopOfStack--]; Error( Empty stack ); return 0; } 55
Exception Conditions in stack Overflow : Attempt to insert an element, when the queue is full is said to be overflow condition. Underflow : Attempt to delete an element from the queue, when the queue is empty is said to be underflow. 56
Types of notations to represent arithmetic expression Infix notation Postfix notation or (Reverse polish notation) Prefix notation 57
INFIX In Infix notation, The arithmetic operator appears between the two operands to which it is being applied. POSTFIX The arithmetic operator appears directly after the two operands to which it applies. Also called reverse polish notation. 58
PREFIX The arithmetic operator is placed before the two operands to which it applies. Also called as polish notation. 59
Applications of Stack Balancing Symbols Postfix Expressions Infix to Postfix Conversion Function Calls(Example: Factorial) Towers of Hanoi 8 Queen Problem. 60
Balancing Symbols 3.3.4 Applications Make an empty stack. Read characters until end of file. If the character is an opening symbol, push it onto stack. If it is a closing symbol, then if the stack is empty, report an error. Otherwise, pop the stack. If the symbol popped is not the corresponding opening symbol, then report an error. At the end of file, if the stack is not empty, report an error. 61
3.3.4 Applications Postfix Expressions 4.99+5.99+6.99*1.06 = 19.05 or 18.39 postfix notation of (4.99*1.06 + 5.99 + 6.99*1.06) is 4.99 1.06 * 5.99 + 6.99 1.06 * + Why do we need postfix notation? 62
Evaluating Postfix Expression Read the postfix expression one character at a time until it encounters the delimiter `#'. Step 1 : - If the character is an operand, push its associated value onto the stack. Step 2 : - If the character is an operator, POP two values from the stack, apply the operator to them and push the result onto the stack. 63
3.3.4 Application: Postfix How does postfix work? e.g. 6 5 2 3 + 8 * + 3 + * 64
3.3.4 Application : Postfix 65
3.3.4 Application : Postfix 66
3.3.4 Applications Expression: Operands: numbers Operators: +, -, *, / Parenthesis: ( ) Precedence: '(' and ')' have the highest precedence '*' and '/ 'have lower precedence than '(' and ')' '+' and '-' have lower precedence than '*' and '/' Converting infix expressions into postfix Infix A * B + C * D Postfix A B * C D * + 67
3.3.4 Application: infix postfix Stack -> Input Postfix String A A * * A * B A B + + A B * + C A B * C + * * A B * C + * D A B * C D pop stack A B * C D * + 68
3.3.4 Application: infix postfix (1) Initialize a stack to empty. (2) Read a symbol (2.1) If it is an operand, place onto the output. (2.2) If it is an operator (a) If it is a right parenthesis, then pop the stack, write symbols, until a left parenthesis is encountered. Remark: The '(' is popped, but not written as output. (b) If it is any other symbol, such as '+', '*', '(', pop entries from the stack until an entry of lower priority is found, or '(' is found. When the popping is done, push the operator onto the stack. Remark: If '(' is found, don t pop it. (3) Goto step (2). (4) If read the end of input, pop the stack until it is empty, writing symbols onto the output. 69
a + b * c + ( d * e + f ) * g Infix to Postfix Conversion 70
71
3.3.4 Application: infix postfix With parentheses: (A+B)*C output: AB+C* Stack -> Input Postfix String ( ( ( A A ( + + A ( + B A B ) A B + * * A B + * C A B + C pop stack A B + C * 72
Function Calls When a call is made to a new function all the variables local to the calling routine need to be saved, otherwise the new function will overwrite the calling routine variables. Similarly the current location address in the routine must be saved so that the new function knows where to go after it is completed. 73
Function call RECURSIVE FUNCTION TO FIND FACTORIAL : - int fact (int n) { int s; if (n = = 1) return (1); else s = n * fact (n - 1); return (s); } 74
Queue ADT The basic operations on a queue are enqueue, which inserts an element at the end of the list (called the rear), and dequeue, which deletes (and returns) the element at the start of the list (known as the front). 75
Queue ADT Model of a queue 76
3.4 Queue ADT Ordered collections of data items Delete item at front of the queue Insert item at rear of the queue A FIFO structure A B C D E front rear 77
3.4 Queue ADT Basic operations (1) Enqueue (ElementType X, Queue Q) (2) Dequeue(Queue Q) 78
Array Implementation of Queue In this implementation queue Q is associated with two pointers namely rear pointer and front pointer. To insert an element X onto the Queue Q, the rear pointer is incremented by 1 and then set Queue [Rear] = X To delete an element, the Queue [Front] is returned and the Front Pointer is incremented by 1. 79
3.4 Queue ADT Array implementation struct QueueRecord { int Capacity; int Front, Rear, Size; ElementType *Array; }; typedef struct QueueRecord *Queue; 80
3.4 Queue ADT Problem: May run out of rooms (1) Keep front always at 0 by shifting the contents up the queue, but the computer solution is inefficient (2) Use a circular queue (wrap around & use a length variable to keep track of the queue length) 81
3.4 Queue ADT static int Succ( int Value, Queue Q) { if (++Value == Q Capacity) Value = 0; return Value; } 82
3.4 Queue ADT void Enqueue (ElementType X, Queue Q ) { if (IsFull(Q) ) Error( Full queue ); else { Q Size++; Q Rear = Succ(Q Rear, Q); Q Array[Q Rear] = x; } } 83
3.4 Queue ADT void Dequeue( Queue Q ) { if( IsEmpty( Q ) ) Error( "Empty queue" ); else { Q->Size--; Q->Front = Succ( Q->Front, Q ); } } 84
Routine to test whether a queue is empty-array implementation int is_empty( QUEUE Q ) { return( Q->q_size == 0 ); } 85
Routine to make an empty queue-array implementation void make_null ( QUEUE Q ) { Q->q_size = 0; Q->q_front = 1; Q->q_rear = 0; } 86
DECLARATION FOR LINKED LIST IMPLEMENTATION OF QUEUE ADT Struct Node { int Element; Struct Node *Next; }* Front = NULL, *Rear = NULL; 87
DECLARATION FOR LINKED LIST IMPLEMENTATION OF QUEUE ADT Struct Node; typedef Struct Node * Queue; int IsEmpty (Queue Q); Queue CreateQueue (void); void MakeEmpty (Queue Q); void Enqueue (int X, Queue Q); void Dequeue (Queue Q); 88
ROUTINE TO CHECK WHETHER THE QUEUE IS EMPTY int IsEmpty (Queue Q) // returns boolean value / { // if Q is empty if (Q Next = = NULL) // else returns 0 return (1); } 89
ROUTINE TO CHECK AN EMPTY QUEUE Struct CreateQueue ( ) { Queue Q; Q = Malloc (Sizeof (Struct Node)); if (Q = = NULL) Error ("Out of Space"); MakeEmpty (Q); return Q; } 90
ROUTINE TO MAKE AN EMPTY QUEUE void MakeEmpty (Queue Q) { if (Q = = NULL) Error ("Create Queue First"); else while (! IsEmpty (Q) Dequeue (Q); } 91
ROUTINE TO ENQUEUE AN void Enqueue (int X) { Struct node *newnode; newnode = ELEMENT IN QUEUE Malloc (sizeof (Struct node)); if (Rear = = NULL) { newnode data = X; newnode Next = NULL; Front = newnode; Rear = newnode; } else { newnode data = X; newnode Next = NULL; Rear next = newnode; Rear = newnode; } } 92
ROUTINE TO DEQUEUE AN ELEMENT FROM THE QUEUE void Dequeue ( ) { Struct node *temp; if (Front = = NULL) Error("Queue is underflow"); else { 93
temp = Front; if (Front = = Rear) { Front = NULL; Rear = NULL; } else Front = Front Next; Print (temp data); free (temp); } } 94
3.4 Applications of Queues Print jobs Computer networks Batch processing in an operating system(os) Real-life waiting lines 95
Double Ended Queue (DEQUE) In Double Ended Queue, insertion and deletion operations are performed at both the ends. 96
Circular Queue In Circular Queue, the insertion of a new element is performed at the very first location of the queue if the last location of the queue is full, in which the first element comes just after the last element. 97
Advantages of Circular Queue It overcomes the problem of unutilized space in linear queues, when it is implemented as arrays. To perform the insertion of an element to the queue, the position of the element is calculated by the relation as Rear = (Rear + 1) % Maxsize. and then set Queue [Rear] = value. 98
ROUTINE TO INSERT AN ELEMENT IN CIRCULAR QUEUE void CEnqueue (int X) { if (Front = = (rear + 1) % Maxsize) print ("Queue is overflow"); else { if (front = = -1) front = rear = 0; else rear = (rear + 1)% Maxsize; CQueue [rear] = X; } } 99
To perform the deletion, the position of the Front printer is calculated by the relation Value = CQueue [Front] Front = (Front + 1) % maxsize. 100
ROUTINE TO DELETE AN ELEMENT FROM CIRCULAR QUEUE int CDequeue ( ) { if (front = = -1) print ("Queue is underflow"); else { X = CQueue [Front]; if (Front = = Rear) Front = Rear = -1; else Front = (Front + 1)% maxsize; } return (X); } 101
Cursor Implementation of Linked Lists The two important items present in a pointer implementation of linked lists are 1. The data is stored in a collection of structures. Each structure contains the data and a pointer to the next structure. 2. A new structure can be obtained from the system's global memory by a call to malloc and released by a call to free. 102
struct node { element_type element; node_ptr next; }; typedef node_ptr LIST; typedef node_ptr position; struct node CURSOR_SPACE[ SPACE_SIZE ]; Declarations for cursor implementation of linked lists 103
Consider if the value of L is 5 and the value of M is 3, then L represents the list a, b, e, and M represents the list c, d, f. L= header 104
Slot Element Next ---------------------- 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 0 An initialized CURSOR_SPACE 105
Slot Element Next L=5(a,b,e), M=3(c,d,f) ---------------------- 0-6 1 b 9 2 f 0 3 header(m) 7 4-0 5 header(l) 10 6-4 7 c 8 8 d 2 9 e 0 10 a 1 Example of a cursor implementation of linked lists 106
int is_empty( LIST L ) /* using a header node */ { return( CURSOR_SPACE[L].next == 0 } Function to test whether a linked list is empty--cursor implementation int is_last( position p, LIST L) /* using a header node */ { return( CURSOR_SPACE[p].next == 0 } Function to test whether p is last in a linked list-- cursor implementation 107
position find( element_type x, LIST L) /* using a header node */ { position p; /*1*/ p = CURSOR_SPACE[L].next; /*2*/ while( p && CURSOR_SPACE[p].element!= x ) /*3*/ p = CURSOR_SPACE[p].next; /*4*/ return p; } Find routine--cursor implementation 108
void delete( element_type x, LIST L ) { position p, tmp_cell; p = find_previous( x, L ); if(!is_last( p, L) ) { tmp_cell = CURSOR_SPACE[p].next; CURSOR_SPACE[p].next = CURSOR_SPACE[tmp_cell].next; cursor_free( tmp_cell ); } } Deletion routine for linked lists--cursor implementation 109
void insert( element_type x, LIST L, position p ) { position tmp_cell; /*1*/ tmp_cell = cursor_alloc( ) /*2*/ if( tmp_cell ==0 ) /*3*/ fatal_error("out of space!!!"); else { /*4*/ CURSOR_SPACE[tmp_cell].element = x; /*5*/ CURSOR_SPACE[tmp_cell].next = CURSOR_SPACE[p].next; /*6*/ CURSOR_SPACE[p].next = tmp_cell; } } Insertion routine for linked lists--cursor implementation 110
Exercises (1) Write a program to print out the elements of a singly linked list. (2) Given two sorted lists, L1 and L2, write a procedure to compute L1 n L2 using only the basic list operations. (3) Given two sorted lists, L1 and L2, write a procedure to compute L1u L2 using only the basic list operations. (4) Write the routines to implement queues using a. linked lists b. arrays 111
(4) A deque is a data structure consisting of a list of items, on which the following operations are possible: push(x,d): Insert item x on the front end of deque d. pop(d): Remove the front item from deque d and return it. inject(x,d): Insert item x on the rear end of deque d. eject(d): Remove the rear item from deque d and return it. Write routines to support the deque that take O(1) time per operation. 112