Beruflich Dokumente
Kultur Dokumente
Abstract Data Type (ADT): An ADT is a set of elements with a collection of well defined
operations. The operations can take as operands not only instances of the ADT but other types of operands or instances of other ADTs. Similarly results need not be instances of the ADT At least one operand or the result is of the ADT type in question.
Examples of ADTs include list, stack, queue, set, tree, graph, etc.
Complexity of Algorithms
It is very convenient to classify algorithms based on the relative amount of time or relative amount of space they require and specify the growth of time /space requirements as a function of the input size. Thus, we have the notions of: Time Complexity: Running time of the program as a function of the size of input Space Complexity: Amount of computer memory required during the program execution, as a function of the input size.
Algorithm Analysis
The finiteness condition implies that an algorithm never goes into an infinite loop no matter what input we give it. It is difficult to predict the actual computation time of an algorithm without knowing the intimate details of the computer architecture, the compiler, the quality of the program and the other factors. But, we can measure the time for a given algorithm by using some special performance programs called benchmarks. It is also possible to predict the performance by looking at the growth rate of an algorithm. It is known that the running time of the algorithm is a function of the input size such as the number of elements in an array, the number of records in a file etc... The amount of time that any algorithm takes to run depends on the amount of input it must process. Ex. Sorting an array of 10000 elements require more processing time than sorting an array of 100 elements. Another example: it is common to write programs whose running time varies with the square of the problem size. Thus, a program taking 1 sec to complete a file handling problem with 10 records in the file, require 4 sec for 20 records (not 2 sec). Increasing the file size by a factor of 10; ie 100 records will increase the running time to 100 sec or 1000 records will require 10000sec (3 hours) to complete!! And 10000 records will require almost two weeks to finish. This is a long time compared to 1 sec for 10 records test. This example shows that we need to know something about the growth rate of our algorithm as test program running time may grow to unacceptable values when real-world-sized data is used. An experienced programmer estimates the performance of the algorithm and takes some actions if there is any. In some cases there may be no alternative solution to the program running in "squared" time but at least the programmer will not be surprised at the end.
Asymptotic Notation
Suppose we are considering two algorithms, A and B, for solving a given problem. Furthermore, let us say that we have done a careful analysis of the running times of each of the algorithms and determined them to be TA(n)and TB(n), respectively, where n is a measure of the problem size. Then it should be a fairly simple matter to compare the two functions TA(n)and TB(n),to determine which algorithm is the best! In the general case, we have no a priori knowledge of the problem size. However, if it can be shown that TA(n)TB(n), say, that for all n0, then algorithm A is better than algorithm B regardless of the problem size. Unfortunately, we usually don't know the problem size before hand, nor is it true that one of the functions is less than or equal the other over the entire range of problem sizes. In this case, we consider the asymptotic behavior of the two functions for very large problem siz Definition: Asymptotic notation is used to give a rough estimate of the rate of growth of a formula. The formula usually gives the run time of an algorithm. Informally, O notation is the leading (i.e. quickest growing) term of a formula with the coefficient stripped. An Asymptotic Upper Bound-Big Oh:
3 In 1892, P. Bachmann invented a notation for characterizing the asymptotic behavior of functions. His invention has come to be known as big oh notation: Definition (Big Oh) Consider a function f(n) which is non-negative for all integers n0 . We say that ``f(n) is big oh g(n),'' which we write f(n)=O(g(n)), if there exists an integer n0 and a constant c>0 such that for all integers n n0, f(n) cg(n).
E.g.
n, 1000n and 5n+2 - O(n) n+log n are O(n) n2 +n+log n, 10n2 +n - O(n2) n log n+10n - O(n log n) 10 log2 n - O(log2 n) 10000, 250 and 4 - O(1)
Computing big-O of an Algorithm: Following is a shorter way to compute big-O for an algorithm: Atomic operations- Constant time Consecutive statements- Sum of times Conditionals- Larger branch time plus test time Loops- Sum of iterations Function Calls - Time of function body Recursive Functions - Solve Recurrence Relation
First, it is common practice when writing big oh expressions to drop all but the most significant terms. Thus, instead of we simply write . , we simply
write . As a special case of this rule, if the function is a constant, instead of, say O(1024), we simply write O(1). An Asymptotic Lower Bound-Omega The big oh is an asymptotic upper bound. In this section, we introduce a similar notation for characterizing the asymptotic behavior of a function, but in this case it is a lower bound.
4 Consider a function f(n) which is non-negative for all integers , if there exists an integer . , . We say and a constant
Definition (Omega)
that ``f(n) is omega g(n),'' which we write c>0 such that for all integers
The definition of omega is almost identical to that of big oh. The only difference is in the comparison-for big oh it is ; for omega, it is .
} }
the outer loop count is N but the inner loop executes N times for each time. So the body of the inner loop will execute N*N and the entire performance will be O(N2). Ex: for (i=1; i<=N; i++) { for (j=0; j< i; j++) {
sequence of simple statments
}
}
In this case outer count trip is N, but the trip count of the inner loop depends not only N, but the value of the outer loop counter as well. If outer counter is 1, the inner loop has a trip count 1 and so on. If outer counter is N the inner loop trip count is N. How many times the body will be executed? 1+2+3+(N-1)+N = N(N+1) / 2 = ((N2) +N )/2 Therefore the performance is O(N2). For large N the contribution of the N/2 term is negligible Generalization: A structure with k nested counting loops where the counter is just incremented or decrement by one has performance O(Nk) if the trip counts depends on the problem size only. While loops: The control variable is multiplied or divided, each time the loop iteration is performed. Each loop has an initialization step, a termination condition and a modification step indicating how the control variable should be changed. In while-structure, the termination condition is checked before the iteration. Let's consider the following: control=1; while (control < n) {
Simple statements;
control=2*control; } In the above example performance depends on the problem size N. The control variable is multiplied by 2 until it gets larger than N. The initial value of control is 1, after k iterations we will have control = 2k In order to find k we take the log of both sides; log2 (control) = log22k , log2 (control)=k . Since the loop stops when control > N , the performance of the algorithm is O(log2 N ). Generalization: Assume that we multiply the control by some other constant; say fact.
7 Then after k iterations; control = fact k so, the performance is O(log(N)) where the log is taken to the base fact. In considering the performance base does not matter as form one base to another the additional factor is a constant. Quadratic algorithms are impractical for input sizes exceeding a few thousand. Cubic algorithms are impractical for input sizes exceeding a few hundred.
The running time of a for loop is at most the running time of the statements inside the for loop (including tests)times the number of iterations.
RULE 2-NESTED FOR LOOPS:
Analyze these inside out. The total running time of a statement inside a group of nested for loops is the running time of the statement multiplied by the product of the sizes of all the for loops. As an example, the following program fragment is O(n2):
for( i=0; i<n; i++ ) for( j=0; j<n; j++ ) k++;
These just add (which means that the maximum is the one that counts). As an example, the following program fragment, which has O(n) work followed by O (n2) work, is also O (n2):
for( a[i] for( for( a[i] i=0; i<n; = 0; i=0; i<n; j=0; j<n; += a[j] + i++) i++ ) j++ ) i + j;
RULE 4-lF/ELSE:
the running time of an if/else statement is never more than the running time of the test plus the larger of the running times of S1 and S2.
8 STACK A stack is a linear data structure for collection of items , with the restriction that items can be added one at a time and can only be removed in the reverse order in which they were added (i-e Last In First Out LIFO). The last item represents the top of the stack. Such a stack resembles a stack of trays in a cafeteria, or stack of boxes. Only the top tray can be removed from the stack and it is the last one that was added to the stack. A tray can be removed only if there are some trays on the stack, and a tray can be added only if there is enough room to hold more trays. The common operations associated with a stack are as follows: 1. push: adds a new item on top of a stack. 2. pop: removes the item on the top of a stack 3. isEmpty: Check to see if the stack is empty 4. isFull: Check to see if stack is already full 5. returnTop: Indicate which item is at the top Applications: A stack is very useful in situations when data have to be stored and then retrieved in the reverse order. 1. Function Calls 2. convert an infix expression into postfix form. e.g. Infix a+b*c (a+b)*c (a + b) * (c d) Postfix abc*+ ab+c* ab+cd-*
53*2+64*+
= 41
A stack can also be used to convert an infix expression in standard form into postfix form. Infix: operator is between operands A + B Postfix : operator follows operands A B + We shall assume that the expression is a legal one (i.e. it is possible to evaluate it). 1. When an operand is read, it will be placed on output list (printed out straight away).
9 2. The operators are pushed on a stack. However, if the priority of the top operator in the stack is higher than the operator being read, then it will be put on output list, and the new operator pushed on to the stack. The priority is assigned as follows. 1. ( Left parenthesis in the expression 2. * / 3. + 4. ( Left parenthesis inside the stack The association is assumed to be left to right. The left parenthesis has the highest priority when it is read from the expression, but once it is on the stack, it assumes the lowest priority. Algorithm: while there are more characters in the input { Read next symbol ch in the given infix expression. If ch is an operand put it on the output. If ch is an operator i.e.* , /, +, -, or ( { If stack is empty push ch onto stack; Else check the item op at the top of the stack; end while (more items in stack && priority(ch)<= priority(op)) { pop op and append it to the output, provided it is not an open parenthesis op = top element of stack } push ch onto stack } If ch is right parenthesis ) Pop items from stack until left parenthesis is reached Pop left parenthesis and discard both left and right parenthesis }/* now no more characters in the infix expression*/ Pop remaining items in the stack to the output.
e.g 3: a + (( b * c ) / d ) a + ( ( b c * ) /d ) (precedence of * and / are same and they are left associative) a+(bc*d/) abc*d/+
11 Evaluating a Postfix Expression We can evaluate a postfix expression using a stack. 1. Each operator in a postfix string corresponds to the previous two operands . 2. Each time we read an operand we push it onto a stack. 3. When we reach an operator its associated operands (the top two elements on the stack ) are popped out from the stack. 4. We then perform the indicated operation on them and push the result on top of the stack so that it will be available for use as one of the operands for the next operator .
12 Implementation of stacks using arrays: /* Program of stack using array*/ #include <STDIO.H> #define MAX 50 int top = -1; int stack_arr[MAX]; main() { int choice; while(1) { switch(choice) { case 1 : push(); break; case 2: pop(); break; case 3: display(); break; case 4: exit(1); default: printf("Wrong choice\n"); }/*End of switch*/ }/*End of while*/ }/*End of main()*/
push() { int pushed_item; if(top = = (MAX-1)) printf("Stack Overflow\n"); else { printf("Enter the item to be pushed in stack : "); scanf("%d",&pushed_item); top=top+1; stack_arr[top] = pushed_item; }
13 }/*End of push()*/
pop() { if(top = = -1) printf("Stack Underflow\n"); else { printf("Popped element is : %d\n",stack_arr[top]); top=top-1; } }/*End of pop()*/
display() { int i; if(top = = -1) printf("Stack is empty\n"); else { printf("Stack elements :\n"); for(i = top; i >=0; i--) printf("%d\n", stack_arr[i] ); } }/*End of display()*/
/* Program for conversion of infix to postfix and evaluation of postfix. It will take only single digit in expression */ #include <stdio.h> #define Blank ' ' #define Tab '\t' #define MAX 50 long int pop (); long int eval_post(); char infix[MAX], postfix[MAX]; long int stack[MAX]; int top;
14 main() { long int value; top = 0; printf("Enter infix : "); gets(infix); infix_to_postfix(); // calling infix to postfix conversion function printf("Postfix : %s\n",postfix); value=eval_post(); // caling the function for evaluating the expression printf("Value of expression : %ld\n",value); printf("Want to continue(y/n) : "); }/*End of main()*/ infix_to_postfix() { int i,p=0,type,precedence; char next ; for(i=0; infix[i]!=\0; i++) { if( !white_space(infix[i])) { switch(infix[i]) { case '(': push(infix[i]); break; case ')': while((next = pop()) != '(' ) postfix[p++] = next; break; case '+': case '-': case '*': case '/': case '%': case '^': precedence = prec(infix[i]); while(top!=len && precedence<= prec(stack[top])) postfix[p++] = pop(); push(infix[i]); break; default: /*if an operand comes */ postfix[p++] = infix[i];
15 } } }
/*End of switch */ /*End of if */ /*End of for */
while(top!=len) postfix[p++] = pop(); postfix[p] = '\0' ; /*End postfix with'\0' to make it a string*/ }
/*End of infix_to_postfix()*/
/* This function returns the precedence of the operator */ prec(char symbol ) { switch(symbol) { case '(': return 0; case '+': case '-': return 1; case '*': case '/': case '%': return 2; case '^': return 3; } /*End of switch*/ } /*End of prec()*/
push(long int symbol) { if(top > MAX) { printf("Stack overflow\n"); exit(1); } else { top=top+1; stack[top] = symbol; } } /*End of push()*/ long int pop() {
// function to check the symbol is a blank space or not white_space(char symbol) { if( symbol == Blank || symbol == Tab || symbol == '\0') return 1; else return 0; } /*End of white_space()*/
// function to evaluate an expression long int eval_post() { long int a,b,temp,result,len; int i; len=strlen(postfix); postfix[len]='#'; for(i=0;postfix[i]!='#';i++) { if(postfix[i]<='9' && postfix[i]>='0') push( postfix[i]-48 ); else { a=pop(); b=pop(); switch(postfix[i]) { case '+': temp=b+a; break; case '-': temp=b-a;
17 break; case '*': temp=b*a; break; case '/': temp=b/a; break; case '%': temp=b%a; break; case '^': temp=pow(b,a); } push(temp);
/*End of switch */
Program to convert decimal number to binary number /* Program of stack using array*/ #include <STDIO.H> #define MAX 50 int top = -1; int stack_arr[MAX]; main() { int dec; printf( enter a decimal number\n) scanf(%d,&dec); while(n>0) { rem = dec/2; push(rem); dec=dec/2; } printf(binary number : \n); while(top!=-1) { b=pop(); printf(%d,b) } }
18
19 Queue A queue is simply a waiting line that grows by adding elements to its end and shrinks by removing elements from the front. Compared to stack, it reflects the more commonly used maxim in real-world, namely, first come, first served. Waiting lines in supermarkets, banks, food counters are common examples of queues. Definition: It is a list from which items may be deleted at one end (front) and into which items may be inserted at the other end (rear). It is also referred to as a first-in-first-out (FIFO) data structure.
Operations on Queue: 1. enqueue (q, x) inserts item x at the rear of the queue q 2. x = dequeue (q) removes the front element from q and returns its value. 3. isEmpty(q) Check to see if the queue is empty. 4. isFull(q) checks to see if there is space to insert more items in the queue. Queue overflow results from trying to add an element onto a full queue and queue underflow happens when trying to remove an element from an empty queue. Applications of Queues: Queues have many applications in computer systems: 1. Direct application Handling jobs in a single processor computer print spooling transmitting information packets in computer networks 2. Indirect applications Auxiliary data structure for algorithms Component of other data structures
20 #include<ctype.h> # define MAXSIZE 200 int q[MAXSIZE]; int front=-1; int rear = -1; void enqueue(int); int dequeue(); void main() { int choice=1,i,num; while(choice ==1) { printf(" MAIN MENU: 1.Add element to queue 2.Delete element from the queue "); scanf("%d",& choice); switch(choice) { case 1: printf("Enter the data... "); scanf("%d",&num); enqueue(num); break; case 2: i=dequeue(); printf("dequeued value is %d ",i); break; default: printf("Invalid Choice ... "); } printf("Do you want to continue press 1 for yes, any other key to exit"); scanf("%d" , & choice); } }
//end of outer while //end of main
rear
%d
and
the
value
of
front
is
int dequeue() { int a; if(front == rear) { printf(" QUEUE EMPTY"); return(0); } else { front = front+1 a=q[front]; } return(a); }
22
Circular Queue
Linear queues have a very big drawback that once the queue is FULL, even though we delete few elements from the "front" and relieve some occupied space, we are not able to add anymore elements, as the "rear" has already reached the Queue's rear most position.
Solution: Once "rear" reaches the Queue's maximum limit, the "first" element will become the queue's new "rear". Now the Queue is not straight but circular. i-e once the Queue is full the "First" element of the Queue becomes the "Rear" most element, if and only if the "Front" has moved forward;
When the queue is empty, there is no front item and no rear item But if the front and rear variables point to valid array items, the difference between an empty queue and a non-empty queue can be identified by o Method 1: set front and/or rear to an out of range value (such as -1) to indicate an empty queue o Method 2: use a separate counter or Boolean flag to indicate an empty queue, in which case (rear+1)%MAX == front while the queue is empty (note that this is also the case when the queue is full!) o Method 3: always keep at least one queue entry unused. Then an empty queue has condition (rear+1)%MAX == front , while a full queue has condition (rear+2)%MAX == front
23 Program for Circular Queue implementation through Array #include <stdio.h> #include<ctype.h> # define MAXSIZE 8 int cq[MAXSIZE]; int front=-1, rear=-1; void enqueue(int); int dequeue( ); void main() { int choice=1,i,num; clrscr(); while(choice = =1) { printf(" MAIN MENU: \n 1.Add element to Circular Queue \n 2.Delete element from the Circular Queue \n "); scanf("%d",& choice); switch(choice) { case 1: printf("Enter the data... "); scanf("%d",&num); enqueue(num); break; case 2: i=dequeue(); printf("Value returned from dequeue function is break; default: printf("Invalid Choice . "); }
%d ",i);
printf(" Do you want to do more operations on Circular Queue ( 1 for yes, any other key to exit) "); scanf("%d" , &choice); } } //end of //end of main outer while
24 void enqueue(int item) { rear=rear+1; rear= (rear%MAX); if(front = =rear) { printf("CIRCULAR QUEUE FULL"); return; } else { cq[rear]=item; printf("Rear = %d Front = %d ",rear,front); } }
int dequeue() { int a; if(front = = rear) { printf("CIRCULAR STACK EMPTY"); return (0); } else { front=front+1; front = front%MAX; a=cq[front]; printf("Rear = %d Front = %d ",rear,front); return(a); } } Applications: 1. Round robin scheduling It is one of the oldest, simplest, and most widely used scheduling algorithms, designed especially for time-sharing systems. A small unit of time, called timeslice or quantum, is defined. All runnable processes are kept in a circular queue. The CPU scheduler goes around this queue, allocating the CPU to each process for a time interval of one quantum. New processes are added to the tail of the queue. The CPU scheduler picks the first process from the queue, sets a timer to interrupt after one quantum, and dispatches the process.
25 If the process is still running at the end of the quantum, the CPU is preempted and the process is added to the tail of the queue. If the process finishes before the end of the quantum, the process itself releases the CPU voluntarily. In either case, the CPU scheduler assigns the CPU to the next process in the ready queue. Every time a process is granted the CPU, a context switch occurs, which adds overhead to the process execution time.
26
LINKED LISTS List is a ordered collections of objects. The linked list is a data structure which consists of a series of structures, which are not necessarily adjacent in memory. Each structure contains the data and a pointer to a structure containing its successor. We call this the next pointer. The last cell's next pointer points to NULL Arrays In an array each node (element) follows the previous one physically (i.e. contiguous spaces in the memory) Arrays are fixed size: either too big (unused space ) or not big enough (overflow problem) Maximum size of the array must be predicted which is sometimes not possible. Inserting and deleting elements into an array is difficult. Have to do lot of data movement, if in array of size 100, an element is to be inserted after the 10th lement, then all remaining 90 have to be shifted down by one position. Linked Lists Linked lists are appropriate when the number of data elements to be represented in the data structure are not known in advance. Linked lists are dynamic, so the length of a list can increase or decrease as necessary. A linked list is a collection of nodes, each node containing a data element. Each node does not necessarily follow the previous one physically in the memory. Nodes are scattered at random in memory. Insertion and Deletion can be made in Linked lists, by just changing links of a few nodes, without disturbing the rest of the list. This is the greatest advantage. But getting to a particular node may take large number of operations. Every node from start needs to be traversed to reach the particular node. Types: Single linked list Circular linked list Doubly linked list Single linked list : Node structure: A node in a linked list is a structure that has at least two fields. One of the fields is a data field; the other is a pointer that contains the address of the next node in the sequence. struct node { int data; struct node *next; } The pointer variable next is called a link. Each structure is linked to a succeeding structure by way of the field next. The pointer variable next contains an address of the location in memory of the
struct node { int number; struct node * link; }; 2. A node with 3 data fields:
struct student { char name[20]; int id; double grdPts; struct student *next_student; }; 3. A structure in a node:
28 char address[30]; char phone[10]; }; struct person_node { struct person data; struct person_node *next; };
The head pointer addresses the first node of the list, and each node points at its successor node. The last node has a link value NULL. Empty List: Empty Linked list is a single pointer having the value of NULL. pHead = NULL; Basic Linked List Operations: 1. Add a node 2. Delete a node 3. Looking up a node 4. List Traversal (e.g. Counting nodes) 1. Add a Node: There are four steps to add a node to a linked list: 1. Allocate memory for the new node. 2. Determine the insertion point (you need to know only the new nodes predecessor) 3. Point the new node to its successor. 4. Point the predecessor to the new node. E.g. adding Two nodes to an Empty List: To form linked list which contains two integers 39 and 60, first define a structure to hold two pieces of information in each node- an integer value, and the address corresponding to the next structure of same type. struct node { int data; struct node *next; };
29 struct node *pNew, *pHead; Use malloc to fetch one node from the memory, and let pNew point to this node. pNew = (struct node *) malloc(sizeof(struct node)); pHead = NULL; Now to store 39 in the data part of the node, we can use (*pNew).data = 39; A more convenient way is to use the notation pNew->data = 39; pNew->next = NULL;
At this moment there are no elements in the list. Pointer pHead points to NULL. First element 39 is stored in node pNew. So make it the head node. pNew->next = pHead; /* set link to NULL*/ pHead = pNew; /* point list to first node*/
Now we have got one node in the list, which is being pointed to by pHead. To put the next value 60 on the list, we must use malloc to fetch another node from the memory. pNew = (struct node *) malloc(sizeof(struct node)); Let us now assign the data value to this node: pNew->data = 60; pNew->next = NULL;
Now we need to link this new node to the pHead node, which can be done by following statement: pHead->next = pNew; This gives us the list of two nodes:
30
Add a node at the end of the list: Given the list pHead
let us say we are interested in adding a node containing 90 at the end of this list. As a first step, use malloc to get a new node pNew from the memory and load 90 in the data field and NULL in the next field. pNew->data = 90; pNew->next = NULL;
Now, the last node containing 80 would become the predecessor of the node containing 90 after the node is added. Let us call this node as pPre. Use a temporary variable pPre and initialize it to pHead. Now hop through the nodes till you get a node whose next link is NULL. Then pPre will be pointing to the last node. pPre = pHead; while ( pPre->next != NULL ) pPre = pPre->next; Now link pPre with pNew . pPre->next = pNew; This would result in the node pNew to be attached to the list as the last node.
31 So far we have been discussing separate codes for inserting a node in the middle, at the end or at front of a list. We can combine all the codes and write a general code for inserting a node anywhere in the list. Given the head pointer (pHead), the predecessor (pPre) and the data to be inserted (item), we first allocate memory for the new node (pNew) and adjust the link pointers. struct node { int data; struct node *next; }; struct node *pHead; pHead=NULL:
void insert_begin(int val) { struct node *pNew; pNew = (struct node *)malloc(sizeof(struct node)); pNew->data = val; if (pHead == NULL) { /*Adding to empty list*/ pHead = pNew; pHead->next= NULL; } else { /* Adding as first node*/ pNew->next = pHead; pHead = pNew; } } void insert_last(int val) { struct node *pNew, *pTemp; pNew = (struct node *)malloc(sizeof(struct node)); pNew->data = val; pNew->next= NULL; if (pHead == NULL) { /*Adding to empty list*/ pHead = pNew; pHead->next= NULL; } else
32 { /* Adding as last node*/ pTemp = pHead; while(pTemp->next!=NULL) pTemp = pTemp->next; pTemp->next = pNew; pNew->next = NULL; } } Insertion a node into single link List( General case) void insert_between(int val) { struct node *pNew, *pPre, *pTemp; int key; printf(Enter the value after which node to be inserted); scanf(%d, &key); // Allocate memory for the new node pNew = (struct node *)malloc(sizeof(struct node)); pNew->data = val; pNew->next = NULL; if (pHead == NULL) { pHead = pNew; pHead->next= NULL; } else { pTemp = pHead; /* Searching insertion point*/ while(pTemp!=NULL) { if(pTemp->data == key) break; else { pPre = pTemp; pTemp = pTemp->next; } } if( pTemp==NULL) printf( The insertion point cant be found\n); else { /*Adding to empty list*/
33 if ( pTemp==pHead) // inserted before head node { pNew->next = pTemp; pHead = pNew; } else // insert as intermediate or last node { pNew->next = pTemp->next; pTemp->next = pNew; } } } } 2. Delete a node: Deletinganoderequiresthatwelogicallyremovethenodefromthelistbychangingvariouslinkpointers andthenphysicallydeletingthenodefromtheheap. Wecandelete thefirstnode anynodeinthemiddle theendnode Tologicallydeleteanode: 1. first locate the node itself , name the current node as pTemp and its predecessor node as pPre. 2.changethepredecessorslinkfieldtopointtosuccessorofthecurrentnode. 3.recyclethenode(senditbacktomemory)usingfree. void delete (int key, struct node *pHead) { struct node *pTemp, *pPre; pTemp=pHead; if (pHead == NULL) printf( Empty linked list\n);; else // Search node { pTemp = pHead; while (pTemp!=NULL) { if(pTemp->data == key) break; else { pPre = pTemp pTemp = pTemp->next;
34 } }
if( pTemp==NULL) printf(Node not found\n); else { if ( pTemp == pHead ) pHead = pTemp->next; else pPre->next = pTemp->next; free(pTemp) printf(Node deleted\n ); } } }
3. Search for an item in the list: /* Given the item and the pointer to the head of the list, the function returns a pointer to the node which matches the item, or returns NULL if the item is not found */ Struct node *Search (int item, struct node *pHead) { struct node *pTemp; int i=1; pTemp=pHead; if (pHead == NULL) printf( Empty linked list\n);; else // Search node { pTemp = pHead; while (pTemp!=NULL) { if(pTemp->data == key) break; else { pTemp = pTemp->next; i = i+1; } } if( pTemp==NULL) printf(Node cant be found\n); else printf(Node found at location %d\n, i ); }
35 return(pTemp) }
4. Counting the nodes in a List: int count(struct node *pHead) { struct node * pTemp; int c = 0; pTemp = pHead; while (pTemp != NULL) { c = c + 1; pTemp = pTemp->next; } return c; } 5.Printingthecontentsofalist: Toprintthecontents,traversethelistfromtheheadnodetothelastnode. void display( struct node *pHead) { struct node *pTemp; pTemp = pHead; while (pTemp != NULL) { printf(%d, pTemp -> data); pTemp = pTemp -> next; } }
36
/* PROGRAM FOR IMPLEMENTATION OF SINGLE LINKED LIST */ #include"stdio.h" #include<alloc.h> #define NULL 0 /* STRUCTURE CONTANING A DATA PART AND A LINK PART */ struct node { int data; struct node *next; }*pHead; /* phead is a global pointer contains the adress of the first node in list*/ pHead=NULL; /* THIS IS THE MAIN PROGRAM main() { int i, num, loc; */
while(1) /* this is an indefinite loop */ { printf(" \n1.INSERT A NUMBER AT BEGINNING;\n"); printf(" 2.INSERT A NUMBER AT LAST:\n"); printf(" 3.INSERT A NUMBER AT A PARTICULAR LOCATION LIST:\n"); printf(" 4.PRINT THE ELEMENTS IN THE LIST :\n"); printf(" 5.PRINT THE NUMBER OF ELEMENTS IN THE LIST\n"); printf(" 6.DELETE A NODE IN THE LINKED LIST:\n"); printf(" 7.GET OUT OF LINKED LIST:\n"); printf(" PLEASE, ENTER THE NUMBER:\n"); scanf("%d",&i); /* ENTER A VALUE FOR SWITCH */ switch(i) { case 1: printf("ENTER THE NUMBER :-"); scanf("%d",&num); insert_begin(num); break; case 2: printf("ENTER THE NUMBER :-"); scanf("%d",&num); insert_last(num); break; case 3:
IN
37 printf(" PLEASE ENTER THE NUMBER :-"); scanf("%d",&num); printf("PLEASE ENTER THE LOCATION NUMBER :-"); scanf("%d",&loc); insert_after(num,loc); break; 4: printf(" THE ELEMENTS IN THE LIST ARE : "); display( ); break; 5: printf(" Total No Of Elements In The List are%d",count()); break; 6: printf(" PLEASE ENTER A NUMBER to be deleted :"); scanf("%d",&num); delete(num); break; 7: exit();
case
case
case
case
} }
/* ADD A NEW NODE AT BEGINNING */ void insert_begin(int val) { struct node *pNew; pNew = (struct node *)malloc(sizeof(struct node)); pNew->data = val; if (pHead == NULL) { /*Adding to empty list*/ pHead = pNew; pHead->next= NULL; } else { /* Adding as first node*/ pNew->next = pHead; pHead = pNew; } }
38 /*THIS FUNCTION ADDS A NODE AT THE LAST OF LINKED LIST */ void insert_last(int val) { struct node *pNew, *pTemp; pNew = (struct node *)malloc(sizeof(struct node)); pNew->data = val; pNew->next= NULL; if (pHead == NULL) { /*Adding to empty list*/ pHead = pNew; pHead->next= NULL; } else { /* Adding as last node*/ pTemp = pHead; while(pTemp->next!=NULL) pTemp = pTemp->next; pTemp->next = pNew; pNew->next = NULL; } }
/*THIS FUNCTION DELETES A NODE */ void delete (int key, struct node *pHead) { struct node *pTemp, *pPre; pTemp=pHead; if (pHead == NULL) printf( Empty linked list\n);; else // Search node { pTemp = pHead; while (pTemp!=NULL) { if(pTemp->data == key) break; else { pPre = pTemp pTemp = pTemp->next; } }
39
if( pTemp==NULL) printf(Node not found\n); else { if ( pTemp == pHead ) pHead = pTemp->next; else pPre->next = pTemp->next; free(pTemp) printf(Node deleted\n ); } } } /* ADD A NEW NODE AFTER A SPECIFIED NO OF NODES */ insert_after(int num, int loc) { int i; struct node *pNew,*pPre,*pTur; pTemp=phead; /* here pTemp stores the first location */ if(loc > count()+1 || loc <= 0) { printf(" insertion is not possible : "); return; } if (loc == 1) /* if list is null then add at beginning */ { insert_begin(num); return; } else { for(i=1;i<loc;i++) { ppre=pcur; /* ppre will be holding previous value */ pcur=pcur->next; } pnew=(struct node *)malloc(sizeof(struct node)); pnew->data=num; pnew->next=ppre->next ppre->next=pnew; return; } }
40
// ADD A NEW NODE AFTER A SPECIFIED node value */ void insert_byValue(int val) { struct node *pNew, *pPre, *pTemp; int key; printf(Enter the value after which node to be inserted); scanf(%d, &key); // Allocate memory for the new node pNew = (struct node *)malloc(sizeof(struct node)); pNew->data = val;
if (pHead == NULL) {
pHead = pNew; pHead->next= NULL; } else { pTemp = pHead; /* Searching insertion point*/ while(pTemp!=NULL) { if(pTemp->data == key) pNew->next = pTemp->next; pTemp->next = pNew else { pPre = pTemp; pTemp = pTemp->next; } } if( pTemp==NULL) printf( The insertion point cant be found\n); else { if ( pTemp==pHead) // inserted before head node { pNew->next = pTemp; pTemp->next = pNew; } else // insert as intermediate or last node { pNew->next = pTemp->next; pTemp->next = pNew;
41 } } } }
/* THIS FUNCTION DISPLAYS THE CONTENTS OF THE LINKED LIST */ void display( ) { struct node *pTemp; pTemp=pHead; if(pTemp ==NULL) { printf("NO ELEMENT IN THE LIST :"); return; } else /* traverse the entire linked list */ while(pTemp!=NULL) { printf("%d \n",r->data); pTemp = pTemp ->next; } }
//THIS FUNCTION COUNTS THE NUMBER OF ELEMENTS IN THE LIST void count() { struct node *pTemp; int c=0; *pTemp=phead; while(pTemp!=NULL) { pTemp = pTemp ->next; c++; } return(c); }
Application of single linked list: Program to add two polynomials: e.g (6X2+3X+1) + (5X3+5X+3) = 5X3+6X2+8X+4 #include<stdio.h> #include<alloc.h>
42 #include<conio.h> struct node { int coeff; int pow; struct node *next; }; struct node *poly1=NULL,*poly2=NULL,*poly=NULL; void create(struct node *n) { char ch; do { printf("\n enter coeff:"); scanf("%d",&node->coeff); printf("\n enter power:"); scanf("%d",&node->pow); node->next=(struct link*)malloc(sizeof(struct link)); node=node->next; node->next=NULL; printf("\n continue(y/n):"); ch=getch(); } while(ch=='y' || ch=='Y'); } void show(struct link *n) { while(n->next!=NULL) { printf("%dx^%d",n->coeff,n->pow); n=n->next; if(n->next!=NULL) printf("+"); } }
void polyadd(struct node *poly1,struct node *poly2,struct node *poly) { while(poly1->next && poly2->next) { if(poly1->pow>poly2->pow) { poly->pow=poly1->pow; poly->coeff=poly1->coeff; poly1=poly1->next;
43 } else if(poly1->pow<poly2->pow) { poly->pow=poly2->pow; poly->coeff=poly2->coeff; poly2=poly2->next; } else { poly->pow=poly1->pow; poly->coeff=poly1->coeff+poly2->coeff; poly1=poly1->next; poly2=poly2->next; } poly->next=(struct node *)malloc(sizeof(struct node)); poly=poly->next; poly->next=NULL; } while(poly1->next || poly2->next) { if(poly1->next) { poly->pow=poly1->pow; poly->coeff=poly1->coeff; poly1=poly1->next; } if(poly2->next) { poly->pow=poly2->pow; poly->coeff=poly2->coeff; poly2=poly2->next; } poly->next=(struct node *)malloc(sizeof(struct node)); poly=poly->next; poly->next=NULL; } } main() { char ch; poly1=(struct link *)malloc(sizeof(struct link)); poly2=(struct link *)malloc(sizeof(struct link)); poly=(struct link *)malloc(sizeof(struct link)); printf("\nenter 1st number:");
44 create(poly1); printf("\nenter 2nd number:"); create(poly2); printf("\n1st Number:"); show(poly1); printf("\n2nd Number:"); show(poly2); polyadd(poly1,poly2,poly); printf("\nAdded polynomial:"); show(poly); getch(); }
45 CIRCULAR LINKED LIST: The circular linked list is similar to single linked list except that the last nodes next pointer points to the first node. In a circularly linked list, all nodes are linked in a continuous circle, without using null.
Various operations that can be performed with circular linked list are Creation of circular linked list Insertion of a node in circular linked list Deletion of any node from the list Display of circular linked list //Circular Linked List in C #include<stdio.h> #include<conio.h> #include<stdlib.h> #include<alloc.h> #define NULL 0 struct node { int data; struct node *next; }; struct node *pHead; void main() { int ch,n,m,key,i,cou pHead = NULL; while(1) { printf(" 1. CREATE A LIST \n); printf(" 2.INSERT A NUMBER AT BEGINNING;\n"); printf(" 3.INSERT A NUMBER AT LAST:\n"); printf(" 4.INSERT A NUMBER AFTER A PARTICULAR VALUE IN LIST:\n"); printf(" 5.DELETE A NODE IN THE LINKED LIST:\n"); printf(" 6.PRINT THE ELEMENTS IN THE LIST :\n"); printf(" 7.PRINT THE NUMBER OF ELEMENTS IN THE LIST\n"); printf(" 8.GET OUT OF LINKED LIST:\n"); printf(" PLEASE, ENTER THE NUMBER:\n"); scanf("%d",&ch); switch(ch)
46 { case 1: printf("enter no of items"); scanf("%d",&n); for(i=0;i<n;i++) { printf("enter the element"); scanf("%d",&m); create(m); } break; case 2: printf("enter the element"); scanf("%d",&m); insert_begin(m); break; case 3: printf("enter the element"); scanf("%d",&m); insert_last(m); break; case 4: printf("enter the element"); scanf("%d",&m); printf("enter the value after which you want to insert"); scanf("%d",&key); insert_after(m,key); break; case 5: printf("Enter the element to delete"); scanf("%d",&m); delete(m); break; case 6: display(); break; case 7: cou=count(); printf("No. of elements in the list%d ", cou); break; case 8: exit(0); break; default: printf("wrong choice"); } } } // Function to create circular list
47 create(int data) { struct node *pNew,*pTemp; pNew =(struct node *)malloc(sizeof(struct node)); pNew ->data=data; pNew ->next=NULL; if(pHead == NULL) { pHead = pNew; pNew ->next= pHead; } else { pTemp = pHead; while(pTemp->next!=pHead) pTemp = pTemp->next; pTemp->next = pNew; pNew->next = pHead; } return; } /* ADD A NEW NODE AT BEGINNING */ insert_begin(int val) { struct node *pNew, *pTemp; pNew = (struct node *)malloc(sizeof(struct node)); pNew->data = val; if (pHead == NULL) { /*Adding to empty list*/ pHead = pNew; pHead->next= pNew; printf("Node inserted\n"); } else { /* Adding as first node*/ pTemp = pHead; while(pTemp->next!=pHead) pTemp = pTemp->next; pTemp->next = pNew; pNew->next = pHead; pHead = pNew; printf("Node inserted\n"); } return; }
48 /*THIS FUNCTION ADDS A NODE AT THE LAST OF LINKED LIST */ insert_last(int val) { struct node *pNew, *pTemp; pNew = (struct node *)malloc(sizeof(struct node)); pNew->data = val; if (pHead == NULL) { /*Adding to empty list*/ pHead = pNew; pHead->next= pHead; } else { /* Adding as last node*/ pTemp = pHead; while(pTemp->next!=pHead) pTemp = pTemp->next; pTemp->next = pNew; pNew->next = pHead; printf(Node inserted\n); } return; } /* ADD A NEW NODE AFTER A SPECIFIED VALUE IN THE LIST */ insert_after(int val, int key) { struct node *pNew, *pPre, *pTemp; // Allocate memory for the new node pNew = (struct node *)malloc(sizeof(struct node)); pNew->data = val; if(pHead == NULL) /*Adding to empty list*/ { pHead = pNew; pHead->next= pNew; } else { pTemp = pHead; /* Searching insertion point*/ do { if(pTemp->data == key) { pNew->next = pTemp->next; pTemp->next = pNew; printf("node inserted"); return;
49 } else { pTemp = pTemp->next; } } while(pTemp!=pHead); if( pTemp==pHead) printf(" The insertion point cant be found\n); } return; } /* THIS FUNCTION DISPLAYS THE CONTENTS OF THE LINKED LIST */ display( ) { struct node *pTemp; pTemp=pHead; if(pTemp ==NULL) { printf("NO ELEMENT IN THE LIST :"); return; } else /* traverse the entire linked list */ do { printf("%d \n", pTemp ->data); pTemp = pTemp ->next; } while(pTemp!=pHead); return; } //THIS FUNCTION COUNTS THE NUMBER OF ELEMENTS IN THE LIST int count() { struct node *pTemp; int c=1; pTemp=pHead; if (pHead ==NULL) return 0; while(pTemp->next!= pHead) { pTemp = pTemp ->next; c++; } return(c); }
50
/*THIS FUNCTION DELETES A NODE */ delete(int key) { struct node *pNew, *pPre, *pTemp; if (pHead == NULL) /*DELETION in empty list*/ { printf(" Empty linked list \n); } else { pTemp = pHead; /* Searching DELETION point*/ do { if(pTemp->data == key) { if(pTemp==pHead && pTemp->next==pHead) { free(pTemp); /* deleting only node existing in the link pHead=NULL; printf(node deleted \n"); return; } else if(pTemp==pHead && pTemp->next!=pHead) { while(pTemp->next != pHead) /*delete head node pTemp = pTemp->next; pTemp->next = pHead->next; free(pHead); pHead = pTemp->next; printf("head node deleted \n"); return; } else { pPre->next = pTemp->next; free(pTemp); printf("node deleted \n"); return; } } else { pPre = pTemp; pTemp = pTemp->next; } } while(pTemp!=pHead);
52 Application of Circular Linked List: Josephus' Problem: This algorithm is named for a historian of the first century, Flavius Josephus, who survived the JewishRoman war due to his mathematical talents. Legend has it that he was one out of 41 Jewish rebels trapped by the Romans. His companions preferred suicide to escape, so they decided to form a cycle and to kill every third person and to proceed around the circle until no one was left. Josephus wasn't excited by the idea of killing himself, so he calculated where he has to stand to survive the vicious circle. Algorithm: There are n people standing in a circle waiting to be executed. After the first man is executed, k1 people are skipped and the k-th man is executed. Then again, k1 people are skipped and the k-th man is executed. The elimination proceeds around the circle (which is becoming smaller and smaller as the executed people are removed), until only the last man remains, who is given freedom. In Euclidean geometry, a circle is the set of all points in a plane at a fixed distance, called the radius, from a fixed point, called the centre. ... The task is to choose the place in the initial circle so that you survive (remain the last one), given n and k. For skip value k = 4, n=8 1 8 3 7 4 6 5 3 7 6 5 Delete(4) 1 7 6 Delete(5) 2 3 7 6 Delete(2) 1 3 3 7 6 delete(1) 7 6 5 delete(8) 3 2 1 2
1 8
53 Program to implement Josophus Problem: #include <stdio.h> #include<stdlib.h> #define NULL 0 struct node { int soilder; struct node *next; }; struct node *head, *current; int tot_soilders; main() { int ch,n; head = NULL; while(1) { printf("\n1. soilder list creation"); printf("\n2. Display soilder list"); printf("\n3. Sucide"); printf("\n0. Exit"); scanf("%d",&ch); switch(ch) { case 1: printf("\nEnter the total no. of soilders"); scanf("%d",&n); create_list(n); break; case 2: display(); getch(); break; case 3: if (tot_soilders <= 1) printf("There Should Be Atleast 2 Soilders in the List"); else { printf("\nEnter the no by which sucide is to be commited"); scanf("%d",&n); if ( n < 1 ) printf("\nInvalid Number!"); else printf("\nThe only Soilder left after sucide session is %d ",left_after_sucide(n)); }
54 getch(); break; case 0: return; default : printf("\nINVALID CHOICE"); getch(); } } } create_list(int data) { struct node *last,*New; int i; head=NULL; for(i=1;i<=data;i++) { New=(struct node *)malloc(sizeof(struct node)); New->soilder=i; New->next=NULL; tot_soilders=tot_soilders+1; if(head==NULL) { head=New; last=New; New->next=head; } else { New->next=last->next; last->next=New; last = New; } } } display() { if (head == NULL) { printf("\nNo Soilders in the Queue"); return; } printf("%d",head->soilder); printf("%c ",2); current = head->next; while( current != head) { printf("%d ",current->soilder);
55 current = current->next; } return; } int left_after_sucide(int by_n) { int i=1,j,dead_sol; struct node *save; current = head; for(i=1; i<tot_soilders; i++) { for (j=1;j< by_n;j++) { save = current; current = current->next; } save->next = current->next; if (current == head) { head = current->next; } dead_sol = current->soilder; free(current); display(); printf("\n\n%d%c is Dead \n",dead_sol,1); current = save->next; } head = current; display(); tot_soilders = 1; return(head->soilder); }
56 Doubly Linked Lists Doubly Linked List (DLL) is a type of Linked List in which each data item points to the next and also to the previous data item. This "linking" is accomplished by keeping two address variables (pointers) together with each data item. These pointers are used to store the address of the previous and the address of the next data items in the list. The structure that is used to store one element of a linked list is called a node like in Linked Lists. A node has three parts: data, previous address and next address. The data part contains the necessary information about the items of the list, the previous address part contains the address of the previous node, the next address part contains the address of the next node.
57 printf("\n 2 Forward Traverse"); printf("\n 3 Insert after the given number"); printf("\n 4 Insert before the given number"); printf("\n 5 Insert By Position "); printf("\n 6 Delete By Value "); printf("\n 7 Delete by Position"); printf("\n 0 EXIT"); printf("\n Enter your choice"); scanf("%d",&ch); switch(ch) { case 1: Append(); break; case 2: ftraverse(); break; case 3: insertafter(); break; case 4: insertbefore(); break; case 5: insertbypos(); break; case 6: delByValue(); break; case 7: delByPos(); break; case 0: break; default: printf("\n Invalid Choice"); } }while(ch!=0); getch(); } // Appending a new node void Append() { struct node *pNew;
58 pNew =(struct node*)malloc(sizeof(struct node)); printf("\nEnter no"); scanf("%d",& pNew ->no); pNew ->next=NULL; if(head==NULL) { head= pNew; tail= pNew; head->pre=NULL; return; } tail->next= pNew; pNew ->pre=tail; tail= pNew; } //Displaying the contents of the list void ftraverse() { struct node *p=head; if(head==NULL) { printf("\n Empty Link List"); return; } while(p!=NULL) { printf(" %d",p->no); p=p->next; } } // Inserting a node after a specified number in the node // Insert by number void insertafter() { int val; struct node *p =head,* pNew; if(head==NULL) { printf("\nEmpty LINK lIST"); return; } printf("\n Enter number after which you want to INSERT"); scanf("%d",&val); if(val==tail->no)
59 { pNew =(struct node*)malloc(sizeof(struct node)); printf("\nENTER number"); scanf("%d",& pNew ->no); tail->next= pNew; pNew ->pre=tail; tail= pNew; tail->next=NULL; printf("\n NODE INSERTED"); return; } while(p->next!=NULL) { if(val==p->no) { pNew =(struct node*)malloc(sizeof(struct node)); printf("\nEnter number"); scanf("%d",& pNew ->no); pNew ->next=p->next; pNew ->pre=p; p->next= pNew; pNew ->next->pre= pNew; printf("\n Node Inserted"); return; } p=p->next; } printf("\n%d Number Not found",val); return; }
// Insert a node before a specified number void insertbefore() { int val; struct node *p =head,* pNew; if(head==NULL) { printf("\nEmpty LINK lIST"); return; } printf("\n Enter number BEFORE which you want to INSERT"); scanf("%d",&val); if(val==head->no)
60 { pNew =(struct node*)malloc(sizeof(struct node)); printf("\nENTER number"); scanf("%d",& pNew ->no); head->pre= pNew; pNew ->next=head; head= pNew; head->pre=NULL; printf("\n NODE INSErted"); return; } while(p!=NULL) { if(val==p->no) { pNew =(struct node*)malloc(sizeof(struct node)); printf("\nENTER number"); scanf("%d",& pNew ->no); pNew ->pre=p->pre; pNew ->next=p; pNew ->pre= pNew; pNew ->pre->next= pNew; printf("\n Node inserted"); return; } p=p->next; } printf("\n%d Number not found",val); return; }
// Insert a new node after a specified position in the list void insertbypos() { int val; struct node *p =head,* pNew; int non=0,i,pos; if(head==NULL) { printf("\nEmpty LINK lIST"); return; }
61 scanf("%d",&pos); while(p!=NULL) { non++; p=p->next; } if (pos<1&&pos>non+1) { printf("\n Invalid Position"); return; } if(pos==1) { pNew =(struct node*)malloc(sizeof(struct node)); printf("\nENTER number"); scanf("%d",& pNew ->no); head->pre= pNew; pNew ->next=head; head= pNew; head->pre=NULL; printf("\n Node inserted"); return; } if(pos==non+1) { pNew =(struct node*)malloc(sizeof(struct node)); printf("\nEnter number"); scanf("%d",& pNew ->no); tail->next=n; pNew ->pre=tail; tail= pNew; tail->next=NULL; printf("\n Node inserted"); return; } p=head; for(i=1;i<pos-1;i++) { p=p->next; } pNew =(struct node*)malloc(sizeof(struct node)); printf("\nENTER number"); scanf("%d",& pNew ->no); pNew ->next=p->next;
62 pNew ->pre=p; p->next= pNew; pNew ->next->pre= pNew; printf("\n NODE Inserted"); return; }
// Delete a node with specified number in the node void delByValue() { int val; struct node *p =head; int i,pos; if(head==NULL) { printf("\nEmpty LINK lIST"); return; }
printf("\nEnter the number to be deleted"); scanf("%d",&val); if(head==tail&&val==head->no) { free(p); head=tail=NULL; printf("\nNode Deleted"); return; } else if (val==head->no) { head=head->next; head->pre=NULL; free(p); printf("\nNode Deleted"); return; } else if(val==tail->no) { p=tail; tail=tail->pre; tail->next=NULL; free(p); printf("\nNode Deleted"); return;
63 } while(p->next!=NULL) { if(val==p->no) { p->pre->next=p->next; p->next->pre=p->pre; free(p); printf("\nNode Deleted"); } p=p->next; } printf("\n Node not Found") ; return; } // Delete a node by position void delByPos() { struct node *p =head; int non=0,i,pos; if(head==NULL) { printf("\nEmpty LINK lIST"); return; } printf("\nEnter the POSITION which is to be deleted"); scanf("%d",&pos); while(p!=NULL) { non++; p=p->next; } p=head; if(pos<1||pos>non); { printf("\n INvalid Position"); } if(head==tail&&pos==1) { free(p); head=tail=NULL; printf("\nNODE Deleted"); return;
64 } else if (pos==1) { head=head->next; head->pre=NULL; free(p); printf("\nNode DELeted"); return; } else if(pos==non) { p=tail; tail=tail->pre; tail->next=NULL; free(p); printf("\nNode DELeted"); return; } for(i=0;i<pos-1;i++) { p=p->next; } p->pre->next=p->next; p->next->pre=p->pre; free(p); printf("\nNode Deleted"); return; } Application : Palindrome checking using doubly linked list
Linked List problems: 1. Implement stack using linked list 2. Implement queue using linked list
65
TREES
Tree definitions A tree is a multi-linked data structure consisting of nodes with pointers to two or more other nodes. The connecting pointers between nodes are typically called tree edges. A tree has a distinguished node called its root to which no other node points. Recursively, a tree can be defined as empty, or a root node with edges to zero or more subtrees.
Tree views There are a number of ways to depict trees in graphic form. The nodes are represented as labeled circles and edges are connecting lines; e.g.,
66 Terminology 1. Root: The topmost node of the tree is the root. (or) A node that doesnt have a parent is a root node. 2. Leaf: A node that doesnt have children is called leaf node. 3. Siblings: Nodes with a common parent are called siblings. 4. An upper node in the tree is the parent of the nodes immediately below it, and those lower nodes are the children. 5. A node is the grandparent of nodes two levels below, etc. 6. Path: A path is a sequence of nodes connected by edges. 7. Height: The height of a node is the length of the longest path from the node to a leaf. 8. Depth: The depth of a node is the length of the path from the root to that node This terminology is illustrated in Figure 1.
Figure 1: Tree terminology. The following are important tree facts. 1. 2. 3. Every node except the root has exactly one parent. The root has no parent. There is a unique path from the root to any node. In tree examples above, each node had two children; such trees are called binary. In general, a tree node may have any number of children, from 0 to n; such trees are called n-ary. Figure 2 is an example of an n-ary tree,
67
Examples of tree-structured hierarchies: 1. Directory Hierarchies: In computers, files are stored in directories that form a tree. The top level directory represents the root. It has many subdirectories and files. The subdirectories would have further set of subdirectories. 2. Organization charts: In a company a number of vice presidents report to a president. Each VP would have a set of general managers, each GM having his/her own set of specific managers and so on. 3. Biological classifications: Starting from living being at the root, such a tree can branch off to mammals, birds, marine life etc. 4. Game Trees: All games which require only mental effort would always have number of possible options at any position of the game. For each position, there would be number of counter moves. The repetitive pattern results in what is known a game tree.
Binary Trees Definition: A binary tree is a tree in which each node can have maximum two children. Thus each node can have no child, one child or two children. The pointers help us to identify whether it is a left child or a right child. Examples of binary trees:
68
The level of a node in a binary tree: The root of the tree has level 0 The level of any other node in the tree is one more than the level of its parent.
Number of nodes in each level are Level 0 : 1 node Level 1 : 2 nodes Level 2 : 4 nodes Level 3 : 8 nodes
69
Total number of nodes for this tree = 15 Height of the root node = 3 n = 23+1 1 = 15 In general, n = 2h+1 1 ( maximum) Maximum Number of nodes in a tree with height h is n = 2h+1 1 Height of a tree with n nodes : h = log ( n+1) - 1 Implementation A binary tree has a natural implementation in linked storage. A tree is referenced with pointer to its root. Recursive definition of a binary tree: A binary tree is either - Empty, or - A node (called root) together with two binary trees (called left subtree and the right subtree of the root) Each node of a binary tree has both left and right sub trees which can be reached with pointers Structure of a binary tree struct tree_node { int data; struct tree_node *left_child; struct tree_node *right_child; };
Tree traversal: Tree traversal is the process of visiting each node in the tree exactly once. Types of tree traversal: ( Depth first tree traversing) 1. Preoder traversal: ( Root Visit the root node. Left Right )
70 Traverse the left subtree. Traverse the right subtree. 2. Inorder traversal: ( Left Root Right ) Traverse the left subtree. Visit the root node. Traverse the right subtree. 3. Postorder traversal: ( Left Right Root ) Traverse the left subtree. Traverse the right subtree. Visit the root node. 4. Level order traversal: (Breadth First Tree Traversal ) Traverse the nodes from level 0 to level n E.g Suppose there are only 3 nodes in the tree having the following arrangement:
Algorithm for Preorder traversal of a binary tree: If the root node of the tree is null, the traversal is done. Otherwise, visit the root node. Then recursively traverse the left subtree. i-e If there is a left child we visit the left subtree (all the nodes) in pre-order fashion starting with that left child . Then recursively traverse the right subtree. i-e If there is a right child then we visit the right subtree in pre-order fashion starting with that right child.
71 preorder(p->right_child); } } Example:
Preorder Traversal : a b c d f g e a(root) b(left) c d f g e (right) In-order traversal of a binary tree If the root node of the tree is null, the traversal is done. Traverse the left subtree recursively. i-e If there is a left child we visit the left subtree (all the nodes) in in-order fashion. Visit the root node. Then recursively traverse the right subtree. i-e If there is a right child then we visit the right subtree in in-order fashion.
72 Inorder: bafdgce b(left) a(root) f d g c e(right) Algorithm for Post order traversal of a binary tree: If the root node of the tree is null, the traversal is done. Recursively traverse the left subtree. i-e If there is a left child we visit the left subtree (all the nodes) in pre-order fashion starting with that left child . Then recursively traverse the right subtree. i-e If there is a right child then we visit the right subtree in pre-order fashion starting with that right child. At last visit the root node.
Post order : b f g d e c a b (left) f g d e c (Right) a ( Root ) Level order: abcdefg a (level 0) b c (level 1) d e (level 2) f g (level 3)
Finding sum of the values of all the nodes of a tree: To find the sum, add to the value of the current node, the sum of values of all nodes of
73 left subtree and the sum of values of all nodes in right subtree. int sum(struct tree_node *p) { if ( p!= NULL) return(p->data + sum(p->left_child)+ sum(p->right_child)); else return 0; }
Expression Trees
Expression tree for (a + b * c) + ((d * e + f ) * g) The above figure shows an example of an expression tree . The leaves of an expression tree are operands , such as constants or variable names, and the other nodes contain operators . This particular tree is a binary tree, because all of the operations are binary, and although this is the simplest case, it is possible for nodes to have more than two children. It is also possible for a node to have only one child, as is the case with the unary minus operator. We can evaluate an expression tree, T, by applying the operator at the root to the values obtained by recursively evaluating the left and right subtrees. In our example, the left subtree evaluates to a + (b * c) and the right subtree evaluates to ((d *e ) + f )*g. The entire tree therefore represents (a + (b*c)) + (((d * e ) + f)* g).
74
1. The first two symbols are operands, so we create one-node trees and push pointers to them onto a stack.* *For convenience, we will have the stack grow from left to right in the diagrams.
2. Next, a '+' is read, so two pointers to trees are popped, a new tree is formed, and a pointer to it is pushed ontothe stack.*
3. Next, c, d, and e are read, and for each a one-node tree is created and a pointer to the corresponding tree is pushed onto the stack.
Continuing, a '*' is read, so we pop two tree pointers and form a new tree with a '*' as root.
75
Finally, the last symbol is read, two trees are merged, and a pointer to the final tree is left on the stack.
76
The left subtree of a node contains only nodes with keys less than the node's key. The right subtree of a node contains only nodes with keys greater than the node's key. Both the left and right subtrees must also be binary search trees. Left subtree < Root < Right subtree
E.g binary search tree The most appealing property of a binary search tree is that searching for a particular node can be performed using the following algorithm: