Vous êtes sur la page 1sur 285

Data Structures

Prepared By: Vikas karanth Last modified: August 08 2008

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 1

Stacks

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 2

Objectives

In this session, you will learnt to: Define a stack Describe the operations on a stack Implement a stack as a special case of a linked list Describe applications of stacks

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 3

What is a Stack?

A stack is a data structure in which insertions and deletions can only be done at the top. A common example of a stack, which permits the selection of only its end elements, is a stack of books. A person desiring a book can pick up only the book at the top, and if one wants to place a plate on the pile of plates, one has to place it on the top.
Copyright Vikas karanth,2007 All rights reserved.
4

08/20/08

What is a Stack?

You would have noticed that whenever a book is placed on top of the stack, the top of the stack moves upward to correspond to the new element (the stack grows).

And, whenever a book is picked, or removed from the top of the stack, the top of the stack moves downward to correspond to the new highest element (the stack shrinks).

What is a Stack?

top top 10

12 10 8 6

top

14 12 10 8 6 4 2

8 6

(a)

(b) (c)

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 6

Characteristics of a Stack

When you add an element to the stack, you say that you push it on the stack, and if you delete an element from a stack, you say that you pop it from the stack. Since the last item pushed into the stack is always the first item to be popped from the stack, a stack is also called as a Last In, First Out or a LIFO structure.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 7

Characteristics of a Stack

Unlike an array that is static in size , stack grows dynamically at run time. An ideal implementation of a stack would be

Using dynamic array,


linked list in which insertions and deletions can only be done at the top of the list.
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

Operations on Stacks

Some of the typical operations performed on stacks are: create (s) to create s as an empty stack

push (s, i)
pop (s)

to insert the element i on top of the stacks

to remove the top element of the stack and to return the removed element. to return the top element of stack(s)

top (s)

empty(s) to check whether the stack is empty or not. It returns true if the stack is empty,

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 9

Array Implementation of Stacks


The implementation of stack using array can be explained as Declaration of stack data type: # define MAX 1000 # define TRUE 1 # define FALSE 0 typedef int StackElement; struct stack_info{ int top; StackElement data[MAX]; }; typedef struct stack_info Stack; Stack st;
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

10

Function prototypes:

void create(Stack *); int IsStackEmpty(Stack *); int IsStackFull(Stack *); StackElement pop(Stack *); void push(Stack *, StackElement);

int menu(void);
Copyright Vikas karanth,2007 All rights reserved.
11

08/20/08

main() { StackElement data; system("clear"); create(&st); do{ switch(menu()){ case 1: printf("enter elemenmt to be pushed\n"); scanf("%d",&data); push(&st,data); break; case 2: printf("poped element is :%d\n",pop(&st)); break; default: printf("error in chioce"); } printf("Do u want to continiue\n"); } while(getchar() != 'n' ); }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 12

void create(Stack *s) { s -> top = -1; } int IsStackEmpty(Stack *s) { if(s -> top == -1) return TRUE; else return FALSE; }

int IsStackFull(Stack *s) { if(s -> top == MAX -1) return TRUE; else return FALSE; }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 13

StackElement pop(Stack *s) { int t; StackElement element; if(IsStackEmpty(s)) printf("Stack empty\n"); else{ t = s ->top ; element = s ->data[t]; s -> top --; } return element; }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 14

void push(Stack *s, StackElement d) { if(IsStackFull(s)) printf("Stack is full\n"); else{ ++(s -> top); s ->data[s ->top] = d; } } int menu(void) { int choice; printf("1 to push\n"); printf("2 to pop\n"); scanf("%d",&choice); return choice; }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 15

Applications of stack

Infix , Prefix and postfix experections: when we think of adding two numbers, normally we think of applying '+' operator to the operands. We can apply operators in different ways like:

A+B +AB AB+

: Infix : Prefix : Postfix

The prefixes "pre-", "post-" and "in-" refer to the relative position of the operator with respect to the two operands. In prefix notation the operator precedes the two operands, in postfix notation the operator follows the two operands and in infix notation the operator is in between the two operands.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 16

Converting infix to postfix:


A+B*C Step 1: (A+(B*C)) Step 2: A+(BC*) Step 3: ABC*+.

In this conversion process are that operations with highest precedence are converted first, then after a portion of expression has been converted to postfix.
Copyright Vikas karanth,2007 All rights reserved.
17

08/20/08

Converting infix to postfix:


A+B*C

Step 1: (A+(B*C)) Step 2: (A+*BC) Step 3: +A*BC


The precedence rules for converting an expression from infix to prefix are identical. The only change is the operator is placed before the operands.
Copyright Vikas karanth,2007 All rights reserved.
18

08/20/08

Examples

Prefix +AB -+ABC *+AB-CD

Infix A+B A+BC (A+B) * (C D)

Postfix AB+ AB+CAB+CD -* AB$C*D-EF/GH+/+ AB+C*DEFG+$

+-*$ABCD//EF+GH $-*+ABC-DE+FG

A$B*C-D+E/F/(G+H) ((A+B)*C-(D-E))$(F+G)

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 19

Algorithm to convert an infix expression to postfix expression


Step 1:Access infix expression in a string S

Step 2: i being position, let it be equal to 0.


Step 3:Initially top=-1, indicating stack is empty.

Step 4:If S[i] is equal to operand, print it, go to step 8.


Step 5:If S[i] is equal to opening bracket, push it, go to step 8.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 20

Step 6: If S[i] is equal to operator, then


Step 6a: if stack empty push the operator got to step 8 else Pop the operator from the stack, say, p, If priority of P is less than priority of S[i], then push p, push s[i] go to step 8. Else print p, go to step 6a.

Step 7: If S[i] is equal to operator, then

Step 7a: pop the operator from the stack, say, p, If p is not equal to opening bracket, then print p, step 7a. Else go to to step 8.
Copyright Vikas karanth,2007 All rights reserved.
21

08/20/08

Step 8: Increment i.

Step9:IfS[i]isnotequalto\0,thengotostep4 else step 10


Step 10: pop the operator from the stack, say, p Step 11: Print p. Step 12: If stack is not empty, then go to step 10. Step 13: Stop.
Copyright Vikas karanth,2007 All rights reserved.
22

08/20/08

Non recursive algorithm for converting Infix to Prefix


Step 1: Get the prefix expression, say S.

Step 2: Set the position counter, i to 0.


Step 3: Initially top1 and top2 are -1, indicating that the stacks are empty. Step 4: If S[i] is equal to opening bracket, push it in stack1, go to step 8. Step 5: If S[i] is equal to operand it in stack2, go to step 8.
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

23

Step 6:

If S[i] is equal to operator, stack1 is empty or stack top elements has less priority as compared to S[i], go to step 8 Else p = pop the operator from stack1. o1 = pop the operator from stack1. o2 = pop the operand from stack2. From the Prefix expression p, o1, o2, Push in stack2 and go to step 6.
Copyright Vikas karanth,2007 All rights reserved.
24

08/20/08

Step 7: If S[i] = opening bracket, then

Step 7a: p = pop the operator from stack1


If p is not equal to closing bracket, then O1 = pop the operand from stack2. O2 = pop the operand from stack2. From the prefix expression p, o1, o2, Push in stack2 and go to step 7a Else go to step 8.

Step 8: Increment i.
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

25

Step 9:IfS[i]isnotequalto\0,thengotostep4.

Step 10: Every time pop one operator from stack1, pop2 operands from stack2, from the prefix expression p, o1, o2, push in stack2 and repeat till stack1 becomes empty. Step 11: Pop operand from stack2 and print it as prefix expression
Step 12:Stop
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

26

Program to Evaluate a Postfix Expression


#include<stdio.h> #include<ctype.h> #include<math.h> #include<stdlib.h> #define MAX 20

typedef struct stack { int top; float items[MAX]; }S;


Copyright Vikas karanth,2007 All rights reserved.
08/20/08 27

/* PROTOTYPE DECLARATIONS */

void push(S *,float); float pop(S *); float eval(char []); float oper(char,float,float); main() { char expr[MAX]; int pos=0; printf("Enter the postfix expression\n"); while((expr[pos++]=getchar())!='\n'); expr[--pos]='\0'; printf("The original postfix expression is %s",expr); printf("\n %f \n",eval(expr)); }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 28

float eval(char expr []) { char c; int pos; float opnd1,opnd2,value; S opndstack; opndstack.top=-1; pos=0; c=expr[pos]; while(c!='\0') { if(isdigit(c)) push(&opndstack,(float)(c-'0')); else { opnd1=pop(&opndstack); opnd2=pop(&opndstack); value=oper(c,opnd1,opnd2); push(&opndstack,value); } c=expr[pos++]; } return (pop(&opndstack)); } Copyright Vikas karanth,2007 All rights reserved.
08/20/08 29

float oper(char symb,float op1,float op2) { switch(symb) { case'+' :return(op1+op2); case'-' :return(op1-op2); case'*' :return(op1*op2); case'/' :return(op1/op2); case'$' :return(pow(op1,op2)); default :printf("Invalid option\n"); } return -1; } void push(S *p,float x) { if(p->top==MAX-1) printf("Sorry! the stack is full\n"); else { ++p->top; p->items[p->top]=x; } }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 30

float pop(S *p) { int num; if(p->top==-1) { printf("The stack is empty\n"); return -1; } else { num=p->items[p->top]; --(p->top); return(num); } }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 31

Linked Implementation of Stacks

An ideal implementation for a stack is a linked list that can dynamically grow and shrink at runtime. Since you are going to employ a variation of a linked list that functions as a stack, you need to employ an additional pointer (top) that always points to the first node in the stack, or the top of the stack. It is using top that a node will either be inserted at the beginning or top of the stack (push a node into the stack), or deleted from the top of the stack (popping a node at the top or beginning of the stack).
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

32

Code Implementing for a Stack

The push( ) and the pop( ) operations on a stack are analogous to insert-first-node and delete-first-node operations on a linked list that functions as a stack.

struct stack { int info; struct stack *next; }; /* pointer declaration to point to the top of the stack */ struct stack *top;
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 33

Code Implementing for a Stack


main( ) {

top = NULL; charmenu=0 ; while(menu=!3){ printf(AddNodes:\n); printf(DeleteNodes:\n); printf(Exit:\n); menu = getchar( ); switch (menu) { case1:push(;) break;

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 34

Code Implementing for a Stack


case2:pop() break; case3:exit(;) break; } /* end of switch */ } /* end of main( ) */

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 35

Implementing push( )
push( ) { struct stack * new; charch=y; while(ch==y){ new = getnode( ) /* checking for an empty stack */ if ( top = = null) { top = new;

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 36

Implementing push( )
else { new->next = top; top = new; } printf("%s","Want to add more nodes\n"); scanf( "%c", &ch ); fflush( stdin ); } /* end of while */ } /* end of push( )
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 37

Creating a Node on a Stack


struct stack * makenode() { struct stack *new; new=(struct stack *) malloc(sizeof(struct(stack)); scanf("%d",&new->info); new->next = NULL; return(new); }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 38

Implementing pop( )
pop( ) { struct stack * temp; int x; /* check for an empty stack */ if (top = = null) { printf(Cannotremovenodesfromanemptystack*/ exit( ); }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 39

Implementing pop( )
else { temp = top; x = top->info; top = top->next; free( temp); return x; }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 40

Applications of Stacks

As a stack is a LIFO structure, it is an appropriate data structure for applications in which information must be saved and later retrieved in reverse order. Consider what happens within a computer when function main( ) calls another function.

How does a program remember where to resume execution from after returning from a function call?
From where does it pick up the values of the local variables in the function main( ) after returning from the subprogram?
Copyright Vikas karanth,2007 All rights reserved.
41

08/20/08

Applications of Stacks

As an example, let main( ) call a( ). Function a( ), in turn, calls function b( ), and function b( ) in turn invokes function c( ). main( ) is the first one to execute, but it is the last one to finish, after a( ) has finished and returned. a( ) cannot finish its work until b( ) has finished and returned. b( ) cannot finish its work until c( ) has finished and returned.
Copyright Vikas karanth,2007 All rights reserved.
42

08/20/08

Applications of Stacks

When a( ) is called, its calling information is pushed on to the stack (calling information consists of the address of the return instruction in main( ) after a( ) was called, and the local variables and parameter declarations in main( ). When b( ) is called from a( ),b()scallinginformationis pushed onto the stack (calling information consists of the address of the return instruction in a( ) after b( ) was called, and the local variables and parameter declarations in a( )).

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 43

Applications of Stacks

Then, when b( ) calls c( ), c( )scallinginformationis pushed onto the stack (calling information consists of the address of the return instruction in b( ) after c( ) was called, and the local variables and parameter declarations in b( )).

When c( ) finishes execution, the information needed to return to b( ) is retrieved by popping the stack.
Then, when b( ) finishes execution, its return address is popped from the stack to return to a( )
Copyright Vikas karanth,2007 All rights reserved.
44

08/20/08

Applications of Stacks

Finally, when a( ) completes, the stack is again popped to get back to main( ). When main( ) finishes, the stack becomes empty. Thus, a stack plays an important role in function calls. The same technique is used in recursion when a function invokes itself.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 45

Summary

In this session, you learnt to: Define a stack Describe the operations on a stack Implement a stack as a special case of a linked list Describe applications of stacks

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 46

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 47

Chapter 11 Queues

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 48

Objectives

In this session, you will learn to: Define a queue Describe the operations on a queue Implement a queue as a special case of a linked list Describe applications of queues

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 49

Defining a Queue

A Queue is a data structure in which elements are added at one end (called the rear), and elements are removed from the other end (called the front). You come across a number of examples of a queue in real life situations. For example, consider a line of students at a fee counter. Whenever a student enters the queue, he stands at the end of the queue (analogous to the addition of nodes to the queue)
Copyright Vikas karanth,2007 All rights reserved.
50

08/20/08

Defining a Queue

Every time the student at the front of the queue deposits the fee, he leaves the queue (analogous to deleting nodes from a queue). The student who comes first in the queue is the one who leaves the queue first. Therefore, a queue is commonly called a first-in-first-out or a FIFO data structure.
Copyright Vikas karanth,2007 All rights reserved.
51

08/20/08

Queue Insertions and Deletions


1 2 3 1 2 3 4

front

rear (a) 2 3 4

front (b)

rear

front (c)

rear

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 52

Queue Operations

To complete this definition of a queue, you must specify all the operations that it permits. The first step you must perform in working with any queue is to initialize the queue for further use. Other important operations are to add an element, and to delete an element from a queue. Adding an element is popularly known as ENQ and deleting an element is know as DEQ. The following slide lists operations typically performed on a queue.
Copyright Vikas karanth,2007 All rights reserved.
53

08/20/08

Queue Operations

create(q) enq(i) deq(q) empty (q)

which creates q as an empty queue adds the element i to the rear end of the queue removes the element at the front end of the queue (q) and returns the removed element. it checks the queue (q) whether it is empty or not. It returns true if the queue is empty and returns false otherwise returns the front element of the queue without changing the queue

front(q)

queuesize(q) returns the number of entries in the queue


Copyright Vikas karanth,2007 All rights reserved.
08/20/08 54

Queues can be implemented using array


#include <stdio.h> #define MAX 10 /* The maximum size of the queue */ #include <stdlib.h> void insert(int queue[], int *rear, int value) { if(*rear < MAX-1) { *rear= *rear +1; queue[*rear] = value; } else { printf("The queue is full can not insert a value\n"); exit(0); } }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 55

void delete(int queue[], int *front, int rear, int * value) { if(*front == rear) { printf("The queue is empty can not delete a value\n"); exit(0); } *front = *front + 1; *value = queue[*front]; } main() { int queue[MAX]; int front,rear; int n,value; front=rear=(-1);

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 56

do { do { printf("Enter the element to be inserted\n"); scanf("%d",&value); insert(queue,&rear,value); printf("Enter 1 to continue\n"); scanf("%d",&n); } while(n == 1); printf("Enter 1 to delete an element\n"); scanf("%d",&n);

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 57

while( n == 1) { delete(queue,&front,rear,&value); printf("The value deleted is %d\n",value); printf("Enter 1 to delete an element\n"); scanf("%d",&n); } printf("Enter 1 to continue\n"); scanf("%d",&n); } while(n == 1); }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 58

(Circular Queue implementation using array)


# include <stdio.h> # define MAX 10 # define TRUE 1 # define FALSE 0 # define ERROR -1 struct queueinfo{ int rear, front; int noelements; int qelements[MAX]; }; typedef int queue_type; typedef struct queueinfo queue; void create(queue *); void Ins_queue(queue *, queue_type); int IsQueueFull(queue *); int IsQueueEmpty(queue *); int dequeue(queue *); int menu(void); queue Queue;
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 59

main() { queue_type item; int re; create(&Queue); do{ switch(menu()) { case 1: printf("enter item to en-queue\n"); scanf("%d",&item); Ins_queue(&Queue, item); break; case 2: item = dequeue(&Queue); printf("Deleted item is %d\n",item); break; default: printf("Error\n"); } printf("Do you want to continue (y/n)\n"); } while(re!= 'n'); Copyright Vikas karanth,2007 All rights reserved. } 08/20/08

60

void create(queue *q) { q -> front = -1;q -> rear = -1; q -> noelements = 0; } int IsQueueEmpty(queue *q) { if(q ->noelements == 0) return TRUE; else return FALSE; }

int IsQueueFull(queue *q) { if(q ->noelements == MAX) return TRUE; else return FALSE; }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 61

void Ins_queue(queue *q, queue_type item) { char stop; if(IsQueueFull(q)) printf("Queue is full\n"); else{ if(IsQueueEmpty(q)){ q -> front ++; q ->rear ++; } else if(q -> rear < (MAX -1) || q -> rear < (q ->front -1 )) q ->rear++; else if(q ->rear == (MAX -1 ) && q ->noelements < MAX) q ->rear = 0; q ->qelements[q ->rear] = item; q ->noelements ++; } }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 62

int dequeue(queue *q) { int data; if(IsQueueEmpty(q)) { printf("Queue is empty\n"); return ERROR; } else{ data = q ->qelements[q ->front]; if(q -> front == MAX -1) q ->front = 0; else q ->front++; q ->noelements --; if(IsQueueEmpty(q)) create(q); return data; } }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 63

int menu(void) { int choice; printf("1. en-queue\n"); printf("2. de-queue\n"); scanf("%d",&choice); return choice; }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 64

Implementing Queues

Linked lists offer a flexible implementation of a queue since insertion and deletion of elements into a list are simple, and a linked list has the advantage of dynamically growing or shrinking at runtime.

Having a list that has the functionality of a queue implies that insertions to the list can only be done at the rear of the list, and deletions to the list can only be done at front of the list.
Copyright Vikas karanth,2007 All rights reserved.
65

08/20/08

Implementing Queues

Queue functionality of a linked list can be achieved by having two pointers front and rear, pointing to the first element, and the last element of the queue respectively.
rear

The following figure gives a visual depiction of linked list implementation of a queue.
front 2 4 7

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 66

Queue Declaration & Operations


struct queue { int info; struct queue *next; }; struct queue *front, *rear; An empty queue is represented by q->front = q->rear = null. Therefore, clearq( ) can be implemented as follows: void clearq(struct queue * queue_pointer) { queue_pointer->front = queue_pointer ->rear = null; }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 67

Queue Operations

You can determine whether a queue is empty or not by checking its front pointer. The front pointer of a queue can be passed as an argument to emptyq( ) to determine whether it is empty or not. int emptyq (queue_pointer) { if (queue_pointer = = null) return (1); else return(0); }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 68

Insertion into a Queue


struct queue { int info; struct queue *next; }; /* pointer declarations to point to the front, and rear of the queue */ struct queue *front, *rear; main( ) { front = NULL; rear = NULL;

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 69

Insertion into a Queue


charmenu=0 ; while(menu=!3) { printf(AddNodes:\n); printf(DeleteNodes:\n); printf(Exit:\n); menu = getchar( ); switch (menu) { case1:enq(;) break; case2:deq() break;
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 70

Insertion into a Queue


case3:exit(;) break; } /* end of switch */ } /* end of main( ) */

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 71

Insertion into a Queue


void enq( ) { struct queue *new; new = getnode( ); if(queue_pointer->front= =queue_pointer->rear = = null) queue_pointer->front = new; queue_pointer->rear = new; } else { rear->next = new; rear = new; } }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 72

Creating a Node on a Queue


struct queue * makenode() { struct queue *new; new=(struct queue *) malloc(sizeof(struct(queue)); scanf("%d",&new->info); new->next = NULL; return(new); }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 73

Insertion into a Queue


New node inserted at rear of queue

rear

front

next

next

next

New

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 74

Deletion from a Queue


int deq( ) { struct queue *temp; int x; if(queue_pointer->front= =queue_pointer->rear = = null) { printf(QueueUnderflow\n); exit (1); } temp = front; x=temp->info; front = front->next; free(temp); if(front = = null) /* check for queue becoming empty after node deletion */ rear = null; return(x); }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 75

Deletion of a Node From a Queue


rear

front

Node being deleted at the front of the queue

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 76

Applications of Queues

Queues are also very useful in a time-sharing multi-user operating system where many users share the CPU simultaneously. Whenever a user requests the CPU to run a particular program, the operating system adds the request
This process ID is then added at the end of the queue of jobs waiting to be executed.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 77

Applications of Queues

Whenever the CPU is free, it executes the job that is at the front of the job queue. Similarly, there are queues for shared I/O devices. Each device maintains its own queue of requests (Example printers). An example is a print queue on a network printer, which queues up print jobs issued by various users on the network. The first print request is the first one to be processed. New print requests are added at the end of the queue.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 78

Summary

In this session, you learnt to:

Define a queue Describe the operations on a queue

Implement a queue as a special case of a linked list


Describe applications of queues

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 79

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 80

Priority Queues

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 81

Objectives

At the conclusion of this section, you will have demonstrated the ability to: Discuss the usefulness and application of priority queues;

Choose between algorithms that trade-off speed and complexity against data size; and
Perform a complete, modularized priority queue implementation.
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

82

Priority Queues

priority queue, which organizes data according to an arbitrary designation of importance. Priority queues are a practical means of implementing schedulers,

such as real-time operating system schedulers that execute processes, or communications schedulers that transmit messages according to their priority.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 83

A priority queue is a queue in which each element is assigned a priority. A priority is typically an integer, and higher integers represent higher priorities. All elements having the same priority are said to belong to the same priority class.
Copyright Vikas karanth,2007 All rights reserved.
84

08/20/08

priority queue may be visualized as an array in which all elements of the same priority class are grouped together, and higher priority classes are grouped closer to the front of the array.
A new element is added to the tail of its priority class, and elements are always removed from the front of the queue. A remove operation, therefore, always addresses the element of the highest priority class that has been enqueued the longest.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 85

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 86

Simple Priority Queues operations


1. Create a queue; 2. Create a queue item; 3. Determine if a queue is empty; 4. Add a new item to a queue; 5. Remove the item at the front of a queue; 6. Empty a queue; 7. Destroy a queue item; and 8. Destroy a queue.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 87

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 88

Chapter 9
Linked Lists

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 89

Objectives
In this session, you will learn to:

Describe the limitations of using a data structure such as an array Define stack and heap variables Describe the characteristics of stack and heap variables State the need for dynamic memory allocation Use the malloc() function to allocate memory Define self-referential structures and their advantages Write code to:

Create a sorted linked list, Insert nodes into a sorted linked list Traverse a linked list Delete nodes from a linked list

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 90

Array Usage A Perspective

Consider the following example:

#include<stdio.h> main( ) { int num_array[50],i; for( i=0; i < 50; i++ ) { num_array[i]=0; scanf( "%d",&num_array[i] ); } }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 91

Array Usage A Perspective

When this program is compiled, the compiler estimates the amount of memory required for the variables, and also the instructions defined by you as part of the program. The compiler writes this information into the header of the executable file that it creates. When the executable is loaded into memory at runtime, the specified amount of memory is set aside. A part of the memory allocated to the program is in an area of memory called a runtime stack or the call stack. Once the size of the stack is fixed, it cannot be changed dynamically.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 92

Array Usage A Perspective

Therefore, arrays present the classic problem of the programmer having allocated too few elements in the array at the time of writing the program, and then finding at run time that more values are required to be stored in the array than what had originally been defined. The other extreme is of the programmer allocating too many elements in the array and then finding at run time that not many values need to be stored in the array thereby resulting in wastage of precious memory.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 93

Array Usage A Perspective

Moreover, array manipulation (in terms of insertion and deletion of elements from the array) is more complex and tedious, and a better alternative to all this is to go for dynamic data structures. Before venturing into dynamic variables, or dynamic data structures, it would be prudent at this juncture to differentiate between stack and heap variables, and their characteristics. Let us begin by understanding the concept of lifetime of variables.
Copyright Vikas karanth,2007 All rights reserved.
94

08/20/08

Lifetime Of A Variable

is a run-time concept period of time during which a variable has memory space associated with it begins when space is allocated ends when space is de-allocated three categories of "lifetime" static - start to end of program execution automatic (stack) - start to end of declaring function's execution heap (variable declared dynamic at runtime, and also deallocated dynamically at runtime.
Copyright Vikas karanth,2007 All rights reserved.
95

08/20/08

Data Memory Model


space for global variables

static data automatic data


run-time stack - activation records added and removed as program runs (expands and shrinks in an orderly LIFO manner)

heap data
08/20/08

space for variables allocated at run-time (allocation and de-allocation requests occur in unpredictable order)

Copyright Vikas karanth,2007 All rights reserved.


96

Heap Variables

Space for heap variables is allocated from an area of runtime memory known as the heap or free store. Heap variables do not have an explicit name, and are accessed indirectly via a pointer.

Memory space for heap variables is explicitly allocated at runtime using malloc( ).
Space occupied by heap variables must be explicitly returned back to the heap to avoid memory leaks. This is done using the free( ) function.
Copyright Vikas karanth,2007 All rights reserved.
97

08/20/08

Dynamic Data Structures

Rather than pre-define array on the stack at compile time, the alternative should be to define a structure type, and dynamically declare at runtime as many instances of the structure variables as needed by the application. When the code containing the structure type is compiled, what the compiler sees is only a structure type declaration. It therefore, does not allocate memory for the structure type since no variable has been defined based on the structure type.
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

98

Dynamic Data Structures

When the program begins execution, it will need to create variables of the structure type. Therefore, the language must support runtime declaration of variables. The problem with these variables is that they cannot be accommodated into the stack, as they were not declared at the time of compilation, and the stack would have been sized based on the stack variables already declared at compile-time. The C language provides the malloc() function which a program can use to declare variables dynamically.
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

99

The malloc( ) Function

The parameter to malloc( ) is an unsigned integer which represents the number of bytes that the programmer has requested malloc( ) to allocate on the heap. A more effective way of passing the number of bytes to malloc( ) would be to pass the structure type along with the sizeof( ) operator to malloc( ). The sizeof( ) operator can be used to determine the size of any data type in C, instead of manually determining the size and using that value. Therefore, the benefit of using sizeof( ) operator in any program makes it portable.
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

100

The malloc( ) Function


Consider the following example: #include<stdio.h> main() { struct marks_data { char name[11]; int marks; }; struct marks_data *ptr; /* declaration of a stack variable */ ptr = (struct marks_data *) malloc(sizeof(struct marks_data)); /* declaration of a block of memory on the heap and the block in turn being referenced by ptr */ }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 101

The malloc( ) Function

Stack
100 ptr

Heap

100
Name Marks

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 102

The malloc( ) Function

The malloc( ) function also returns the starting address to the marks_data structure variable on the heap. However, malloc( ) returns this not as a pointer to a structure variable of type marks_data , but as a void pointer. Therefore, the cast operator was used on the return value of malloc( ) to cast it as a pointer to a structure of type marks_data before being assigned to ptr, which has accordingly been defined to be a pointer to a structure of type marks_data.
Copyright Vikas karanth,2007 All rights reserved.
103

08/20/08

THE LIST DATA STRUCTURE

A list is a sequential data structure, i.e. a collection of items accessible one after another beginning at the head and ending at the tail. It is a widely used data structure for applications which do not need random access. It differs from the stack and queue data structures in that additions and removals can be made at any position in the list.
Copyright Vikas karanth,2007 All rights reserved.
104

08/20/08

Operations On the list


The main primitive operations of a list are known as: Add : adds a new node Update :updates the contents of a node

Remove : removes a node


Sort : sorting the list members

Insert : inserting the data at the given position.


Copyright Vikas karanth,2007 All rights reserved.
105

08/20/08

Storing a list in a static data structure (Array List)

This implementation stores the list in an array. The Array List has the following properties:

Thepositionofeachelementisgivenbyanindex from 0 to n-1, where n is the number of elements. Givenanyindex,theelementwiththatindexcan be accessed in constant time i.e. the time to access does not depend on the size of the list.
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 106

Toaddanelementattheendofthelist,thetimetaken does not depend on the size of the list. However, the time taken to add an element at any other point in the list does depend on the size of the list, as all subsequent elements must be shifted up. Additions near the start of the list take longer than additions near the middle or end. Whenanelementisremoved,subsequentelementsmust be shifted down, so removals near the start of the list take longer than removals near the middle or end.
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 107

Storing a list in a dynamic data structure (Linked List)

The Linked List is stored as a sequence of linked nodes, each node in a linked list contains data AND a reference to the next node. The Linked List has the following properties:

Thelistcangrowandshrinkasneeded.
Thepositionofeachelementisgivenbyanindexfrom0 to n-1, where n is the number of elements.
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 108

Given any index, the time taken to access an element with that index depends on the index. This is because each element of the list must be traversed until the required index is found.

Thetimetakentoaddanelementatanypointinthelist does not depend on the size of the list, as no shifts are required. It does, however, depend on the index. Additions near the end of the list take longer than additions near the middle or start. The same applies to the time taken to remove an element.
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 109

Self-Referential Structures

Suppose, you have been given a task to store a list of marks. The size of the list is not known. If it were known, then it would have facilitated the creation of an array of the said number of elements and have the marks entered into it. Elements of an array are contiguously located, and therefore, array manipulation is easy using an integer variable as a subscript, or using pointer arithmetic.
Copyright Vikas karanth,2007 All rights reserved.
110

08/20/08

Self-Referential Structures

However, when runtime variables of a particular type are declared on the heap, let's say a structure type in which we are going to store the marks, each variable of the structure type marks will be located at a different memory location on the heap, and not contiguously located. Therefore, these variables cannot be processed the way arrays are processed, i.e., using a subscript, or using pointer arithmetic. An answer to this is a self-referential structure.
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

111

Self-Referential Structures

A self-referential structure is so defined that one of the elements of the structure variable is able to reference another subsequent structure variable of the same type, wherever it may be located on the heap. In other words, each variable maintains a link to another variable of the same type, thus forming a non-contiguous, loosely linked data structure. This self-referential data structure is also called a linked list.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 112

Types of List implementation

Singular Linked list Single circular Linked list

Double Linked list


Double circular Linked list
Copyright Vikas karanth,2007 All rights reserved.
113

08/20/08

Declaring a Linked List

Let us define a self-referential structure to store a list of marks the size of which may not be known.

struct marks_list { int marks; struct marks_list *next; };

We have defined a structure of type marks_list. It consists of two elements, one integer element marks and the other element, a pointer next, which is a pointer to a structure of the same type, i.e., of type marks_list itself.
Copyright Vikas karanth,2007 All rights reserved.
114

08/20/08

Declaring a Linked List

Therefore, a part of the structure is referencing a structure type of itself, and hence the name selfreferential structure. Such data structures are also popularly known as linked lists, since each structure variable contains a link to other structure variables of the same type. One can visualize a linked list as shown in the following slide:
Copyright Vikas karanth,2007 All rights reserved.
115

08/20/08

Visualizing a Linked List


Stack
start

Heap
100 140 180

100

75
mark s

140
next

85
mark s

180
next

95
mark s

230
next

100 x
mark s next

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 116

Circularly linked lists

The tail of the list always points to the head of the list

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 117

Doubly linked lists

These permit scanning or searching of the list in both directions. (To go backwards in a simple list, it is necessary to go back to the start and scan forwards.) In this case, the node structure is altered to have two links:

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 118

Single Linked List can be explained as


struct node { int data; struct node *next; }; typedef struct node Node; int menu(void); Node *create(void); void insert(Node *,int); void display(Node *); int small(Node *); int large(Node *); int search(Node *,int); int delete(int);

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 119

Node *fptr; main() { int choice,element,num; char ch='y'; fptr=NULL; printf("enter the element\n"); scanf("%d",&element); fptr=create(); fptr->data=element; fptr->next=NULL; do { choice=menu(); switch(choice) {
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 120

case 1: printf("ENTER THE ELEMENTS\n"); scanf("%d",&element); insert(fptr,element); break; case 2: printf("DISPLAYING THE CONTENTS OF THE LIST\n"); display(fptr); break; case 3: if(fptr==NULL) printf("Empty list\n"); else printf("THE SMALLEST ELEMENT IS %d\n",small(fptr)) case 4: if(fptr==NULL) printf("Empty list\n"); else printf("THE LARGEST ELEMENT IS %d\n",large(fptr)); break;

break;

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 121

case 5: printf("ENTER THE ELEMENT TO BE SEARCHED\n"); scanf("%d",&num); search(fptr,num); break; case 6: printf("ENTER THE ELEMENT TO BE DELETED\n"); scanf("%d",&num); element=delete(num); printf("THE ELEMENT %d IS DELETED\n",element); break; case 7: exit(0); default: printf("invalid option\n"); } printf("DO YOU WISH TO CONTINUE Y or N \n"); ch=getchar(); }while(ch!='N'||ch!='n'); }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 122

Node *create(void) { Node *new; new=(Node *)malloc(sizeof(Node)); return(new); } void insert(Node *p,int x) { if(p==NULL) printf("error\n"); else { while(p->next!=NULL) p=p->next; p->next=create(); p=p->next; p->data=x; p->next=NULL; } }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 123

void display(Node *p) { while(p!=NULL) { printf("%d\n",p->data); p=p->next; } } int small(Node *p) { int s; s=p->data; while(p! = NULL){ if(p->data<s) s=p->data; p=p->next; } return(s); }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 124

int large(Node *p) { int l; l=p->data; while(p!=NULL){ if(p->data>l) l=p->data; p=p->next; } return(l); } int search(Node *p,int x) { while(p!=NULL){ if(p->data==x){ printf("THE ELEMENT %d EXISTS\n",x); return 0; } else p=p->next; } printf("ELEMENT NOT FOUND\n"); return -1; }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 125

int delete(int x) { Node *temp,*node,*old; temp=node=fptr; while(temp!=NULL){ if(node->data==x){ fptr=fptr->next; free(node); return(x); } else if(temp ->data == x){ old->next=temp->next; free(temp); return(x); } old=temp; temp=temp->next; } return -1; }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 126

Creating a Sorted Linked List


#include <stdio.h> struct node { int info; struct node *next; }; struct node *head, *save; int j=0; struct node *create(void); void sort(struct node *); void insert(int ); void display(struct node *);

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 127

main() { int n, i ,x; head = NULL; printf("Enter how many terms\n"); scanf("%d", &n); printf("Enter elements\n"); for(i = 0; i < n; i++){ scanf("%d",&x); insert(x); j++; } printf("linked list is"); display(head); sort(head); printf("Sorted linked list is \n"); display(head); }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 128

void insert(int x) { struct node *temp; if(j == 0){ temp=create(); temp->info=x; temp->next = NULL; head = temp; save = temp; } else { temp = create(); temp -> info = x; temp -> next = NULL; save -> next = temp; save = temp; } }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 129

struct node *create(void) { struct node *p; p=(struct node *) malloc(sizeof(*head)); return (p); } void display(struct node *p) { while(p!= NULL){ printf("%d ->", p -> info); p = p ->next; } printf("\n\n"); }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 130

void sort(struct node *p) { struct node *p1,*p2,*p3; int temp; for(p1 = p; p1!= NULL;p1=p1 -> next) for(p2 = p1 -> next; p2 != NULL;p2=p2 -> next){ if( p1 -> info >= p2 -> info){ temp = p1 ->info; p1 -> info = p2 -> info; p2 -> info = temp; } } }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 131

Doubly Linked Lists

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 132

Objectives

In this session, you will learn to:

Describe the need for, and advantages of a doubly linked list


Write code to:

Create a sorted doubly linked list, Insert nodes into a sorted doubly linked list Traverse a doubly linked list Delete nodes from a doubly linked list
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

133

Need For a Doubly Linked List

The disadvantage with a singly linked list is that traversal is possible in only direction, i.e., from the beginning of the list till the end. If the value to be searched in a linked list is toward the end of the list, the search time would be higher in the case of a singly linked list. It would have been efficient had it been possible to search for a value in a linked list from the end of the list.
Copyright Vikas karanth,2007 All rights reserved.
134

08/20/08

Properties of a Doubly Linked List

This would be possible only if we have a doubly linked list.

In a doubly linked list, each node has two pointers, one say, next pointing to the next node in the list, and another say, prior pointing to the previous node in the list.
Therefore, traversing a doubly linked list in either direction is possible, from the start to the end using next, and from the end of the list to the beginning of the list using prior.
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

135

Properties of a Doubly Linked List

In a doubly linked list, the prior pointer of the first node will be null, as there is no node before the first node.

In a doubly linked list, the next pointer of the last node will be null, as there is no node after this list.
Bidirectional traversal of a doubly linked list is useful for implementing page up, and page down functionality when using doubly linked lists to create editors. A doubly linked list would have two pointers, start and last to facilitate traversal from the beginning and end of the list respectively.
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

136

Declaration of a Doubly Linked List


struct marks_list { struct double_list *prior; int info; struct marks_list *next; }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 137

Visualizing a Doubly Linked List


last start 100 140

100 null 1 120

120 100 2 140

140 120 3 null

prior

marks

next

prior

marks

next

prior

marks

next

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 138

Creating a Sorted Doubly Linked List


struct marks_list *start, *last; /* variables declared outside main() are global in nature and can be accessed by other functions called from main() */ struct marks_list { struct marks_list *prior; int marks; struct marks_list *next; }; main() { struct marks_list * makenode(); /* function prototype declaration */
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 139

Creating a Sorted Doubly Linked List


struct marks_list *new; start = NULL; charmenu=0 ; while(menu=!5) { printf(AddNodes:\n); printf(DeleteNodes:\n); printf(ForwardTraversealist:\n); printf(ReverseTraversealist:\n); printf(Exit:\n); menu = getchar( );
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 140

Creating a Sorted Doubly Linked List


switch (menu) { case1:addnode(;) break; case2:deletenode() break; case3:forward_traverse(;) break; case4:reverse_traverse(;) break; case5:exit(;) break; } /* end of switch */ } /* end of main( ) */

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 141

Creating a Sorted Doubly Linked List


addnode( ) { char ch = 'y'; while ( ch = = 'y' ) { new = makenode(); /* creation of a list is treated as a special case of insertion */ if ( start = = NULL) { start = new; start->prior = null; } else { insert(); traverse( ); }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 142

Creating a Sorted Doubly Linked List


printf("%s","Want to add more nodes\n"); scanf( "%c", &ch ); } /* end of while */ } /* end of addnode( )

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 143

Creating a Sorted Doubly Linked List


struct marks_list * makenode() { struct marks_list *new; new=(struct marks_list *) malloc(sizeof(struct(marks_list)); scanf("%d",&new->marks); new->prior = NULL; new->next = NULL; return(new); }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 144

Creating a Sorted Linked List


insert( ) { struct marks_list *ptr, *prev; for(ptr=start,prev=start;(ptr);prev=ptr,ptr=ptr->next) if (new->marks < start->marks) { /* insertion at the beginning of a list */ new->next = start; new->prior = NULL; start->prior = new; last = start; start = new; }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 145

Creating a Sorted Doubly Linked List


/* insertion in the middle of a list */ if(new->marks > ptr->marks) { else { prev->next = new; new->prior = prev; new->next = ptr; ptr->prior = new; } } /* end of for loop */ continue; }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 146

Creating a Sorted Linked List


/* insertion at the end of the list */ if (ptr = = null) { prev->next = new; new->prior = prev; new->next = null; last = new; } } /* end of insert */

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 147

Searching a Value in a Doubly Linked List


struct marks_list *search( int val) { for( ptr = start; (ptr); ptr = ptr->next) { if (val = = ptr-> marks) return ptr; }

struct marks_list *search( int val) { for( ptr = last; (ptr); ptr = ptr->prior) { if (val = = ptr-> marks) return ptr; }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 148

Deleting a Node From a Doubly Linked List


delete ( ) { struct marks_list *ptr, *prev, *temp; int score; /* search the linked list for the value to be deleted */ scanf(%d,&score); for (ptr = start, prev = start; (ptr); prev = ptr, ptr = ptr->next) {

/* deletion of the first node in the list */ if (score = = start-> marks) { temp =start; start = start-> next; start->prior = null; free(temp); }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 149

Deleting a Node From a Linked List


/* deletion in the middle of a linked list */ if (score = = ptr-> marks) { prev-> next = ptr-> next; ptr->next->prior = ptr->prior; free(ptr); } /* deletion at the end of the list */ if (ptr->next = = null) { temp = ptr; prev->next = null; last = ptr->prior; } }}
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 150

Traversal of a Doubly Linked List


/* forward traversal of a doubly linked list */ for( ptr = start; (ptr); ptr = ptr->next){ printf(%d,ptr->marks); } /* reverse traversal of a doubly linked list */ for( ptr = last; (ptr); ptr = ptr->prior) { printf(%d,ptr->marks); }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 151

Points to remember

Linked lists are used when the quantity of data is not known prior to execution. In linked lists, data is stored in the form of nodes and at runtime, memory is allocated for creating nodes. Due to overhead in memory allocation and deallocation, the speed of the program is lower. The data is accessed using the starting pointer of the list.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 152

Summary

In this session, you learnt to:


Describe the limitations of using a data structure such as an array Define stack and heap variables Describe the characteristics of stack and heap variables State the need for dynamic memory allocation Use the malloc() function to allocate memory Define self-referential structures and their advantages Write code to:

08/20/08

Create a sorted linked list, Insert nodes into a sorted linked list Traverse a linked list Delete nodes from a linked list
Copyright Vikas karanth,2007 All rights reserved.
153

Summary

Describe the need for, and advantages of a doubly linked list Write code to:

Create a sorted doubly linked list, Insert nodes into a sorted doubly linked list Traverse a doubly linked list Delete nodes from a doubly linked list
Copyright Vikas karanth,2007 All rights reserved.
154

08/20/08

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 155

Binary Trees

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 156

Objectives

At the end of this lesson, you will be able to: Define a binary tree Describe the terminologies associated with a binary tree Use a dynamically allocated data structure to represent a binary tree Traverse a binary tree Add nodes to a binary tree Remove nodes from a binary tree Search a binary tree
Copyright Vikas karanth,2007 All rights reserved.
157

08/20/08

Eliminative or a Binary Search

The mode of accessing data in a linked list is linear. Therefore, in the worst case scenario of the data in question being stored at the extremes of the list, it would involve starting with the first node, and traversing through all the nodes till one reaches the last node of the list to access the data. Therefore, search through a linked list is always linear.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 158

Eliminative or a Binary Search

A linear search is fine if the nodes to be searched in a linked list are small in number.

But the linear search becomes ineffective as the number of nodes in a linked list increase.
The search time increases in direct proportion with the size of the linked list. It becomes imperative to have better searching mechanisms than a linear search.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 159

Eliminative or a Binary Search

You will now be exposed to a game that will highlight a new mechanism of searching.
Coin Search in a Group
X
YES NO

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 160

Eliminative or a Binary Search

A coin is with one of the members in the audience divided into the two sections on the left and the right respectively as shown in the diagram. The challenge facing X, the protagonist, is to find the person with the coin in the least number of searches.
Copyright Vikas karanth,2007 All rights reserved.
161

08/20/08

Employing the Linear Search

X, familiar with a linear search, starts using it to search for the coin among the group. Let us assume the worst-case scenario of the coin being with R.

If X was to start the search with A and progress linearly throughB,C,.MandfinallytoR,hewouldhave taken a minimum of 18 searches (R being the 18th person searched in sequence to find the coin.
Copyright Vikas karanth,2007 All rights reserved.
162

08/20/08

Employing the Linear Search

As you can see, this kind of search is not very efficient especially when the number of elements to be searched is high. An eliminative search, also called a binary search, provides a far better searching mechanism.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 163

Employing the Eliminative or The Binary Search

Assume that X can pose intelligent questions to the audience to cut down on the number of searches. AvalidquestionthathecouldposeisWhichsideofthe audiencehasthecoin? Aintheaudiencetotheleftofhimsaysthathissideofthe audience does not have the coin.

So X can completely do away with searching the audience to his left. That saves him 9 searches.
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 164

Employing the Eliminative or The Binary Search

X has now got to search the audience to the right of him for searching out the coin. Here too, he can split the audience into half by standing adjacent to the middle row, and posing the same questionthatheaskedearlierWhichsideofthe audiencehasthecoin? Minthemiddlerowrepliesthatthecoiniswiththe audience to the left of him.
Copyright Vikas karanth,2007 All rights reserved.
165

08/20/08

Employing the Eliminative or The Binary Search

X has in the process now eliminated 6 more searches, that is, the middle row and the row to the left of the middle as shown in the diagram below.

6 searches saved
NO

M
YES

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 166

X is now left with row to the right of the middle row containing P, Q, and R that has to be searched. Here too, he can position himself right at the middle of the row adjacent to Q and pose the same question, Whichsideoftheaudiencehasthe coin?Qrepliesthatthecoinistohis left.

P
NO

Q
YES

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 167

Eliminative or Binary Search


That completes our eliminative or binary search.

It is called a binary search because at each stage of the search, the search is cut by more than half.
This kind of search forms the basis for the searching mechanism employed in a data structure wherein the data is represented in a hierarchal manner unlike the linear mechanism of storage employed in a linked list.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 168

Trees

Compared to linked lists that are linear data structures, trees are non-linear data structures.

In a linked list, each node has a link which points to another node.
In a tree structure, however, each node may point to several nodes, which may in turn point to several other nodes. Thus, a tree is a very flexible and a powerful data structure that can be used for a wide variety of applications.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 169

Trees
Rajeev

Ravi

Vijay

Anjali

Ramesh

Suresh

Sukesh

Arvind

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 170

Trees

A tree consists of a collection of nodes that are connected to each other. A tree contains a unique first element known as the root, which is shown at the top of the tree structure.

A node which points to other nodes is said to be the parent of the nodes to which it is pointing, and the nodes that the parent node points to are called the children, or child nodes of the parent node.
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

171

Trees

The root is the only node in the tree that does not have a parent. All other nodes in the tree have exactly one parent. There are nodes in the tree that do not have any children. Such nodes are called leaf nodes.

Nodes are siblings if they have the same parent.


Copyright Vikas karanth,2007 All rights reserved.
172

08/20/08

Trees

A node is an ancestor of another node if it is the parent of that node, or the parent of some other ancestor of that node. The root is an ancestor of every other node in the tree. Similarly, we can define a node to be a descendant of another node if it is the child of the node, or the child of some other descendant of that node. You may note that all the nodes in the tree are descendants of the root node.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 173

Tree

An important feature of a tree is that there is a single unique path from the root to any particular node.

The length of the longest path from the root to any node is known as the depth of the tree.
The root is at level 0 and the level of any node in the tree is one more than the level of its parent. In a tree, any node can be considered to be a root of the tree formed by considering only the descendants of that node. Such a tree is called the subtree that itself is a tree.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 174

Binary Tree

If you can introduce a restriction that each node can have a maximum of two children or two child nodes, then you can have a binary tree. You can give a formal definition of a binary tree as a tree which is either empty or consists of a root node together with two nodes, each of which in turn forms a subtree. You therefore have a left subtree and a right subtree under the root node.
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

175

Binary Search Tree

A complete binary tree can be defined as one whose non-leaf nodes have non-empty left and right subtrees and all leaves are at the same level. This is also called as a balanced binary tree. If a binary tree has the property that all elements in the left subtree of a node n are less than the contents of n, and all elements in the right subtree are greater than the contents of n, such a tree is called a binary search tree. The following is an example of a balanced binary search tree.
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

176

Balanced Binary Search Tree


4

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 177

Binary Vs. Linear Search

As the name suggests, balanced binary search trees are very useful for searching an element just as with a binary search. If we use linked lists for searching, we have to move through the list linearly, one node at a time. If we search an element in a binary search tree, we move to the left subtree for smaller values, and to the right subtree for larger values, every time reducing the search list by half approximately.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 178

Linear Search in a Linked List

100

1 120

2 140

3 160

4 180

5 200

6 220

7 null

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 179

Binary Search in a Binary Search Tree


Search Value

Search Value

Search Value

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 180

The Essence of a Binary Search

To summarize, you have completely done away with searching with the entire left subtree of the root node and its descendant subtrees, in the process doing away with searching one-half of the binary search tree. Even while searching the right subtree of the root node and its descendant subtrees, we keep searching only one-half of the right subtree and its descendants. This is more because of the search value in particular, which is 7. The left subtree of the right subtree of the root could have been searched in case the value being searched for was say 5.
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 181

The Essence of a Binary Search

Thus we can conclude that while searching for a value in a balanced binary search tree, the number of searches is cut by more than half (3 searches in a balanced binary search tree) compared to searching in a linked list (7 searches). Thus a search that is hierarchical, eliminative and binary in nature is far efficient when compared to a linear search.
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 182

Data Structure Representation of a Binary Trees

A tree node may be implemented through a structure declaration whose elements consist of a variable for holding the information and also consist of two pointers, one pointing to the left subtree and the other pointing to the right subtree.
A binary tree can also be looked at as a special case of a doubly linked list that is traversed hierarchically. The following is the structure declaration for a tree node:

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 183

Data Structure Representation of a Binary Trees


struct btreenode { int info; struct btreenode *left; struct btreenode *right; };

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 184

Traversing a Binary Tree

Traversing a binary tree entails visiting each node in the tree exactly once. Binary tree traversal is useful in many applications, especially those involving an indexed search. Nodes of a binary search tree are traversed hierarchically. The methods of traversing a binary search tree differ primarily in the order in which they visit the nodes.
Copyright Vikas karanth,2007 All rights reserved.
185

08/20/08

Traversing a Binary Tree


At a given node, there are three things to do in some order. They are: To visit the node itself To traverse its left subtree To traverse its right subtree We can traverse the node before traversing either sub tree. Or, we can traverse the node between the sub trees. Or, we can traverse the node after traversing both sub trees.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 186

Traversing a Binary Tree

IfwedesignatethetaskofvisitingtherootasR, traversing the left subtree as L and traversing the right subtree as R, then the three modes of tree traversal discussed earlier would be represented as:

RLR Preorder LRR Postorder LRR In order


Copyright Vikas karanth,2007 All rights reserved.
187

08/20/08

Traversing a Binary Tree

The functions used to traverse a binary tree using these methods can be kept quite short if we understand the recursive nature of the binary tree. Recall that a binary tree is recursive in that each subtree is really a binary tree itself. Thus traversing a binary tree involves visiting the root node, and traversing its left and right subtrees. The only difference among the methods is the order in which these three operations are performed.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 188

Traversing a Binary Tree

Depending on the position at which the given node or the root is visited, the name is given. If the root is visited before traversing the subtree, it is called the preorder traversal.

If the root is visited after traversing the subtrees, it is called postorder traversal. If the root is visited in between the subtrees, it is called the inorder traversal.
Copyright Vikas karanth,2007 All rights reserved.
189

08/20/08

Traversing a Binary Tree

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 190

Preorder Traversal

When we traverse the tree in preorder, the root node is visited first. So, the node containing A is traversed first. Next, we traverse the left subtree. This subtree must again be traversed using the preorder method.

Therefore, we visit the root of the subtree containing B and then traverse its left subtree.
The left subtree of B is empty, so its traversal does nothing. Next we traverse the right subtree that has root labeled C.
Copyright Vikas karanth,2007 All rights reserved.
191

08/20/08

Preorder Traversal

Then, we traverse the left and right subtrees of C getting D and E as a result. Now, we have traversed the left subtree of the root containing A completely, so we move to traverse the right subtree of A. The right subtree of A is empty, so its traversal does nothing. Thus the preorder traversal of the binary tree results in the values ABCDE.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 192

Inorder Traversal

For inorder traversal, we begin with the left subtree rooted at B of the root. Before we visit the root of the left subtree, we must visit its left subtree, which is empty. Hence the root of the left subtree rooted at B is visited first. Next, the right subtree of this node is traversed inorder.
Copyright Vikas karanth,2007 All rights reserved.
193

08/20/08

Inorder Traversal

Again, first its left subtree containing only one node D is visited, then its root C is visited, and finally the right subtree of C that contains only one node E is visited. After completing the left subtree of root A, we must visit the root A, and then traverse its right subtree, which is empty. Thus, the complete inorder traversal of the binary tree results in values BDCEA.
Copyright Vikas karanth,2007 All rights reserved.
194

08/20/08

Postorder Traversal

For postorder traversal, we must traverse both the left and the right subtrees of each node before visiting the node itself.

Hence, we traverse the left subtree in postorder yielding values D, E, C and B.


Then we traverse the empty right subtree of root A, and finally we visit the root which is always the last node to be visited in a postorder traversal. Thus, the complete postorder traversal of the tree results in DECBA.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 195

Code - Preorder Traversal


void preorder (p) struct btreenode *p; { /* Checking for an empty tree */ if ( p != null) { /* print the value of the root node */ printf(%d,p->info); preorder(p->left); /* traverse its left subtree */
preorder(p->right); /* traverse its right subtree */ }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 196

Code Inorder Traversal


void inorder(p) struct btreenode *p; { /* checking for an empty tree */ if (p != null) { /* traverse the left subtree inorder */ inorder(p->left);

/* print the value of the root node */ printf(%d,p->info);


/*traverse the right subtree inorder */ inorder(p->right); } }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 197

Code Postorder Traversal


void postorder(struct btreenode *p ) { /* checking for an empty tree */ if (p != null) { postorder(p->left); /* traverse in the left subtree */ postorder(p->right); /* traverse in the right subtree */ /* print the value of the root node */ printf(%d,p->info); }

}
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 198

Accessing Values From a Binary Search Tree Using Inorder Traversal

You may note that when you traverse a binary search tree inorder, the keys will be in sorted order because all the keys in the left subtree are less than the key in the root, and all the keys in the right subtree are greater than that in the root.

The same rule applies to all the subtrees until they have only one key.
Therefore, given the entries, we can build them into a binary search tree and use inorder traversal to get them in sorted order.
Copyright Vikas karanth,2007 All rights reserved.
199

08/20/08

Insertion into a Tree

Another important operation is to create and maintain a binary search tree.

While inserting any node, we have to take care the resulting tree satisfies the properties of a binary search tree.
A new node will always be inserted at its proper position in the binary search tree as a leaf. Before writing a routine for inserting a node, consider how a binary tree may be created for the following input: 10, 15, 12, 7, 8, 18, 6, 20.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 200

Insertion into a Tree

First of all, you must initialize the tree. To create an empty tree, you must initialize the root to null. The first node will be inserted into the tree as a root node as shown in the following figure.

Root

10

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 201

Insertion into a Tree

Since 15 is greater than 10, it must be inserted as the right child of the root as shown in the following figure.
Root

10

15

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 202

Insertion into a Tree

Now 12 is larger than the root; it must go to the right subtree of the root. Further, since it is smaller than 15, it must be inserted as the left child of the root as shown below.

Root

10

15

12

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 203

Insertion into a Tree

Next, 7 is smaller than the root.


7

10

15

Therefore, it must be inserted as the left child of the root as shown in the following figure.
Copyright Vikas karanth,2007 All rights reserved.
08/20/08

12

204

Insertion into a Tree

Similarly, 8, 18, 6 and 20 are inserted at the proper place as shown in the following figures.
10 10

15

15

8
8

12

12

18

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 205

Insertion into a Tree

10

10

15 7

15

12

18

12

18

20

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 206

Insertion into a Tree

This example clearly illustrates that given the root of a binary search tree and a value to be added to the tree, we must search for the proper place where the new value can be inserted. We must also create a node for the new value and finally, we have to adjust the left and right pointers to insert the new node. To find the insertion place for the new value, say 17, we initialize a temporary pointer p, which points to the root node.
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 207

Insertion into a Tree

We can change the contents of p to either move left or right through the tree depending on the value to be inserted. When p becomes null, we know that we have found the insertion place as in the following figure.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 208

Insertion into a Tree


Root

10

15

12

18

20

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 209

Insertion into a Tree

But once p becomes null, it is not possible to link the new node at this position because there is no access to the node that p was pointing to (node with value 18) just before it became null. From the following figure, p becomes null when we have found that 17 will be inserted at the left of 18.
Copyright Vikas karanth,2007 All rights reserved.
210

08/20/08

Insertion into a Tree


Root

10

15

12

18

null

20

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 211

Insertion into a Tree

You therefore need a way to climb back into the tree so that you can access the node containing 18, in order to make its left pointer point to the new node with the value 17. For this, you need a pointer that points to the node containing 18 when p becomes null. To achieve this, you need to have another pointer (trail) that must follow p as p moves through the tree.
Copyright Vikas karanth,2007 All rights reserved.
212

08/20/08

Insertion into a Tree

When p becomes null, this pointer will point to the leaf node (the node with value 18) to which you must link the new node (node with value 17). Once you know the insertion place, you must adjust the pointers of the new node. At this point, you only have a pointer to the leaf node to which the new node is to be linked. You must determine whether the insertion is to be done at the left subtree or the right subtree of the leaf node.
Copyright Vikas karanth,2007 All rights reserved.
213

08/20/08

Insertion into a Tree

To do that, you must compare the value to be inserted with the value in the leaf node. If the value in the leaf node is greater, we insert the new node as its left child; otherwise we insert the new node as its right child.
Copyright Vikas karanth,2007 All rights reserved.
214

08/20/08

Creating a Tree A Special Case of Insertion

A special case of insertion that you need to watch out for arises when the tree in which you are inserting a node is an empty tree. You must treat it as a special case because when p equals null, the second pointer (trail) trailing p will also be null, and any reference to info of trail like trail->info will be illegal. You can check for an empty tree by determining if trail is equal to null. If that is so, we can initialize root to point to the new node.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 215

Code Implementation For Insertion Into a Tree

The C function for insertion into a binary tree takes two parameters; one is the pointer to the root node (root), and the other is the value to be inserted (x). You will implement this algorithm by allocating the nodes dynamically and by linking them using pointer variables. The following is the code implementation of the insert algorithm.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 216

Code Implementation For Insertion Into a Tree


tree insert(tree *s, int x) { tree *trail, *p, *q; q = (struct tree *) malloc (sizeof(tree)); q->info = x; q->left = null; q->right = null; p = s; trail = null;
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 217

Code Implementation For Insertion Into a Tree


while (p != null) { trail = p; if (x < p->info) p = p->left; else
p = p->right;

}
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 218

Code Implementation For Insertion Into a Tree


/*insertion into an empty tree; a special case of insertion */ if (trail == null) { s = q; return (s); } if(x < trail->info) trail->left = q; else trail->right = q; return (s); }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 219

Code Implementation For Insertion Into a Tree Using Recursion

You have seen that to insert a node, you must compare x with root>info.

If x is less than root->info, then x must be inserted into the left subtree.
Otherwise, x must be inserted into the right subtree. This description suggests a recursive method where you compare the new value (x) with the one in the root and you use exactly the same insertion method either on the left subtree or on the right subtree.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 220

Code Implementation For Insertion Into a Tree Using Recursion

The base case is inserting a node into an empty tree. You can write a recursive routine (rinsert) to insert a node recursively as follows:

tree rinsert (tree *s, int x) { /* insertion into an empty tree; a special case of insertion */ if (!s) { s=(struct tree*) malloc (sizeof(struct tree));

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 221

Code Implementation For Insertion Into a Tree Using Recursion


s->info = x; s ->left = null; s->right = null; return (s); } if (x < s->info) s->left = rinsert(x, s->left); else if (x > s->info) s->right = rinsert(x, s->right); return (s); }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 222

Circumstances When a Binary Tree Degenerates Into a Linked List

The shape of a binary tree is determined by the order in which the nodes are inserted. Given the following input, their insertion into the tree in the same order would more or less produce a balanced binary search tree as shown below:

Input values: 10, 15, 12, 7, 8, 18, 6, 20

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 223

Circumstances When a Binary Tree Degenerates Into a Linked List

Root

10

15

12

18

20

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 224

Circumstances When a Binary Tree Degenerates Into a Linked List

If the same input is given in the sorted order as 6, 7, 8, 10, 12, 15, 18, 20, you will construct a lopsided tree with only right subtrees starting from the root. Such a tree will be conspicuous by the absence of its left subtree from the top.
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

225

Circumstances When a Binary Tree Degenerates Into a Linked List


6 7 8 10 12 15 18

20

A Lopsided Binary Tree With Only Right Subtrees

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 226

Circumstances When a Binary Tree Degenerates Into a Linked List

However if you reverse the input as

20, 18, 15, 12, 10, 8, 7, 6, and insert them into a tree in the same sequence, you will construct a lopsided tree with only the left subtrees starting from the root.
Such a tree will be conspicuous by the absence of its right subtree from the top.
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

227

Circumstances When a Binary Tree Degenerates Into a Linked List


20 18 15 12 10 8 7 6

A Lopsided Binary Tree With Only Left Subtrees

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 228

Deletion from a Binary Search Tree

An important function for maintaining a binary search tree is to delete a specific node from the tree. The method to delete a node depends on the specific position of the node in the tree. The algorithm to delete a node can be subdivided into different cases.
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 229

Case I Deletion Of The Leaf Node

If the node to be deleted is a leaf, you only need to set appropriate link of its parent to null, and do away with the node that is to be deleted.
For example, to delete a node containing 1 in the following figure, we have to set the left pointer of its parent (pointing to 1) to null. The following diagram illustrates this.
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

230

Case I Deletion Of The Leaf Node


2 2

null

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 231

Case II Deletion Of a Node With a Single Child

If the node to be deleted has only one child, you cannot simply make the link of the parent to nil as you did in the case of a leaf node. Because if you do so, you will lose all of the descendants of the node that you are deleting from the tree. So, you need to adjust the link from the parent of deleted node to point to the child of the node you intend to delete. You can subsequently dispose of the deleted node.
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

232

Case II Deletion Of a Node With a Single Child


5

Node to be deleted

Node to be deleted

To delete node containing the value 3, where the right subtree of 3 is empty, we simply make the link of the parent of the node with the value 3 (node with value 5) point to the child of 3 (node with the value 2).
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 233

Case III Deletion Of a Node With Two Child Nodes

Complications arise when you have to delete a node with two children. There is no way you can make the parent of the deleted node to point to both of the children of the deleted node. So, you attach one of the subtrees of the node to be deleted to the parent, and then link the other subtree onto the appropriate node of the first subtree.
Copyright Vikas karanth,2007 All rights reserved.
234

08/20/08

Case III Deletion Of a Node With Two Child Nodes

You can attach the right subtree to the parent node and then link the left subtree on to the appropriate node of the right subtree.

Therefore, you must attach the left subtree as far to the left as possible. This proper place can be found by going left until an empty left subtree is found.
For example, if you delete the node containing x as shown in the following figure, you make the parent of x (node with the value r) point to the right subtree of x (node containing y) and then go as far left as possible (to the left of the node containing y) and attach the left subtree there.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 235

Case III Deletion Of a Node With Two Child Nodes


r r q x
Delete node x

q t y t z s Before Deletion of Node x u

After Deletion of Node x

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 236

Code Implementation for Node Deletion for Cases I, II & III


void delete (Struct tree *p) { struct tree *temp if(p==null)printf(Tryingtodeleteanon-existentnode); else if (p->left == null) { temp = p; p = p->right; free(temp); } else if (p->right == null) { temp = p; p = p->left; free (temp); }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 237

Code Implementation for Node Deletion for Cases I, II & III


else if(p->left != null && p->right!= null) { temp = p->right; while (temp->left != null) { temp = temp->left; temp->left = p->left; temp = p; p = p->right; free (Temp); } }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 238

Code Implementation for Node Deletion for Cases I, II & III

Note that the while loop stops when it finds a node with an empty left subtree so that the left subtree of the node to be deleted can be attached here. Also, note that you first attach the left subtree at the proper place and then attach the right subtree to the parent node of the node to be deleted.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 239

Search The Tree

To search a tree, you employ a traversal pointer p, and set it equal to the root of the tree. Then you compare the information field of p with the given value x. If the information is equal to x, you exit the routine and return the current value of p. If x is less than p->info, you search in the left subtree of p. Otherwise, you search in the right subtree of p by making p equal to p->right.
Copyright Vikas karanth,2007 All rights reserved.
240

08/20/08

Search the Tree

You continue searching until you have found the desired value or reach the end of the tree. You can write the code implementation for a tree search as follows:

search (struct tree *p; int x) { p = root; while (p != null && p->info != x) { if (p->info > x) p = p->left; else p = p->right; } return (p); }
Copyright Vikas karanth,2007 All rights reserved.
08/20/08 241

threaded binary tree

A binary tree is threaded by making all right child pointers that would normally be null point to the inorder successor of the node, and all left child pointers that would normally be null point to the inorder predecessor of the node." A threaded binary tree makes it possible to traverse the values in the binary tree via a linear traversal that is more rapid than a recursive in-order traversal. It is also possible to discover the parent of a node from a threaded binary tree, without explicit use of parent pointers or a stack, albeit slowly. This can be useful where stack space is limited, or where a stack of parent pointers is unavailable.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 242

struct TBTNODE { struct NODE *leftchild; int node_value; struct NODE *rightchild; struct NODE *thread; }; struct thtree{ int data; enum boolean left; struct thtree *leftchild; struct thtree *rightchild; enum boolean right; }; Example : threadbt_cp.c

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 243

Summary

In this session, you learnt to: Define a binary tree Describe the terminologies associated with a binary tree Use a dynamic allocated data structure to represent a binary tree Traverse a binary tree Add nodes to a binary tree Remove nodes from a binary tree Search a binary tree

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 244

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 245

Sorting

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 246

Sorting algorithms used in computer science are often classified by:

Computational complexity (worst, average and best behaviour) of element comparisons in terms of the size of the list.

For typical sorting algorithms good behavior is O(n log n) and bad behavior is (Omega(n^2)) Ideal behavior for a sort is O(n) .
Computational complexity of swaps (for "in place" algorithms).

Memory usage (and use of other computer resources). In particular, some sorting algorithms are "in place", such that only O(1) or (O( log n)) memory is needed beyond the items being sorted, while others need to create auxiliary locations for data to be temporarily stored.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 247

Recursion. Some algorithms are either recursive or non recursive, while others may be both (e.g., merge sort).

Stability: stable sorting algorithms maintain the relative order of records with equal keys (i.e., values).
Whether or not they are a comparison sort. A comparison sort examines the data only by comparing two elements with a comparison operator.

General method: insertion, exchange, selection, merging, etc. Exchange sorts include bubble sort and quicksort. Selection sorts include shaker sort and heapsort.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 248

Exchange sorts - Bubble sort


If N elements are given in memory then for sorting we do following stepsFirst compare the 1st and 2nd element of array if 1st < 2nd then compare the 2nd with 3rd If 2nd > 3rd then interchange the value of 2nd and 3rd Now compare the value of 3rd (which has the value of 2nd ) with 4th . Similarly compare until the N-1th element is compared with Nth element. Now the highest value element is reached at the Nth place. Now elements will be compared until N-1 elements.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 249

Time complexity

This requires to perform (n1) passes. In the first pass we have (n1) pairs, in the second pass we have (n2) pairs, and in the last (or (n1)th) pass, we have only one pair. Therefore, the number of probes or comparisons that are required to be carried out is (n-1)+(n-2)+(n-3)++1 n*(n-1) / 2 and the order of the algorithm is O(n2).
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

250

Void bubbleSort(int num[]) { for(i=0;i<5-1;i++) for(j=0;j<5-(i+1);j++) { if(num[j]>num[j+1]) { temp=num[j]; num[j]=num[j+1]; num[j+1]=temp; } } for(i=0;i<5;i++) printf("%d\t",num[i]); } }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 251

Quick Sort

The idea behind sorting is much easier in two lists than one long list.

Take the first element of list as pivot.


Place pivot at the proper place in list. So one element of the list i.e, pivot will be at its proper place. Create two sublists left and right side of pivot. Repeat the same process until all elements of list are at proper position in list.
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

252

The average case-time complexity of the quick sort algorithm can be determined as follows:

We assume that every time this is done, the list gets split into two approximately equal-sized sublists. If the size of a given list is n, it gets split into two sublists of size approximately n/2. Each of these sublists gets further split into two sublists of size n/4, and this is continued until the size equals 1. When the quick sort works with a list of size n, it places the key element in its proper position in the list. This requires no more than n iterations. After placing the key element in its proper position in the list of size n, quick sort activates itself twice to work with the left and right sublists, each assumed to be of size n/2. Therefore T(n) is the time required to sort a list of size n.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 253

Since the time required to sort the list of size n is equal to the sum of the time required to place the key element in its proper position in the list of size n, and the time required to sort the left and right sublists, each assumed to be of size T(n/2). T(n) turns out to be:

T(n) = c*n + 2*T(n/2)


where c is a constant and T(n/2) is the time required to sort the list of size n/2.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 254

Similarly, the time required to sort a list of size n/2 is equal to the sum of the time required to place the key element in its proper position in the list of size n/2 and the time required to sort the left and right sublists each assumed to be of size n/4. T(n/2) turns out to be: T(n/2) = c*n/2 + 2*T(n/4) where T(n/4) is the time required to sort the list of size n/4. T(n/4) = c*n/4 + 2*T(n/8), and so on. We eventually we get T(1) = 1. T(n) = c*n + 2(c*n(n/2) + 2T(n/4)) T(n) = c*n + c*n + 4T(n/4)) = 2*c*n + 4T(n/4) = 2*c*n + 4(c*(n/4) + 2T(n/8)) T(n) = 2*c*n + c*n + 8T(n/8) = 3*c*n + 8T(n/8) T(n) = (log n)*c*n + n T(n/n)= (log n)*c*n + n T(1) = n + n*(log n) *c T(n)n log(n)

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 255

To place the pivot at proper place, follow the steps Compare the pivot element one by one from right to left for getting the element which has value less than pivot element. Interchange the element with pivot element.

Now the comparison will start from the interchanged element position from left to right for getting the element which has higher value than pivot.
Repeatthesameprocessuntilpivotisatitsproperposition.
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

256

Example for Quick sort :

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 257

Insertion sorts

Simple Insertion sort

Shell sort

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 258

Insertion sort
Efficient on (quite) small data sets Efficient on data sets which are already substantially sorted: it runs in O(n + d) time, where d is the number of inversions More efficient in practice than most other simple O(n2) algorithms such as selection sort or bubble sort the average time is n2/4 and it is linear in the best case Stable (does not change the relative order of elements with equal keys) In-place (only requires a constant amount O(1) of extra memory space)

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 259

Insertion sort

In abstract terms, every iteration of an insertion sort removes an element from the input data, inserting it at the correct position in the already sorted list, until no elements are left in the input.

Sorting is typically done in-place. The resulting array after k iterations contains the first k entries of the input array and is sorted.
In each step, the first remaining entry of the input is removed, inserted into the result at the right position, thus extending the result:

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 260

Shell sort

The original implementation performs O(n2) comparisons and exchanges in the worst case. Shell sort improves insertion sort by comparing elements separated by a gap of several positions. This lets an element take "bigger steps" toward its expected position. Multiple passes over the data are taken with smaller and smaller gap sizes. The last step of Shell sort is a plain insertion sort, but by then, the array of data is guaranteed to be almost sorted. Consider a small value that is initially stored in the wrong end of the array. Using an O(n2) sort such as bubble sort or insertion sort, it will take roughly n comparisons and exchanges to move this value all the way to the other end of the array. Shell sort first moves values using giant step sizes, so a small value will move a long way towards its final position, with just a few comparisons and exchanges.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 261

One can visualize Shellsort in the following way: arrange the list into a table and sort the columns (using an insertion sort). Repeat this process, each time with smaller number of longer columns. At the end, the table has only one column. While transforming the list into a table makes it easier to visualize, the algorithm itself does its sorting in-place (by incrementing the index by the step size, For example, consider a list of numbers like [ 13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10 ]. If we started with a step-size of 5, we could visualize this as breaking the list of numbers into a table with 5 columns. This would look like this:

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 262

13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10

We then sort each column, which gives us

10 14 73 25 23 13 27 94 33 39 25 59 94 65 82 45

When read back as a single list of numbers, we get [ 10 14 73 25 23 13 27 94 33 39 25 59 94 65 82 45 ].

Here, the 10 which was all the way at the end, has moved all the way to the beginning.
This list is then again sorted using a 3-gap sort, and then 1-gap sort (simple insertion sort).

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 263

void shell_sort(int A[], int size) { int i, j, increment, temp; increment = size / 2; while (increment > 0) { for (i = increment; i < size; i++) { j = i; temp = A[i]; while ((j >= increment) && (A[j-increment] > temp)) { A[j] = A[j - increment]; j = j - increment; } A[j] = temp; } if (increment == 2) increment = 1; else increment = (int) (increment / 2.2); } }

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 264

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 265

Merge sort

Merge sort

Radix sort

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 266

Merge Sort

If there are two sorted lists of array then process of combining these sorted lists into sorted order is called merging Ther are Two approaches - First approach

Take the first array, after that take second array and sort them with any sorting.
But it is not useful because both the lists are sorted and we are taking them as unsorted list.
Copyright Vikas karanth,2007 All rights reserved.

08/20/08

267

Merge Sort

The second approach is to take one element of each array, compare them and then take the smaller one in third array. Repeat this process until the elements of any array are finished. Then take the remaining elements of unfinished array in third array.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 268

we start by viewing it to be n lists each of size 1, and merge the first list with the second list to form a single sorted list of size 2. Similarly, we merge the third and the fourth lists to form a second single sorted list of size 2, and so on. This completes one pass. We then consider the first sorted list of size 2 and the second sorted list of size 2, and merge them to form a single sorted list of size 4. Similarly, we merge the third and the fourth sorted lists, each of size 2, to form the second single sorted list of size 4, and so on. This completes the second pass. In the third pass, we merge these adjacent sorted lists, each of size 4, to form sorted lists of size 8. We continue this process until we finally obtain a single sorted list of size n.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 269

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 270

Time complexity

The merging of two sublists, the first running from the index 0 to m, and the second running from the index (m + 1) to (n 1) requires no more than (nl+1) iterations. So if l =1, then no more than n iterations are required, where n is the size of the list to be sorted. Therefore, if n is the size of the list to be sorted, every pass that a merge routine performs requires a time proportional to O(n), since the number of passes required to be performed is log2n. The time complexity of the algorithm is O(n log2(n)), for both average-case and worst-case. The merge sort requires an additional list of size n.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 271

Radix sort

In computer science, radix sort is a sorting algorithm that sorts integers by processing individual digits. Because integers can represent strings of characters (e.g., names or dates) and specially formatted floating point numbers, radix sort is not limited to integers. Two classifications of radix sorts are least significant digit (LSD) radix sorts and most significant digit (MSD) radix sorts. LSD radix sorts process the integer representations starting from the least significant digit and move towards the most significant digit. MSD radix sorts work the other way around.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 272

Original, unsorted list: 170, 45, 75, 90, 2, 24, 802, 66


Sorting by least significant digit (1s place) gives: 170, 90, 2, 802, 24, 45, 75, 66 Sorting by next digit (10s place) gives: 2, 802, 24, 45, 66, 170, 75, 90 Sorting by most significant digit (100s place) gives: 2, 24, 45, 66, 75, 90, 170, 802 It is important to realize that each of the above steps requires just a single pass over the data, since each item can be placed in its correct bucket without having to be compared with other items.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 273

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 274

Selection and tree sorting

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 275

Selection sort

Selection sort is the selection of an element and keeping it in sorted order. Take the smallest element and keep in the new list, after that second smallest element and so on until the largest element of the list.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 276

Heap sort

Heapsort is a sorting technique that sorts a contiguous list of length n with O(n log2 (n)) comparisons and movement of entries, even in the worst case. Hence it achieves the worst-case bounds better than those of quick sort, and for the contiguous list, it is better than merge sort, since it needs only a small and constant amount of space apart from the list being sorted. Heapsort proceeds in two phases. First, all the entries in the list are arranged to satisfy the heap property, and then the top of the heap is removed and another entry is promoted to take its place repeatedly.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 277

Therefore, we need a function that builds an initial heap to arrange all the entries in the list to satisfy the heap property.

The function that builds an initial heap uses a function that adjusts the ith entry in the list, whose entries at 2i and 2i + 1 positions already satisfy the heap property in such a manner that the entry at the ith position in the list will also satisfy the heap property.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 278

Searching

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 279

Sequential searching

Sequential searching is nothing but searching an element in linear way. This can be in array or in linked list. We have a need to start the search from beginning and scan the elements one by one until the end of array or linked list. If search is successful then it will return the location of element, otherwise it will return the failure notification.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 280

Binary Search

The sequential search situation will be in worst case if the element is at the end of the list. To eliminate this problem we have binary search. The condition for binary search is the data should be in sorted array. We compare the element with the middle element of the array. If it is less than the middle element then we search it in the left position of the array and if it is greater than the middle element then search in the right portion of the array. Now we will take that portion only for search and compare with middle element of that portion. This process will be iteration until we find the element or middle element has no left or right portion to search.

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 281

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 282

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 283

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 284

Copyright Vikas karanth,2007 All rights reserved.


08/20/08 285

Vous aimerez peut-être aussi