Data structures

23,146 views

Published on

8 Comments
37 Likes
Statistics
Notes
  • I am a direct Mandate to a genuinely renowned Investment Finance Company offering Cash & Asset Backed Financial Instruments on Lease and Sale at the best rates and with the most feasible procedures. Instruments offered can be put in all forms of trade and can be monetized or discounted for direct funding. GENUINE BANK GUARANTEE (BG) AND STANDBY LETTER OF CREDIT (SBLC) FOR LEASE AND SALE AT THE LOWEST RATES AVAILABLE. OTHER FINANCIAL INSTRUMENTS SUCH AS MTN, CD, DLC, PB ARE ALSO AVAILABLE. We offer certifiable and verifiable bank instruments via Swift Transmission from a genuine provider capable of taking up time bound transactions. Kindly contact for genuine inquiries and I can provider you with the needed information. With Regards, Bharath Kumar Vasu Financial Mandate on Bank Instruments Email:bharathkvasufinservices@gmail.com Emai:bharathkvasu@yahoo.com Skype: bharath.kvasu.finservices Bharath Kumar Vasu Financial Mandate on Bank Instruments Private Banking and Client Services Email : bharathkvasufinservices@gmail.com Skype : bharath.kvasu.finservices London - Manchester - Essex - Southampton - England - United Kingdom BROKERS ARE WELCOME & 100% PROTECTED!!
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Hello dear, My name is mariam nasrin, I know that this email will meet you in a good health and also surprisingly but God has his own way of bringing people together. Nice to Meet you I would appreciate if you can reply me back( mariamnasrin2@gmail.com ) So that i can explain you more about me. thank Yours mariam.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • All slides are so good and very helpful ,, I like it ~!
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • good and useful..
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • thank you.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total views
23,146
On SlideShare
0
From Embeds
0
Number of Embeds
75
Actions
Shares
0
Downloads
2,523
Comments
8
Likes
37
Embeds 0
No embeds

No notes for slide
  • Dynamic memory allocation and de-allocation of memory is therefore, the responsibility of the programmer. The malloc( ) function allocates a block of memory on heap, and returns the starting offset of the block of memory allocated on the heap. This can be stored in a pointer declared as part of the stack, and access to the heap variable can be had using this pointer. When returning from the function, care must be taken by the programmer to return the storage occupied by the heap variable back to the heap. This is done using the function free( ) to which the pointer pointing to the heap variable is passed as an argument. The free( ) function de-allocates the block of memory on the heap used by the heap variable, and returns it back to the heap. If the call to the function free( ) is not issued before returning from the function, the pointer (stack variable) pointing to the heap variable will be destroyed when returning from the function, and therefore, the address to the heap variable will be lost. There will then be no way to de-allocate this block of memory on the heap, and return it back to the heap. This amount of memory will not be available for future use by other applications. This is what is referred to as a memory leak.
  • The size of the stack cannot grow to accommodate these runtime variables as the stack size is determined at the time of compilation. Therefore, dynamically created variables are accommodated into another area of memory called the heap or free store.
  • One way of determining the size of the structure marks_data and pass it as an argument to malloc() is to determine the size of the element name (11 bytes) and marks (2 bytes or 4 bytes) depending on the platform. Therefore, the size argument to be passed to malloc() can be 13 bytes or 15 bytes. Rather than count the number of bytes constituting the structure marks_data that is platform dependent, a better way should be for the program to determine the size of the elements constituting the structure marks_data based on the platform. This is where the sizeof( ) operator comes into the picture. What we had requested malloc() in the aforesaid example is a block on the Heap representing the structure marks_data and accordingly this structure on the heap was allocated.
  • In diagram (a), 10 is the current top element of the stack. If we add any element in the stack, it will be placed on top of 10, and if we delete an element, it will be 10 that is on top of the stack.
  • If a stack is empty, and it contains no element, it is not possible to pop the stack. Therefore, before popping an element, you must ensure that the stack is not empty. As the definition of a stack does not presuppose the number of elements in it, there is no upper limit on the number of elements in the stack, memory constraints notwithstanding.
  • Therefore, implementing a stack as an array prohibits the growth of the stack beyond the finite number of elements that the declared array can contain.
  • To understand queues in a better way, consider a queue containing three elements as shown if figure (a). If you want to add element 4 to this queue, you can do so only at the rear end of the queue as shown in figure (b). Now, if you remove an element from this queue, it should be element 1 that is to be deleted because this is the element that has come first in the queue and is at the front. After deleting 1, element 2 will be at the front as shown in figure (c).
  • You can insert a new node at the end of the list after the last node that is pointed to by pointer rear. You must take care when you insert an element into an empty queue as in this case you need to adjust the front pointer as well to point to this element.
  • Whenever you remove an element from a queue, you must ensure that the queue is not empty. If you are deleting the last element, you must ensure that q->rear = null to indicate that the queue is now empty.
  • Point 4: If we used the statement r= r+1; the value of r would be 5, but because it is a circular queue, the value of r should be 0. This can be achieved by using the statement:- r = (r+1)%QUEUE_SIZE;
  • The first node at address 100 (fictitious memory addresses) is referenced by a pointer called start (declared on the stack) . Once you are able to access the first node through start , you would be able to access all subsequent nodes through next . The pointer next of the last node points to no node in particular, and therefore contains the value NULL .
  • above For example, we wish to use a data structure to represent a person, and all of his or her descendants. Assume that the person’s name is Rajeev and that he has three children, Ravi, Vijay and Anjali. Ravi has three children Ramesh, Suresh and Sukesh. And Vijay has Arvind.
  • A search in such lopsided trees would degenerate into a linear search, and such a tree bears the functionality of a linked list. Such lopsided trees are not efficient from the perspective of search efficiency
  • You should also note that this type of deletion maintains the binary search tree but increases the height of the tree. Thus, it increases the time required for a search. Hence, to optimize searching through a binary search tree, you need methods that make the left and the right subtrees as balanced as possible.
  • In the calling routine, you can check whether the returned value of p is null or not. If it is null, the calling routine can give a message that the searched value is not there in the tree. The maximum number of comparisons in a binary search tree is equal to the depth of the tree whereas in the case of a linear linked list, the maximum number of comparisons is equal to the number of nodes in the list.
  • Consider the worst-case scenario of searching for the value 7 in the linked list. This would entail starting with the first node in the list and progressing linearly through the subsequent nodes till one comes to the value being searched for, which is 7. This would involve a maximum of 7 searches.
  • Consider the scenario of searching for the same value 7 in a balanced binary search tree. The search begins at the root node. Since the value being searched for is greater than the value in the root node, you need to search from the top of the right subtree to locate 7. Since the value being searched is also greater than the value at the top of the right subtree, you move to the top of the next right subtree of the current subtree where the search value 7 is located.
  • Data structures

    1. 1. DATA STRUCTURES AND ALGORITHMS
    2. 2. Day One Objectives <ul><li>Introduction to Data Structures </li></ul><ul><li>Introduction to Abstract Data Types </li></ul><ul><ul><li>Need for Data Structures, Data Types, Data Abstraction </li></ul></ul><ul><ul><li>Array and Structure Overview </li></ul></ul><ul><li>Linear Data Structures </li></ul><ul><ul><li>Stacks </li></ul></ul><ul><ul><li>Queues </li></ul></ul><ul><ul><li>Linked lists </li></ul></ul><ul><li>Non – Linear Data Structure </li></ul><ul><ul><li>Trees </li></ul></ul><ul><ul><li>Graphs </li></ul></ul><ul><li>Sorting and Searching </li></ul>
    3. 3. Introduction to Data Structures <ul><li>Data is a set of elementary items. </li></ul><ul><li>“ The data structures deal with the study of how the data is organized in the memory, how efficiently it can be retrieved and manipulated and the possible ways in which different data items are logically related”. </li></ul><ul><li>They can be classified in to </li></ul><ul><ul><li>Primitive data structures </li></ul></ul><ul><ul><li>Non primitive data structure . </li></ul></ul>
    4. 4. Introduction to Data Structures <ul><li>Primitive data structure : </li></ul><ul><ul><li>These are data structures that can be manipulated directly by machine instructions. </li></ul></ul><ul><ul><li>In C language, the different primitive data structures are int, float, char, double. </li></ul></ul><ul><li>Non primitive data structures : </li></ul><ul><ul><li>These are data structures that can not be manipulated directly by machine instructions. Arrays, linked lists, files etc., are some of non-primitive data structures and are classified into linear data structures and non-linear data structures . </li></ul></ul>
    5. 5. Introduction to Abstract Data Types <ul><li>An abstract data type is a data structure and a collection of functions or procedures which operate on the data structure </li></ul><ul><li>An Example: Collections </li></ul><ul><ul><li>Programs often deal with collections of items. These collections may be organized in many ways and use many different program structures to represent them, yet, from an abstract point of view, there will be a few common operations on any collection. These might include: </li></ul></ul>create Create a new collection add Add an item to a collection delete Delete an item from a collection find Find an item matching some criterion in the collection destroy Destroy the collection
    6. 6. Lifetime Of A Variable <ul><li>is a run-time concept </li></ul><ul><li>period of time during which a variable has memory space associated with it </li></ul><ul><ul><li>begins when space is allocated </li></ul></ul><ul><ul><li>ends when space is de-allocated </li></ul></ul><ul><li>three categories of &quot;lifetime&quot; </li></ul><ul><ul><li>static - start to end of program execution </li></ul></ul><ul><ul><li>automatic (stack) - start to end of declaring function's execution </li></ul></ul><ul><ul><li>heap (variable declared dynamic at runtime, and also de-allocated dynamically at runtime. </li></ul></ul>
    7. 7. Data Memory Model run-time stack - activation records added and removed as program runs (expands and shrinks in an orderly LIFO manner) space for global variables space for variables allocated at run-time (allocation and de-allocation requests occur in unpredictable order) static data automatic data heap data
    8. 8. Heap Variables <ul><li>Space for heap variables is allocated from an area of runtime memory known as the heap or free store. </li></ul><ul><li>Heap variables do not have an explicit name, and are accessed indirectly via a pointer. </li></ul><ul><li>Memory space for heap variables is explicitly allocated at runtime using malloc( ) . </li></ul><ul><li>Space occupied by heap variables must be explicitly returned back to the heap to avoid memory leaks. This is done using the free( ) function. </li></ul>
    9. 9. Array Usage – A Perspective <ul><li>Consider the following example: </li></ul><ul><li>#include<stdio.h> </li></ul><ul><li>main( ) </li></ul><ul><li>{ </li></ul><ul><li>int num_array[50],i; </li></ul><ul><li>for( i=0; i < 50; i++ ) </li></ul><ul><li>{ </li></ul><ul><li>num_array[i]=0; </li></ul><ul><li>scanf( &quot;%d&quot;,&num_array[i] ); </li></ul><ul><li>fflush(stdin); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
    10. 10. Array Usage – A Perspective <ul><li>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. </li></ul><ul><li>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. </li></ul><ul><li>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. </li></ul>
    11. 11. Array Usage – A Perspective <ul><li>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. </li></ul><ul><li>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. </li></ul>
    12. 12. Array Usage – A Perspective <ul><li>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. </li></ul><ul><li>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. </li></ul><ul><li>Let us begin by understanding the concept of lifetime of variables. </li></ul>
    13. 13. Dynamic Data Structures <ul><li>A structure in C is a collection of items, each of which has its own identifier. </li></ul><ul><li>Each item is called a member of the structure. </li></ul><ul><li>To access the members of a structure, we need to create objects or instances of the structure. </li></ul><ul><li>A structure is declared as: </li></ul><ul><ul><li>struct <structure name> ex: struct employee { </li></ul></ul><ul><ul><li>{ char name[50]; </li></ul></ul><ul><ul><li>members; char designation[25]; </li></ul></ul><ul><ul><li>}object; float salary; </li></ul></ul><ul><ul><li> } emp; </li></ul></ul>
    14. 14. Dynamic Data Structures <ul><li>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. </li></ul><ul><li>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. </li></ul>
    15. 15. Dynamic Data Structures <ul><li>When the program begins execution, it will need to create variables of the structure type. Therefore, the language must support runtime declaration of variables. </li></ul><ul><li>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. </li></ul><ul><li>The C language provides the malloc() function which a program can use to declare variables dynamically. </li></ul>
    16. 16. The malloc( ) Function <ul><li>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. </li></ul><ul><li>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( ) . </li></ul><ul><li>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 . </li></ul>
    17. 17. The malloc( ) Function <ul><li>Consider the following example: </li></ul><ul><li>#include<stdio.h> </li></ul><ul><li>main() </li></ul><ul><li>{ </li></ul><ul><li>struct marks_data </li></ul><ul><li>{ </li></ul><ul><li>char name[11]; </li></ul><ul><li>int marks; </li></ul><ul><li>}; </li></ul><ul><li>struct marks_data *ptr; </li></ul><ul><li>/* declaration of a stack variable */ </li></ul><ul><li>ptr = (struct marks_data *)malloc(sizeof(struct marks_data)); </li></ul><ul><li>/* declaration of a block of memory on the heap and the block in turn being referenced by ptr */ </li></ul><ul><li>} </li></ul>
    18. 18. The malloc( ) Function ptr 100 Stack Heap Name Marks 100
    19. 19. The malloc( ) Function <ul><li>The malloc( ) function also returns the starting address to the marks_data structure variable on the heap. </li></ul><ul><li>However, malloc( ) returns this not as a pointer to a structure variable of type marks_data , but as a void pointer. </li></ul><ul><li>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 . </li></ul>
    20. 20. The free() Function <ul><li>The free() function is used to deallocate memory allocated to a variable by the malloc() function. </li></ul><ul><li>If the call to the function free( ) is not issued before returning from the function, the pointer pointing to the heap variable will be destroyed when returning from the function, and therefore, the address to the heap variable will be lost. There will then be no way to de-allocate this block of memory on the heap, and return it back to the heap. This amount of memory will not be available for future use by other applications. This is what is referred to as a memory leak. </li></ul>
    21. 21. Self-Referential Structures <ul><li>Suppose, you have been given a task to store a list of marks. The size of the list is not known . </li></ul><ul><li>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. </li></ul><ul><li>Elements of an array are contiguously located, and therefore, array manipulation is easy using an integer variable as a subscript, or using pointer arithmetic. </li></ul>
    22. 22. Self-Referential Structures <ul><li>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. </li></ul><ul><li>Therefore, these variables cannot be processed the way arrays are processed, i.e., using a subscript, or using pointer arithmetic. </li></ul><ul><li>An answer to this is a self-referential structure . </li></ul>
    23. 23. Self-Referential Structures <ul><li>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. </li></ul><ul><li>In other words, each variable maintains a link to another variable of the same type, thus forming a non-contiguous, loosely linked data structure. </li></ul><ul><li>This self-referential data structure is also called a linked list. </li></ul>
    24. 24. Linear and non- linear data structures <ul><li>The data structures that shows the relationship of logical adjacency between the elements are called linear data structures. </li></ul><ul><li>Otherwise, they are called non-linear data structures. </li></ul><ul><li>Different linear data structures are stacks, queues, linear linked lists such as singly linked list, doubly linked linear lists etc. </li></ul><ul><li>Trees, graphs and files are non-linear data structures. </li></ul>
    25. 25. What is the need for ADT, Data types and Data Structures?
    26. 26. LINEAR DATA STRUCTURES
    27. 27. <ul><li>STACKS </li></ul>
    28. 28. Objectives <ul><li>Define a stack </li></ul><ul><li>Describe the operations on a stack </li></ul><ul><li>Implement a stack as a special case of a linked list </li></ul><ul><li>Describe applications of stacks </li></ul>
    29. 29. What is a Stack? <ul><li>A stack is a data structure in which insertions and deletions can only be done at the top. </li></ul><ul><li>A common example of a stack, which permits the selection of only its end elements, is a stack of books. </li></ul><ul><li>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. </li></ul>
    30. 30. What is a Stack? <ul><li>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). </li></ul><ul><li>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). </li></ul>
    31. 31. What is a Stack? (c) top 2 4 6 8 10 2 4 6 8 10 12 2 4 6 8 10 12 14 (a) (b) top top
    32. 32. Characteristics of a Stack <ul><li>When you add an element to the stack, you say that you push it on the stack (it is always done at the top), and if you delete an element from a stack, you say that you pop it from the stack( again from the top of the stack). </li></ul><ul><li>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 . </li></ul><ul><li>Unlike an array that is static in terms of its size, a stack is a dynamic data structure. </li></ul>
    33. 33. Characteristics of a Stack <ul><li>Since the definition of the stack provides for the insertion and deletion of nodes into it, a stack can grow and shrink dynamically at runtime. </li></ul><ul><li>An ideal implementation of a stack would be a special kind of linked list in which insertions and deletions can only be done at the top of the list. </li></ul>
    34. 34. Operations on Stacks <ul><li>Some of the typical operations performed on stacks are: </li></ul><ul><li>create (s) – to create s as an empty stack </li></ul><ul><li>push (s, i) – to insert the element i on top of the stack s </li></ul><ul><li>pop (s) – to remove the top element of the stack and to eturn the removed element as a function value. </li></ul><ul><li>top (s) – to return the top element of stack(s) </li></ul><ul><li>empty(s) – to check whether the stack is empty or not. It returns true if the stack is empty, and returns false otherwise. </li></ul>
    35. 35. Implementation of Stacks <ul><li>An array can be used to implement a stack. </li></ul><ul><li>But since array size is defined at compile time, it cannot grow dynamically at runtime, and therefore, an attempt to insert an element into a array implementation of a stack that is already full causes a stack overflow. </li></ul><ul><li>A stack, by definition, is a data structure that cannot be full since it can dynamically grow and shrink at runtime. </li></ul>
    36. 36. Implementation of Stacks <ul><li>An ideal implementation for a stack is a linked list that can dynamically grow and shrink at runtime. </li></ul><ul><li>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 . </li></ul><ul><li>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). </li></ul>
    37. 37. Code Implementing for a Stack <ul><li>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. </li></ul><ul><li>struct stack </li></ul><ul><li>{ int info; </li></ul><ul><li>struct stack *next; </li></ul><ul><li>}; </li></ul><ul><li>/* pointer declaration to point to the top of the stack */ </li></ul><ul><li>struct stack *top; </li></ul><ul><li>main( ) </li></ul><ul><li>{ </li></ul>
    38. 38. Code Implementing for a Stack <ul><li>top = NULL; </li></ul><ul><li>char menu = ‘0 ‘; </li></ul><ul><li>while (menu != ‘3’) </li></ul><ul><li>{ </li></ul><ul><li>printf( “Add Nodes :n”); </li></ul><ul><li>printf( “Delete Nodes :n”); </li></ul><ul><li>printf( “Exit :n”); </li></ul><ul><li>menu = getchar( ); </li></ul><ul><li>switch (menu) </li></ul><ul><li>{ </li></ul><ul><li>case ‘1’ : push( ); </li></ul><ul><li>break; </li></ul><ul><li>case ‘2’ : pop( ) </li></ul><ul><li>break; </li></ul>
    39. 39. Code Implementing for a Stack <ul><li>case ‘3’: exit( ); </li></ul><ul><li> break; </li></ul><ul><li>} /* end of switch */ </li></ul><ul><li>} /* end of main( ) */ </li></ul>
    40. 40. Implementing push( ) <ul><li>push( ) </li></ul><ul><li>{ </li></ul><ul><li>struct stack * new; </li></ul><ul><li>char ch; </li></ul><ul><li>ch = ‘y’; </li></ul><ul><li>while (ch = = ‘y’) </li></ul><ul><li>{ </li></ul><ul><li>new = makenode( ); </li></ul><ul><li>/* checking for an empty stack */ </li></ul><ul><li>if ( top = = null) </li></ul><ul><li>{ </li></ul><ul><li>top = new; </li></ul><ul><li>} </li></ul>
    41. 41. Implementing push( ) <ul><li>else </li></ul><ul><li>{ </li></ul><ul><li>new->next = top; </li></ul><ul><li>top = new; </li></ul><ul><li>} </li></ul><ul><li>printf(&quot;%s&quot;,&quot;Want to add more nodesn&quot;); </li></ul><ul><li>scanf( &quot;%c&quot;, &ch ); </li></ul><ul><li>fflush( stdin ); </li></ul><ul><li>} /* end of while */ </li></ul><ul><li>} /* end of push( ) </li></ul>
    42. 42. A View of the Stack After Insertion next info info next new top 1 2 3
    43. 43. Creating a Node on a Stack <ul><li>struct stack *makenode() </li></ul><ul><li>{ </li></ul><ul><li>struct stack *new; </li></ul><ul><li>new=(struct stack *) malloc(sizeof(struct(stack))); </li></ul><ul><li>scanf(&quot;%d&quot;,&new->info); </li></ul><ul><li>new->next = NULL; </li></ul><ul><li>return(new); </li></ul><ul><li>} </li></ul>
    44. 44. Implementing pop( ) <ul><li>pop( ) </li></ul><ul><li>{ </li></ul><ul><li>struct stack * temp; </li></ul><ul><li>int x; </li></ul><ul><li>/* check for an empty stack */ </li></ul><ul><li>if (top = = null) </li></ul><ul><li>{ </li></ul><ul><li>printf (“Cannot remove nodes from an empty stack */ </li></ul><ul><li>exit( ); </li></ul><ul><li>} </li></ul>
    45. 45. Implementing pop( ) <ul><li>else </li></ul><ul><li>{ </li></ul><ul><li>temp = top; </li></ul><ul><li>x = top->info; </li></ul><ul><li>top = top->next; </li></ul><ul><li>free( temp); </li></ul><ul><li>return x; </li></ul><ul><li>} </li></ul>
    46. 46. A View of the Stack After Deletion next top temp A B
    47. 47. Applications of Stacks <ul><li>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. </li></ul><ul><li>Consider what happens within a computer when function main( ) calls another function. </li></ul><ul><li>How does a program remember where to resume execution from after returning from a function call? </li></ul><ul><li>From where does it pick up the values of the local variables in the function main( ) after returning from the subprogram? </li></ul>
    48. 48. Applications of Stacks <ul><li>As an example, let main( ) call a( ) . Function a( ) , in turn, calls function b( ) , and function b( ) in turn invokes function c( ) . </li></ul><ul><li>main( ) is the first one to execute, but it is the last one to finish, after a( ) has finished and returned. </li></ul><ul><li>a( ) cannot finish its work until b( ) has finished and returned. b( ) cannot finish its work until c( ) has finished and returned. </li></ul>
    49. 49. Applications of Stacks <ul><li>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(). </li></ul><ul><li>When b( ) is called from a( ) , b( )’s calling information is 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( ) ). </li></ul>
    50. 50. Applications of Stacks <ul><li>Then, when b( ) calls c( ) , c( ) ’s calling information is 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( ) ). </li></ul><ul><li>When c( ) finishes execution, the information needed to return to b( ) is retrieved by popping the stack. </li></ul><ul><li>Then, when b( ) finishes execution, its return address is popped from the stack to return to a( ) </li></ul>
    51. 51. Applications of Stacks <ul><li>Finally, when a( ) completes, the stack is again popped to get back to main( ) . </li></ul><ul><li>When main( ) finishes, the stack becomes empty. </li></ul><ul><li>Thus, a stack plays an important role in function calls. </li></ul><ul><li>The same technique is used in recursion when a function invokes itself. </li></ul>
    52. 52. <ul><li>Queues </li></ul>
    53. 53. Objectives <ul><li>In this session, you will learn to: </li></ul><ul><li>Define a queue </li></ul><ul><li>Describe the operations on a queue </li></ul><ul><li>Implement a queue as a special case of a linked list </li></ul><ul><li>Describe applications of queues </li></ul>
    54. 54. Defining a Queue <ul><li>A Queue is a special kind of data structure in which elements are added at one end (called the rear ), and elements are removed from the other end (called the front ). </li></ul><ul><li>You come across a number of examples of a queue in real life situations. </li></ul><ul><li>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) </li></ul>
    55. 55. Defining a Queue <ul><li>Every time the student at the front of the queue deposits the fee, he leaves the queue (analogous to deleting nodes from a queue). </li></ul><ul><li>The student who comes first in the queue is the one who leaves the queue first. </li></ul><ul><li>Since the first item inserted is the first item removed, a queue is commonly called a first-in-first-out or a FIFO data structure . </li></ul>
    56. 56. Queue Insertions and Deletions rear front front rear front 1 2 3 1 2 3 4 2 3 4 rear (a) (b) (c)
    57. 57. Queue Operations <ul><li>To complete this definition of a queue, you must specify all the operations that it permits. </li></ul><ul><li>The first step you must perform in working with any queue is to initialize the queue for further use. Other important operations are </li></ul><ul><ul><li>Add or insert an element into a queue. </li></ul></ul><ul><ul><li>Delete an element from a queue. </li></ul></ul><ul><ul><li>Display the contents of a queue. </li></ul></ul><ul><li>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. </li></ul>
    58. 58. Queue Operations <ul><li>create(q) – which creates q as an empty queue </li></ul><ul><li>enq(i) – adds the element i to the rear of the queue and returns the new queue </li></ul><ul><li>deq(q) – removes the element at the front end of the queue (q) and returns the resulting queue as well as the removed element </li></ul><ul><li>empty (q) – it checks the queue (q) whether it is empty or not. It returns true if the queue is empty and returns false otherwise </li></ul><ul><li>front(q) – returns the front element of the queue without changing the queue </li></ul><ul><li>queuesize (q) – returns the number of entries in the queue </li></ul>
    59. 59. Implementing Queues <ul><li>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. </li></ul><ul><li>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. </li></ul>
    60. 60. Implementing Queues <ul><li>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. </li></ul><ul><li>The following figure gives a visual depiction of linked list implementation of a queue. </li></ul>rear front 2 4 7
    61. 61. Queue Declaration & Operations <ul><li>struct queue </li></ul><ul><li>{ </li></ul><ul><li>int info; </li></ul><ul><li>struct queue *next; </li></ul><ul><li>}; </li></ul><ul><li>struct queue *front, *rear; </li></ul><ul><li>An empty queue is represented by q->front = q->rear = null. Therefore, clearq( ) can be implemented as follows: </li></ul><ul><li>void clearq(struct queue * queue_pointer) </li></ul><ul><li>{ </li></ul><ul><li>queue_pointer->front = queue_pointer ->rear = null; </li></ul><ul><li>} </li></ul>
    62. 62. Queue Operations <ul><li>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. </li></ul><ul><li>int emptyq (queue_pointer) </li></ul><ul><li>{ </li></ul><ul><li>if (queue_pointer = = null) </li></ul><ul><li>return (1); </li></ul><ul><li>else </li></ul><ul><li>return(0); </li></ul><ul><li>} </li></ul>
    63. 63. Insertion into a Queue <ul><li>struct queue </li></ul><ul><li>{ int info; </li></ul><ul><li>struct queue *next; </li></ul><ul><li>}; </li></ul><ul><li>/* pointer declarations to point to the front, and rear of the queue */ </li></ul><ul><li>struct queue *front, *rear; </li></ul><ul><li>main( ) </li></ul><ul><li>{ </li></ul><ul><li>front = NULL; </li></ul><ul><li>rear = NULL; </li></ul>
    64. 64. Insertion into a Queue <ul><li>char menu = ‘0 ‘; </li></ul><ul><li>while (menu != ‘3’) </li></ul><ul><li>{ </li></ul><ul><li>printf( “Add Nodes :n”); </li></ul><ul><li>printf( “Delete Nodes :n”); </li></ul><ul><li>printf( “Exit :n”); </li></ul><ul><li>menu = getchar( ); </li></ul><ul><li>switch (menu) </li></ul><ul><li>{ </li></ul><ul><li>case ‘1’ : enq( ); </li></ul><ul><li>break; </li></ul><ul><li>case ‘2’ : deq( ) </li></ul><ul><li>break; </li></ul>
    65. 65. Insertion into a Queue <ul><li>case ‘3’: exit( ); </li></ul><ul><li>break; </li></ul><ul><li>} /* end of switch */ </li></ul><ul><li>} /* end of main( ) */ </li></ul>
    66. 66. Insertion into a Queue <ul><li>void enq( ) </li></ul><ul><li>{ </li></ul><ul><li>struct queue *new; </li></ul><ul><li>new = getnode( ); </li></ul><ul><li>if(queue_pointer->front= =queue_pointer->rear = = null) </li></ul><ul><li>{ </li></ul><ul><li>queue_pointer->front = new; </li></ul><ul><li>queue_pointer->rear = new; </li></ul><ul><li>} </li></ul><ul><li>else </li></ul><ul><li>{ </li></ul><ul><li>rear->next = new; </li></ul><ul><li>rear = new; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
    67. 67. Creating a Node on a Queue <ul><li>struct queue * makenode() </li></ul><ul><li>{ </li></ul><ul><li>struct queue *new; </li></ul><ul><li>new=(struct queue *) malloc(sizeof(struct(queue)); </li></ul><ul><li>scanf(&quot;%d&quot;,&new->info); </li></ul><ul><li>new->next = NULL; </li></ul><ul><li>return(new); </li></ul><ul><li>} </li></ul>
    68. 68. Insertion into a Queue New node inserted at rear of queue next next next rear front 2 4 7 8 New
    69. 69. Deletion from a Queue <ul><li>int deq( ) </li></ul><ul><li>{ </li></ul><ul><li>struct queue *temp; </li></ul><ul><li>int x; </li></ul><ul><li>if(queue_pointer->front= =queue_pointer->rear = = null) </li></ul><ul><li>{ </li></ul><ul><li>printf( “Queue Underflown”); </li></ul><ul><li>exit (1); </li></ul><ul><li>} </li></ul><ul><li>temp = front; </li></ul><ul><li>x=temp->info; </li></ul><ul><li>front = front->next; </li></ul><ul><li>free(temp); </li></ul><ul><li>if(front = = null) /* check for queue becoming empty after node deletion */ </li></ul><ul><li>rear = null; </li></ul><ul><li>return(x); </li></ul><ul><li>} </li></ul>
    70. 70. Deletion of a Node From a Queue Node being deleted at the front of the queue rear front 2 4 7
    71. 71. Applications of Queues <ul><li>Queues are also very useful in a time-sharing multi-user operating system where many users share the CPU simultaneously. </li></ul><ul><li>Whenever a user requests the CPU to run a particular program, the operating system adds the request ( by first of all converting the program into a process that is a running instance of the program, and assigning the process an ID). </li></ul><ul><li>This process ID is then added at the end of the queue of jobs waiting to be executed. </li></ul>
    72. 72. Applications of Queues <ul><li>Whenever the CPU is free, it executes the job that is at the front of the job queue. </li></ul><ul><li>Similarly, there are queues for shared I/O devices. Each device maintains its own queue of requests. </li></ul><ul><li>An example is a print queue on a network printer, which queues up print jobs issued by various users on the network. </li></ul><ul><li>The first print request is the first one to be processed. New print requests are added at the end of the queue. </li></ul>
    73. 73. Disadvantages of a Queue <ul><li>The operations such as inserting an element and deleting an element from queue works perfectly until the rear index reaches the end of the array. </li></ul><ul><li>If some items are deleted from the front, there will be some empty spaces in the beginning of the queue. Since the rear index points to the end of the array, the queue is thought to be full and no more insertions are possible. </li></ul><ul><li>This disadvantage can be eliminated by removing the item in the front end of the queue and then shifting the data from the second locations onwards to their respective previous locations. </li></ul><ul><li>Another option is to use a circular queue. </li></ul>
    74. 74. Double – ended Queue <ul><li>A double – ended queue or deque is a special kind of queue in which insertions and deletions can be done at both ends of queue. </li></ul><ul><li>The operations that can be performed on a deque are: </li></ul><ul><ul><li>Insert an item at the front end </li></ul></ul><ul><ul><li>Insert an item at the rear end </li></ul></ul><ul><ul><li>Delete an item at the front end </li></ul></ul><ul><ul><li>Delete an item at the rear end </li></ul></ul><ul><ul><li>Display the contents of the queue </li></ul></ul>
    75. 75. Circular Queue <ul><li>In circular queue, the elements of a given queue can be stored efficiently in an array so as to “wrap around” in such a way that the rear of the queue is followed by the front of the queue. </li></ul><ul><li>Allows the entire array to store the elements without shifting any data within the queue. </li></ul><ul><li>Assume that circular queue contains only one item as shown in the next slide [fig (a)]. In this case, the rear end identified by r is 0 and the front end identified by f is also 0. Since only r is incremented upon item insertion, its initial value has to be -1 and f should be 0. </li></ul>
    76. 76. Pictorial Representation of Circular Queue f, r (a) (b) After inserting 20 & 30 After inserting 40 & 50 (c) 10 0 2 4 1 3 10 0 2 4 1 3 20 30 f r 10 0 2 4 1 3 20 30 f r 40 50 10 f 20 30 r 40 50 0 1 2 3 4 10 f 20 30 r 10 f r 0 1 2 3 4 0 1 2 3 4
    77. 77. Pictorial Representation of Circular Queue (contd…) (d) After deleting 10 & 20 (e) After inserting 60 f r 0 2 4 1 3 30 40 50 f r 0 2 4 1 3 30 40 50 60 f 30 r 40 50 0 1 2 3 4 f 30 r 40 50 60 0 1 2 3 4
    78. 78. <ul><li>The configuration shown in fig (b) is obtained on inserting 20 and 30 into the queue. To insert an item, r has to be incremented first. The statement used is: </li></ul><ul><ul><li>r=r+1; or r = (r+1)%QUEUE_SIZE; </li></ul></ul><ul><li>The configuration shown in fig (c) is obtained on inserting 40 and 50 into the queue. At this point the queue is full. r is now pointing to the last element in the queue. </li></ul><ul><li>Suppose we delete 2 items, 10 and 20, one after the other. The resulting queue is shown in fig (d). </li></ul><ul><li>Now, if we insert 60 into the queue, r will again be incremented. Now r will become 0. </li></ul>Pictorial Representation of Circular Queue (contd…)
    79. 79. <ul><li>In case of items being deleted, as in an ordinary queue, the front end identifier f is updated accordingly [fig (d)] and is made to point to the next front item in the queue. </li></ul><ul><li>The statement used to increment f is: </li></ul><ul><ul><li>f = (f+1)%QUEUE_SIZE; </li></ul></ul><ul><li>In case of this kind of queue, to check for overflow or underflow, a variable is used that contains the number of items in the queue at any time. When an item is inserted or deleted, the variable is incremented or decremented by 1 respectively. </li></ul>Pictorial Representation of Circular Queue (contd…) >>Program
    80. 80. Priority Queue <ul><li>A special type of queue in which items can be inserted or deleted based on the priority. </li></ul><ul><li>An element with highest priority is processed before processing any of the lower priority elements. </li></ul><ul><li>If the elements in the queue are of same priority, then the element, which is inserted first is processed. </li></ul>
    81. 81. Types of Priority Queues <ul><li>Classified into two groups: </li></ul><ul><ul><li>Ascending priority queue </li></ul></ul><ul><ul><li>Descending priority queue </li></ul></ul><ul><li>In ascending priority queue, elements can be inserted in any order. But, while deleting an element from the queue, only the smallest element is removed first. </li></ul><ul><li>In descending priority queue too, elements can be inserted in any order. But, while deleting an element from the queue, only the largest element is deleted first. </li></ul>
    82. 82. LINKED LISTS
    83. 83. Objectives <ul><li>Describe linked lists </li></ul><ul><li>Types of linked lists </li></ul><ul><li>Write code to: </li></ul><ul><ul><li>Create a sorted linked list, </li></ul></ul><ul><ul><li>Insert nodes into a sorted linked list </li></ul></ul><ul><ul><li>Traverse a linked list </li></ul></ul><ul><ul><li>Delete nodes from a linked list </li></ul></ul><ul><li>Doubly linked lists </li></ul><ul><li>Circular linked lists </li></ul>
    84. 84. Linked Lists – An Introduction <ul><li>Linked lists were developed in 1955-56 by Allen Newell , Cliff Shaw and Herbert Simon at RAND Corporation as the primary data structure for their Information Processing Language . IPL was used by the authors to develop several early artificial intelligence programs. </li></ul><ul><li>Can be defined as a collection of self – referential structures ordered not by their physical placement in memory but by logical links that are stored as part of the data in the structure itself. </li></ul><ul><li>Each structure of the list is called a node. </li></ul><ul><li>Each node consists of the data and a pointer pointing to the next node on the list. </li></ul><ul><li>Accessed via a pointer to the first node of the list </li></ul>
    85. 85. Linked List <ul><li>Subsequent nodes are accessed via the link-pointer member of the current node </li></ul><ul><li>Link pointer in the last node is set to null to mark the list’s end </li></ul><ul><li>Linked lists permit insertion and removal of nodes at any point in the list in constant time, but do not allow random access. </li></ul><ul><li>Use a linked list instead of an array when </li></ul><ul><ul><li>You have an unpredictable number of data elements </li></ul></ul><ul><ul><li>Your list needs to be sorted quickly </li></ul></ul>
    86. 86. Types of linked lists <ul><li>Types of linked lists are: </li></ul><ul><ul><li>Singly linked list </li></ul></ul><ul><ul><ul><li>Begins with a pointer to the first node </li></ul></ul></ul><ul><ul><ul><li>Terminates with a null pointer </li></ul></ul></ul><ul><ul><ul><li>Only traversed in one direction </li></ul></ul></ul><ul><ul><li>Circular, singly linked </li></ul></ul><ul><ul><ul><li>Pointer in the last node points back to the first node </li></ul></ul></ul><ul><ul><li>Doubly linked list </li></ul></ul><ul><ul><ul><li>Two “start pointers” – first element and last element </li></ul></ul></ul><ul><ul><ul><li>Each node has a forward pointer and a backward pointer </li></ul></ul></ul><ul><ul><ul><li>Allows traversals both forwards and backwards </li></ul></ul></ul><ul><ul><li>Circular, doubly linked list </li></ul></ul><ul><ul><ul><li>Forward pointer of the last node points to the first node and backward pointer of the first node points to the last node </li></ul></ul></ul>
    87. 87. Elementary linked list functions <ul><li>These functions allow the user to add or remove elements to the linked list . </li></ul><ul><li>Depending on the language you wish to use for actual implementation, you can use virtually any datatype. </li></ul><ul><li>The following basic operations can be performed on a linked list: </li></ul><ul><ul><li>Creating a linked list </li></ul></ul><ul><ul><li>Traversing the list </li></ul></ul><ul><ul><li>Counting the items in the list </li></ul></ul><ul><ul><li>Printing the list </li></ul></ul><ul><ul><li>Looking up an item for editing or printing </li></ul></ul><ul><ul><li>Inserting an item </li></ul></ul><ul><ul><li>Deleting an item </li></ul></ul><ul><ul><li>Concatenating two lists </li></ul></ul>
    88. 88. Declaring a Linked List <ul><li>Let us define a self-referential structure to store a list of marks the size of which may not be known. </li></ul><ul><li>struct marks_list </li></ul><ul><li>{ </li></ul><ul><li>int marks; </li></ul><ul><li>struct marks_list *next; </li></ul><ul><li>}; </li></ul><ul><li>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 . </li></ul>
    89. 89. Declaring a Linked List <ul><li>Therefore, a part of the structure is referencing a structure type of itself, and hence the name self-referential structure . </li></ul><ul><li>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. </li></ul><ul><li>One can visualize a linked list as shown in the following slide: </li></ul>
    90. 90. Visualizing a Linked List marks next 180 100 140 start 100 75 140 85 180 95 230 100 x marks next marks next marks next Stack Heap
    91. 91. Insertion of an element into a linked list <ul><li>An element can be inserted into the list in 3 ways: </li></ul><ul><ul><li>Insertion at the front of the list </li></ul></ul><ul><ul><li>Insertion in the middle </li></ul></ul><ul><ul><li>Insertion in the end of the list </li></ul></ul><ul><li>A general algorithm for insertion is : </li></ul><ul><ul><li>Begin </li></ul></ul><ul><ul><li>if list is empty or the new node comes before the head node then , </li></ul></ul><ul><ul><li>insert the new node as the head node </li></ul></ul><ul><ul><li>else </li></ul></ul><ul><ul><li>if the new node comes after the last node then , </li></ul></ul><ul><ul><li>insert the new node as the end node </li></ul></ul><ul><ul><li>else </li></ul></ul><ul><ul><li>inset the new node in the body of the list </li></ul></ul><ul><ul><li>End </li></ul></ul>
    92. 92. Inserting an element at the head of a linked list <ul><li>Adding an element to the head of a linked list is quite simple. </li></ul><ul><li>Algorithm for placing the new element at the head of the list: </li></ul><ul><ul><li>Obtain space for the new node </li></ul></ul><ul><ul><li>Assign data to the item field of the new node </li></ul></ul><ul><ul><li>Set the next pointer of the new node to point to the start of the list </li></ul></ul><ul><ul><li>Change the head pointer to point to the new node. </li></ul></ul>
    93. 93. Inserting an element in the middle of a linked list <ul><li>Adding a new node in the middle of a list is relatively simple. </li></ul><ul><li>Assuming a new node X is being inserted between nodes N1 and N2 </li></ul><ul><li>Algorithm for inserting a new node between two nodes in the list </li></ul><ul><ul><li>Set space for new node X </li></ul></ul><ul><ul><li>Assign value to the item field of X </li></ul></ul><ul><ul><li>Set the next field of X to point to N2 </li></ul></ul><ul><ul><li>Set the next field of N1 to point to X </li></ul></ul>
    94. 94. Inserting an element at the tail of a linked list <ul><li>Adding an element to the tail of a list is similar to inserting an element in the middle except the next filed is set to NULL. </li></ul><ul><li>Algorithm for placing a new node at the end of the list </li></ul><ul><ul><li>Set space for new node </li></ul></ul><ul><ul><li>Assign value to the item field of the new node. </li></ul></ul><ul><ul><li>Set the next field to point to NULL or to dummy or sentinel node) </li></ul></ul><ul><ul><li>Set the next pointer of the last node to point to the new node. </li></ul></ul>
    95. 95. <ul><li>Deleting a node from a linked list is a very simple procedure as only one pointer value needs to be changed. </li></ul><ul><li>A node can be deleted from a linked list as follows: </li></ul><ul><ul><li>Deleting the first item </li></ul></ul><ul><ul><li>Deleting the last item </li></ul></ul><ul><ul><li>Deleting an item between 2 nodes in the middle of the list. </li></ul></ul><ul><li>A general algorithm for deletion is: </li></ul><ul><ul><li>Begin </li></ul></ul><ul><ul><li>if list is empty, then , </li></ul></ul><ul><ul><li>node cannot be deleted </li></ul></ul><ul><ul><li>else </li></ul></ul><ul><ul><li>if node to be deleted is the first node, then , </li></ul></ul><ul><ul><li>make the head to point to the second node </li></ul></ul><ul><ul><li>else </li></ul></ul><ul><ul><li>delete the node from the body of the list </li></ul></ul><ul><ul><li>End </li></ul></ul>Deleting a node in a linked list
    96. 96. Creating a Sorted Linked List <ul><li>#include<stdio.h> </li></ul><ul><li>#include<malloc.h> </li></ul><ul><li>struct marks_list *start, *prev; </li></ul><ul><li>/* variables declared outside main() are global in nature and can be accessed by other functions called from main() */ </li></ul><ul><li>struct marks_list </li></ul><ul><li>{ </li></ul><ul><li>int marks; </li></ul><ul><li>struct marks_list *next; </li></ul><ul><li>}; </li></ul><ul><li>main() </li></ul><ul><li>{ </li></ul><ul><li>struct marks_list * makenode(); </li></ul><ul><li>/* function prototype declaration */ </li></ul>
    97. 97. Creating a Sorted Linked List <ul><li>struct marks_list *new; </li></ul><ul><li>start = NULL; </li></ul><ul><li>char menu = ‘0 ‘; </li></ul><ul><li>while (menu != ‘4’) </li></ul><ul><li>{ </li></ul><ul><li>printf( “Add Nodes :n”); </li></ul><ul><li>printf( “Delete Nodes :n”); </li></ul><ul><li>printf( “Traverse a list :n”); </li></ul><ul><li>printf( “Exit :n”); </li></ul><ul><li>menu = getchar( ); </li></ul>
    98. 98. Creating a Sorted Linked List <ul><li>switch (menu) </li></ul><ul><li>{ </li></ul><ul><li>case ‘1’ : addnode( ); </li></ul><ul><li>break; </li></ul><ul><li>case ‘2’ : deletenode( ) </li></ul><ul><li>break; </li></ul><ul><li>case ‘3’ : traverse( ); </li></ul><ul><li>break; </li></ul><ul><li>case ‘4’: exit( ); </li></ul><ul><li>break; </li></ul><ul><li>} /* end of switch */ </li></ul><ul><li>} /* end of main( ) */ </li></ul>
    99. 99. Creating a Sorted Linked List <ul><li>addnode( ) </li></ul><ul><li>{ </li></ul><ul><li>char ch = 'y'; </li></ul><ul><li>while ( ch = = 'y' ) </li></ul><ul><li>{ </li></ul><ul><li>new = makenode(); </li></ul><ul><li>/* creation of a list is treated as a special case of insertion */ </li></ul><ul><li>if ( start == NULL) </li></ul><ul><li>{ </li></ul><ul><li>start = new; </li></ul><ul><li>} </li></ul><ul><li>else </li></ul><ul><li>{ </li></ul><ul><li>insert(); </li></ul><ul><li>traverse( ); </li></ul><ul><li>} </li></ul>
    100. 100. Creating a Sorted Linked List <ul><li>printf(&quot;%s&quot;,&quot;Want to add more nodesn&quot;); </li></ul><ul><li>scanf( &quot;%c&quot;, &ch ); </li></ul><ul><li>fflush( stdin ); </li></ul><ul><li>} /* end of while */ </li></ul><ul><li>} /* end of addnode( ) </li></ul>
    101. 101. Creating a Sorted Linked List <ul><li>struct marks_list * makenode() </li></ul><ul><li>{ </li></ul><ul><li>struct marks_list *new; </li></ul><ul><li>new=(struct marks_list *) malloc(sizeof(struct(marks_list)); </li></ul><ul><li>scanf(&quot;%d&quot;,&new->marks); </li></ul><ul><li>new->next = NULL; </li></ul><ul><li>return(new); </li></ul><ul><li>} </li></ul>
    102. 102. Creating a Sorted Linked List <ul><li>insert(struct marks_list *start) </li></ul><ul><li>{ </li></ul><ul><li>struct marks_list *ptr, *prev; </li></ul><ul><li>for(ptr=start,prev=start;(ptr);prev=ptr,ptr=ptr->next) </li></ul><ul><li>{ </li></ul><ul><li>if (new->marks < start->marks) </li></ul><ul><li>{ </li></ul><ul><li>/* insertion at the beginning of a list */ </li></ul><ul><li>new->next = start; </li></ul><ul><li>start = new; </li></ul><ul><li>} </li></ul>
    103. 103. Creating a Sorted Linked List <ul><li>/* insertion in the middle of a list */ </li></ul><ul><li>if(new->marks > ptr->marks) </li></ul><ul><li>{ </li></ul><ul><li>continue; </li></ul><ul><li>} </li></ul><ul><li>else </li></ul><ul><li>{ </li></ul><ul><li>prev->next = new; </li></ul><ul><li>new->next = ptr; </li></ul><ul><li>} </li></ul><ul><li>} /* end of for loop */ </li></ul>
    104. 104. Creating a Sorted Linked List <ul><li>/* insertion at the end of the list */ </li></ul><ul><li>if (ptr == null) </li></ul><ul><li>{ </li></ul><ul><li>prev->next = new; </li></ul><ul><li>new->next = null; </li></ul><ul><li>} </li></ul><ul><li>} /* end of insert */ </li></ul>
    105. 105. Searching a Value in a Linked List <ul><li>struct marks_list *search( int val) </li></ul><ul><li>{ </li></ul><ul><li>for( ptr = start; (ptr); ptr = ptr->next) </li></ul><ul><li>{ </li></ul><ul><li>if (val = = ptr-> marks) </li></ul><ul><li>return ptr; </li></ul><ul><li>} </li></ul>
    106. 106. Deleting a Node From a Linked List <ul><li>delete ( ) </li></ul><ul><li>{ </li></ul><ul><li>struct marks_list *ptr, *prev, *temp; </li></ul><ul><li>int score; </li></ul><ul><li>/* search the linked list for the value to be deleted */ </li></ul><ul><li>scanf(“%d”, &score); </li></ul><ul><li>fflush(stdin); </li></ul><ul><li>for (ptr = start, prev = start; (ptr); prev = ptr, ptr = ptr->next) </li></ul><ul><li>{ </li></ul><ul><li>/* deletion of the first node in the list */ </li></ul><ul><li>if (score = = start-> marks) </li></ul><ul><li>{ </li></ul><ul><li>temp =start; </li></ul><ul><li>start = start-> next; </li></ul><ul><li>free(temp); </li></ul><ul><li>} </li></ul>
    107. 107. Deleting a Node From a Linked List <ul><li>/* this code would hold true for deletion in the middle and at the end of a linked list */ </li></ul><ul><li>if (score = = ptr-> marks) </li></ul><ul><li>{ </li></ul><ul><li>prev-> next = ptr-> next; </li></ul><ul><li>free(ptr); </li></ul><ul><li>} </li></ul><ul><li>}/* end of for loop */ </li></ul><ul><li>} /* end of delete */ </li></ul>
    108. 108. <ul><li>Doubly Linked Lists </li></ul>
    109. 109. Objectives <ul><li>In this session, you will learn to: </li></ul><ul><li>Describe the need for, and advantages of a doubly linked list </li></ul><ul><li>Write code to: </li></ul><ul><ul><li>Create a sorted doubly linked list, </li></ul></ul><ul><ul><li>Insert nodes into a sorted doubly linked list </li></ul></ul><ul><ul><li>Traverse a doubly linked list </li></ul></ul><ul><ul><li>Delete nodes from a doubly linked list </li></ul></ul>
    110. 110. Need For a Doubly Linked List <ul><li>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. </li></ul><ul><li>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. </li></ul><ul><li>It would have been efficient had it been possible to search for a value in a linked list from the end of the list. </li></ul>
    111. 111. Properties of a Doubly Linked List <ul><li>This would be possible only if we have a doubly linked list. </li></ul><ul><li>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. </li></ul><ul><li>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 . </li></ul>
    112. 112. Properties of a Doubly Linked List <ul><li>In a doubly linked list, the prior pointer of the first node will be null , as there is no node before the first node. </li></ul><ul><li>In a doubly linked list, the next pointer of the last node will be null , as there is no node after this list. </li></ul><ul><li>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. </li></ul><ul><li>A doubly linked list would have two pointers, start and last to facilitate traversal from the beginning and end of the list respectively. </li></ul>
    113. 113. Declaration of a Doubly Linked List <ul><li>struct marks_list </li></ul><ul><li>{ </li></ul><ul><li>struct double_list *prior; </li></ul><ul><li>int info; </li></ul><ul><li>struct marks_list *next; </li></ul><ul><li>} </li></ul>
    114. 114. Visualizing a Doubly Linked List prior next marks null 1 120 100 prior next marks 100 2 140 120 prior next marks 120 3 null 140 100 start 140 last
    115. 115. Creating a Sorted Doubly Linked List <ul><li>#include<stdio.h> </li></ul><ul><li>#include<malloc.h> </li></ul><ul><li>struct marks_list *start, *last; </li></ul><ul><li>/* variables declared outside main() are global in nature and can be accessed by other functions called from main() */ </li></ul><ul><li>struct marks_list </li></ul><ul><li>{ </li></ul><ul><li>struct marks_list *prior; </li></ul><ul><li>int marks; </li></ul><ul><li>struct marks_list *next; </li></ul><ul><li>}; </li></ul><ul><li>main() </li></ul><ul><li>{ </li></ul><ul><li>struct marks_list * makenode(); </li></ul><ul><li>/* function prototype declaration */ </li></ul>
    116. 116. Creating a Sorted Doubly Linked List <ul><li>struct marks_list *new; </li></ul><ul><li>start = NULL; </li></ul><ul><li>char menu = ‘0 ‘; </li></ul><ul><li>while (menu != ‘5’) </li></ul><ul><li>{ </li></ul><ul><li>printf( “Add Nodes :n”); </li></ul><ul><li>printf( “Delete Nodes :n”); </li></ul><ul><li>printf( “Forward Traverse a list :n”); </li></ul><ul><li>printf( “Reverse Traverse a list :n”); </li></ul><ul><li>printf( “Exit :n”); </li></ul><ul><li>menu = getchar( ); </li></ul>
    117. 117. Creating a Sorted Doubly Linked List <ul><li>switch (menu) </li></ul><ul><li>{ </li></ul><ul><li>case ‘1’ : addnode( ); </li></ul><ul><li>break; </li></ul><ul><li>case ‘2’ : deletenode( ) </li></ul><ul><li>break; </li></ul><ul><li>case ‘3’ : forward_traverse( ); </li></ul><ul><li>break; </li></ul><ul><li>case ‘4’ : reverse_traverse( ); </li></ul><ul><li>break; </li></ul><ul><li>case ‘5’: exit( ); </li></ul><ul><li>break; </li></ul><ul><li>} /* end of switch */ </li></ul><ul><li>} /* end of main( ) */ </li></ul>
    118. 118. Creating a Sorted Doubly Linked List <ul><li>addnode( ) </li></ul><ul><li>{ </li></ul><ul><li>char ch = 'y'; </li></ul><ul><li>while ( ch = = 'y' ) </li></ul><ul><li>{ </li></ul><ul><li>new = makenode(); </li></ul><ul><li>/* creation of a list is treated as a special case of insertion */ </li></ul><ul><li>if ( start = = NULL) </li></ul><ul><li>{ </li></ul><ul><li>start = new; </li></ul><ul><li>start->prior = null; </li></ul><ul><li>} </li></ul><ul><li>else </li></ul><ul><li>{ </li></ul><ul><li>insert(); </li></ul><ul><li>traverse( ); </li></ul><ul><li>} </li></ul>
    119. 119. Creating a Sorted Doubly Linked List <ul><li>printf(&quot;%s&quot;,&quot;Want to add more nodesn&quot;); </li></ul><ul><li>scanf( &quot;%c&quot;, &ch ); </li></ul><ul><li>fflush( stdin ); </li></ul><ul><li>} /* end of while */ </li></ul><ul><li>} /* end of addnode( ) </li></ul>
    120. 120. Creating a Sorted Doubly Linked List <ul><li>struct marks_list * makenode() </li></ul><ul><li>{ </li></ul><ul><li>struct marks_list *new; </li></ul><ul><li>new=(struct marks_list *) malloc(sizeof(struct(marks_list)); </li></ul><ul><li>scanf(&quot;%d&quot;,&new->marks); </li></ul><ul><li>new->prior = NULL; </li></ul><ul><li>new->next = NULL; </li></ul><ul><li>return(new); </li></ul><ul><li>} </li></ul>
    121. 121. Creating a Sorted Linked List <ul><li>insert( ) </li></ul><ul><li>{ </li></ul><ul><li>struct marks_list *ptr, *prev; </li></ul><ul><li>for(ptr=start,prev=start;(ptr);prev=ptr,ptr=ptr->next) </li></ul><ul><li>{ </li></ul><ul><li>if (new->marks < start->marks) </li></ul><ul><li>{ </li></ul><ul><li>/* insertion at the beginning of a list */ </li></ul><ul><li>new->next = start; </li></ul><ul><li>new->prior = NULL; </li></ul><ul><li>start->prior = new; </li></ul><ul><li>last = start; </li></ul><ul><li>start = new; </li></ul><ul><li>} </li></ul>
    122. 122. Creating a Sorted Doubly Linked List <ul><li>/* insertion in the middle of a list */ </li></ul><ul><li>if(new->marks > ptr->marks) </li></ul><ul><li>{ </li></ul><ul><li>continue; </li></ul><ul><li>} </li></ul><ul><li>else </li></ul><ul><li>{ </li></ul><ul><li>prev->next = new; </li></ul><ul><li>new->prior = prev; </li></ul><ul><li>new->next = ptr; </li></ul><ul><li>ptr->prior = new; </li></ul><ul><li>} </li></ul><ul><li>} /* end of for loop */ </li></ul>
    123. 123. Creating a Sorted Linked List <ul><li>/* insertion at the end of the list */ </li></ul><ul><li>if (ptr = = null) </li></ul><ul><li>{ </li></ul><ul><li>prev->next = new; </li></ul><ul><li>new->prior = prev; </li></ul><ul><li>new->next = null; </li></ul><ul><li>last = new; </li></ul><ul><li>} </li></ul><ul><li>} /* end of insert */ </li></ul>
    124. 124. Searching a Value in a Doubly Linked List <ul><li>struct marks_list *search( int val) </li></ul><ul><li>{ </li></ul><ul><li>for( ptr = start; (ptr); ptr = ptr->next) </li></ul><ul><li>{ </li></ul><ul><li>if (val = = ptr-> marks) </li></ul><ul><li>return ptr; </li></ul><ul><li>} </li></ul><ul><li>struct marks_list *search( int val) </li></ul><ul><li>{ </li></ul><ul><li>for( ptr = last; (ptr); ptr = ptr->prior) </li></ul><ul><li>{ </li></ul><ul><li>if (val = = ptr-> marks) </li></ul><ul><li>return ptr; </li></ul><ul><li>} </li></ul>
    125. 125. Deleting a Node From a Doubly Linked List <ul><li>delete ( ) </li></ul><ul><li>{ </li></ul><ul><li>struct marks_list *ptr, *prev, *temp; </li></ul><ul><li>int score; </li></ul><ul><li>/* search the linked list for the value to be deleted */ </li></ul><ul><li>scanf(“%d”, &score); </li></ul><ul><li>fflush(stdin); </li></ul><ul><li>for (ptr = start, prev = start; (ptr); prev = ptr, ptr = ptr->next) </li></ul><ul><li>{ </li></ul>/* deletion of the first node in the list */ if (score = = start-> marks) { temp =start; start = start-> next; start->prior = null; free(temp); }
    126. 126. Deleting a Node From a Linked List <ul><li>/* deletion in the middle of a linked list */ </li></ul><ul><li>if (score = = ptr-> marks) </li></ul><ul><li>{ </li></ul><ul><li>prev-> next = ptr-> next; </li></ul><ul><li>ptr->next->prior = ptr->prior; </li></ul><ul><li>free(ptr); </li></ul><ul><li>} </li></ul>/* deletion at the end of the list */ if (ptr->next = = null) { temp = ptr; prev->next = null; last = ptr->prior; } } }
    127. 127. Traversal of a Doubly Linked List <ul><li>/* forward traversal of a doubly linked list */ </li></ul><ul><li>for( ptr = start; (ptr); ptr = ptr->next) </li></ul><ul><li>{ </li></ul><ul><li>printf(“%d”, ptr->marks); </li></ul><ul><li>} </li></ul><ul><li>/* reverse traversal of a doubly linked list */ </li></ul><ul><li>for( ptr = last; (ptr); ptr = ptr->prior) </li></ul><ul><li>{ </li></ul><ul><li>printf(“%d”, ptr->marks); </li></ul><ul><li>} </li></ul>
    128. 128. Summary <ul><li>In this session, you learnt to: </li></ul><ul><li>Describe the need for, and advantages of a doubly linked list </li></ul><ul><li>Write code to: </li></ul><ul><ul><li>Create a sorted doubly linked list, </li></ul></ul><ul><ul><li>Insert nodes into a sorted doubly linked list </li></ul></ul><ul><ul><li>Traverse a doubly linked list </li></ul></ul><ul><ul><li>Delete nodes from a doubly linked list </li></ul></ul>
    129. 129. <ul><li>Binary Trees </li></ul>
    130. 130. Objectives <ul><li>At the end of this lesson, you will be able to: </li></ul><ul><li>Define a binary tree </li></ul><ul><li>Describe the terminologies associated with a binary tree </li></ul><ul><li>Use a dynamically allocated data structure to represent a binary tree </li></ul><ul><li>Traverse, Add and delete nodes from a binary tree </li></ul><ul><li>Understand the Huffman Algorithm </li></ul><ul><li>Describe the types of Trees </li></ul>
    131. 131. Trees <ul><li>Compared to linked lists that are linear data structures, trees are non-linear data structures . </li></ul><ul><li>In a linked list, each node has a link which points to another node. </li></ul><ul><li>In a tree structure, however, each node may point to several nodes, which may in turn point to several other nodes. </li></ul><ul><li>Thus, a tree is a very flexible and a powerful data structure that can be used for a wide variety of applications. </li></ul>
    132. 132. Trees Rajeev Ravi Vijay Anjali Ramesh Suresh Sukesh Arvind
    133. 133. Trees <ul><li>A tree consists of a collection of nodes that are connected to each other. </li></ul><ul><li>A tree contains a unique first element known as the root, which is shown at the top of the tree structure. </li></ul><ul><li>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. </li></ul>
    134. 134. Trees <ul><li>The root is the only node in the tree that does not have a parent. </li></ul><ul><li>All other nodes in the tree have exactly one parent. </li></ul><ul><li>There are nodes in the tree that do not have any children. Such nodes are called leaf nodes. </li></ul><ul><li>Nodes are siblings if they have the same parent. </li></ul>
    135. 135. Trees <ul><li>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. </li></ul><ul><li>The root is an ancestor of every other node in the tree. </li></ul><ul><li>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. </li></ul><ul><li>You may note that all the nodes in the tree are descendants of the root node. </li></ul>
    136. 136. Tree <ul><li>An important feature of a tree is that there is a single unique path from the root to any particular node. </li></ul><ul><li>The length of the longest path from the root to any node is known as the depth of the tree. </li></ul><ul><li>The root is at level 0 and the level of any node in the tree is one more than the level of its parent. </li></ul><ul><li>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. </li></ul>
    137. 137. Binary Tree <ul><li>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. </li></ul><ul><li>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. </li></ul><ul><li>You therefore have a left subtree and a right subtree under the root node. </li></ul>
    138. 138. Binary Tree <ul><li>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. </li></ul><ul><li>This is also called as a balanced binary tree. </li></ul><ul><li>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. </li></ul><ul><li>The following is an example of a balanced binary search tree. </li></ul>
    139. 139. Balanced Binary Tree 4 6 5 7 3 1 2 N N N N N N N N
    140. 140. Data Structure Representation of a Binary Trees <ul><li>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. </li></ul><ul><li>A binary tree can also be looked at as a special case of a doubly linked list that is traversed hierarchically. </li></ul><ul><li>The following is the structure declaration for a tree node: </li></ul>
    141. 141. Data Structure Representation of a Binary Trees <ul><li>struct btreenode </li></ul><ul><li>{ </li></ul><ul><li>int info; </li></ul><ul><li>struct btreenode *left; </li></ul><ul><li>struct btreenode *right; </li></ul><ul><li>}; </li></ul>
    142. 142. Traversing a Binary Tree <ul><li>Traversing a binary tree entails visiting each node in the tree exactly once. </li></ul><ul><li>Binary tree traversal is useful in many applications, especially those involving an indexed search. </li></ul><ul><li>Nodes of a binary search tree are traversed hierarchically. </li></ul><ul><li>The methods of traversing a binary search tree differ primarily in the order in which they visit the nodes. </li></ul>
    143. 143. Traversing a Binary Tree <ul><li>At a given node, there are three things to do in some order. They are: </li></ul><ul><ul><li>To visit the node itself </li></ul></ul><ul><ul><li>To traverse its left subtree </li></ul></ul><ul><ul><li>To traverse its right subtree </li></ul></ul><ul><li>We can traverse the node before traversing either subtree. </li></ul><ul><li>Or, we can traverse the node between the subtrees. </li></ul><ul><li>Or, we can traverse the node after traversing both subtrees. </li></ul>
    144. 144. Traversing a Binary Tree <ul><li>If we designate the task of visiting the root as R’, 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: </li></ul><ul><ul><li>R’LR – Preorder </li></ul></ul><ul><ul><li>LRR’ – Postorder </li></ul></ul><ul><ul><li>LR’R – Inorder </li></ul></ul>
    145. 145. Traversing a Binary Tree <ul><li>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. </li></ul><ul><li>Recall that a binary tree is recursive in that each subtree is really a binary tree itself. </li></ul><ul><li>Thus traversing a binary tree involves visiting the root node, and traversing its left and right subtrees. </li></ul><ul><li>The only difference among the methods is the order in which these three operations are performed. </li></ul>
    146. 146. Traversing a Binary Tree <ul><li>Depending on the position at which the given node or the root is visited, the name is given. </li></ul><ul><li>If the root is visited before traversing the subtree, it is called the preorder traversal. </li></ul><ul><li>If the root is visited after traversing the subtrees, it is called postorder traversal. </li></ul><ul><li>If the root is visited in between the subtrees, it is called the inorder traversal. </li></ul>
    147. 147. Traversing a Binary Tree A B C D E
    148. 148. Preorder Traversal <ul><li>When we traverse the tree in preorder, the root node is visited first. So, the node containing A is traversed first. </li></ul><ul><li>Next, we traverse the left subtree. This subtree must again be traversed using the preorder method. </li></ul><ul><li>Therefore, we visit the root of the subtree containing B and then traverse its left subtree. </li></ul><ul><li>The left subtree of B is empty, so its traversal does nothing. Next we traverse the right subtree that has root labeled C. </li></ul>
    149. 149. Preorder Traversal <ul><li>Then, we traverse the left and right subtrees of C getting D and E as a result. </li></ul><ul><li>Now, we have traversed the left subtree of the root containing A completely, so we move to traverse the right subtree of A. </li></ul><ul><li>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 . </li></ul>
    150. 150. Inorder Traversal <ul><li>For inorder traversal, we begin with the left subtree rooted at B of the root. </li></ul><ul><li>Before we visit the root of the left subtree, we must visit its left subtree, which is empty. </li></ul><ul><li>Hence the root of the left subtree rooted at B is visited first. Next, the right subtree of this node is traversed inorder. </li></ul>
    151. 151. Inorder Traversal <ul><li>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. </li></ul><ul><li>After completing the left subtree of root A, we must visit the root A, and then traverse its right subtree, which is empty. </li></ul><ul><li>Thus, the complete inorder traversal of the binary tree results in values BDCEA . </li></ul>
    152. 152. Postorder Traversal <ul><li>For postorder traversal, we must traverse both the left and the right subtrees of each node before visiting the node itself. </li></ul><ul><li>Hence, we traverse the left subtree in postorder yielding values D, E, C and B. </li></ul><ul><li>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. </li></ul><ul><li>Thus, the complete postorder traversal of the tree results in DECBA . </li></ul>
    153. 153. Code - Preorder Traversal <ul><li>void preorder (p) </li></ul><ul><li>struct btreenode *p; </li></ul><ul><li>{ </li></ul><ul><li>/* Checking for an empty tree */ </li></ul><ul><li>if ( p != null) </li></ul><ul><li>{ </li></ul><ul><li>/* print the value of the root node */ </li></ul><ul><li>printf(“%d”, p->info); </li></ul><ul><li>/* traverse its left subtree */ </li></ul><ul><li>preorder(p->left); </li></ul><ul><li>/* traverse its right subtree */ </li></ul><ul><li>preorder(p->right); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
    154. 154. Code – Inorder Traversal <ul><li>void inorder(p) </li></ul><ul><li>struct btreenode *p; </li></ul><ul><li>{ </li></ul><ul><li>/* checking for an empty tree */ </li></ul><ul><li>if (p != null) </li></ul><ul><li>{ </li></ul><ul><li>/* traverse the left subtree inorder */ </li></ul><ul><li>inorder(p->left); </li></ul><ul><li>/* print the value of the root node */ </li></ul><ul><li>printf(“%d”, p->info); </li></ul><ul><li>/*traverse the right subtree inorder */ </li></ul><ul><li>inorder(p->right); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
    155. 155. Code – Postorder Traversal <ul><li>void postorder(p) </li></ul><ul><li>struct btreenode *p; </li></ul><ul><li>{ </li></ul><ul><li>/* checking for an empty tree */ </li></ul><ul><li>if (p != null) </li></ul><ul><li>{ </li></ul><ul><li>/* traverse the left subtree */ </li></ul><ul><li>postorder(p->left); </li></ul><ul><li>/* traverse the right subtree */ </li></ul><ul><li>postorder(p->right); </li></ul><ul><li>/* print the value of the root node */ </li></ul><ul><li>printf(“%d”, p->info); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
    156. 156. Accessing Values From a Binary Search Tree Using Inorder Traversal <ul><li>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. </li></ul><ul><li>The same rule applies to all the subtrees until they have only one key. </li></ul><ul><li>Therefore, given the entries, we can build them into a binary search tree and use inorder traversal to get them in sorted order. </li></ul>
    157. 157. Insertion into a Tree <ul><li>Another important operation is to create and maintain a binary search tree. </li></ul><ul><li>While inserting any node, we have to take care the resulting tree satisfies the properties of a binary search tree. </li></ul><ul><li>A new node will always be inserted at its proper position in the binary search tree as a leaf. </li></ul><ul><li>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. </li></ul>
    158. 158. Insertion into a Tree <ul><li>First of all, you must initialize the tree. </li></ul><ul><li>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. </li></ul>10 Root
    159. 159. Insertion into a Tree <ul><li>Since 15 is greater than 10, it must be inserted as the right child of the root as shown in the following figure. </li></ul>10 Root 15
    160. 160. Insertion into a Tree <ul><li>Now 12 is larger than the root; it must go to the right subtree of the root. </li></ul><ul><li>Further, since it is smaller than 15, it must be inserted as the left child of the root as shown below. </li></ul>10 Root 15 12
    161. 161. Insertion into a Tree <ul><li>Next, 7 is smaller than the root. Therefore, it must be inserted as the left child of the root as shown in the following figure. </li></ul>10 15 12 7
    162. 162. Insertion into a Tree <ul><li>Similarly, 8, 18, 6 and 20 are inserted at the proper place as shown in the following figures. </li></ul>10 15 12 7 8 10 15 12 7 8 18
    163. 163. Insertion into a Tree 10 15 12 7 8 18 6 10 15 12 7 8 18 6 20
    164. 164. Insertion into a Tree <ul><li>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. </li></ul><ul><li>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. </li></ul><ul><li>To find the insertion place for the new value, say 17, we initialize a temporary pointer p, which points to the root node. </li></ul>
    165. 165. Insertion into a Tree <ul><li>We can change the contents of p to either move left or right through the tree depending on the value to be inserted. </li></ul><ul><li>When p becomes null, we know that we have found the insertion place as in the following figure. </li></ul>
    166. 166. Insertion into a Tree 10 15 12 7 8 18 6 20 Root p
    167. 167. Insertion into a Tree <ul><li>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. </li></ul><ul><li>From the following figure, p becomes null when we have found that 17 will be inserted at the left of 18. </li></ul>
    168. 168. Insertion into a Tree 10 15 12 7 8 18 6 20 Root p null
    169. 169. Insertion into a Tree <ul><li>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. </li></ul><ul><li>For this, you need a pointer that points to the node containing 18 when p becomes null. </li></ul><ul><li>To achieve this, you need to have another pointer (trail) that must follow p as p moves through the tree. </li></ul>
    170. 170. Insertion into a Tree <ul><li>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). </li></ul><ul><li>Once you know the insertion place, you must adjust the pointers of the new node. </li></ul><ul><li>At this point, you only have a pointer to the leaf node to which the new node is to be linked. </li></ul><ul><li>You must determine whether the insertion is to be done at the left subtree or the right subtree of the leaf node. </li></ul>
    171. 171. Insertion into a Tree <ul><li>To do that, you must compare the value to be inserted with the value in the leaf node. </li></ul><ul><li>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. </li></ul>
    172. 172. Creating a Tree – A Special Case of Insertion <ul><li>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. </li></ul><ul><li>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. </li></ul><ul><li>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. </li></ul>
    173. 173. Code Implementation For Insertion Into a Tree <ul><li>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). </li></ul><ul><li>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. </li></ul>
    174. 174. Code Implementation For Insertion Into a Tree <ul><li>tree insert(s,x) </li></ul><ul><li>int x; </li></ul><ul><li>tree *s; </li></ul><ul><li>{ </li></ul><ul><li>tree *trail, *p, *q; </li></ul><ul><li>q = (struct tree *) malloc (sizeof(tree)); </li></ul><ul><li>q->info = x; </li></ul><ul><li>q->left = null; </li></ul><ul><li>q->right = null; </li></ul><ul><li>p = s; </li></ul><ul><li>trail = null; </li></ul>
    175. 175. Code Implementation For Insertion Into a Tree <ul><li>while (p != null) </li></ul><ul><li>{ </li></ul><ul><li>trail = p; </li></ul><ul><li>if (x < p->info) </li></ul><ul><li>{ </li></ul><ul><li>p = p->left; </li></ul><ul><li>} </li></ul><ul><li>else </li></ul><ul><li>{ </li></ul><ul><li>p = p->right; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
    176. 176. Code Implementation For Insertion Into a Tree <ul><li>/*insertion into an empty tree; a special case of insertion */ </li></ul><ul><li>if (trail == null) </li></ul><ul><li>{ </li></ul><ul><li>s = q; </li></ul><ul><li>return (s); </li></ul><ul><li>} </li></ul><ul><li>if(x < trail->info) </li></ul><ul><li>{ </li></ul><ul><li>trail->left = q; </li></ul><ul><li>} </li></ul><ul><li>else </li></ul><ul><li>{ </li></ul><ul><li>trail->right = q; </li></ul><ul><li>} </li></ul><ul><li>return (s); </li></ul><ul><li>} </li></ul>
    177. 177. Code Implementation For Insertion Into a Tree Using Recursion <ul><li>You have seen that to insert a node, you must compare x with root->info. </li></ul><ul><li>If x is less than root->info, then x must be inserted into the left subtree. </li></ul><ul><li>Otherwise, x must be inserted into the right subtree. </li></ul><ul><li>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. </li></ul>
    178. 178. Code Implementation For Insertion Into a Tree Using Recursion <ul><li>The base case is inserting a node into an empty tree. </li></ul><ul><li>You can write a recursive routine (rinsert) to insert a node recursively as follows: </li></ul><ul><li>tree rinsert (s,x) </li></ul><ul><li>tree *s; </li></ul><ul><li>int x; </li></ul><ul><li>{ </li></ul><ul><li>/* insertion into an empty tree; a special case of insertion */ </li></ul><ul><li>if (!s) </li></ul><ul><li>{ </li></ul><ul><li>s=(struct tree*) malloc (sizeof(struct tree)); </li></ul>
    179. 179. Code Implementation For Insertion Into a Tree Using Recursion <ul><li>s->info = x; </li></ul><ul><li>s->left = null; </li></ul><ul><li>s->right = null; </li></ul><ul><li>return (s); </li></ul><ul><li>} </li></ul><ul><li>if (x < s->info) </li></ul><ul><li>s->left = rinsert(x, s->left); </li></ul><ul><li>else </li></ul><ul><li>if (x > s->info) </li></ul><ul><li>s->right = rinsert(x, s->right); </li></ul><ul><li>return (s); </li></ul><ul><li>} </li></ul>
    180. 180. Circumstances When a Binary Tree Degenerates into a Linked List <ul><li>The shape of a binary tree is determined by the order in which the nodes are inserted. </li></ul><ul><li>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: </li></ul><ul><ul><li>Input values: 10, 15, 12, 7, 8, 18, 6, 20 </li></ul></ul>
    181. 181. Circumstances When a Binary Tree Degenerates into a Linked List 10 15 12 7 8 18 6 20 Root
    182. 182. Circumstances When a Binary Tree Degenerates into a Linked List <ul><li>If the same input is given in the sorted order as </li></ul><ul><li>6, 7, 8, 10, 12, 15, 18, 20, you will construct a lopsided tree with only right subtrees starting from the root. </li></ul><ul><li>Such a tree will be conspicuous by the absence of its left subtree from the top. </li></ul>
    183. 183. Circumstances When a Binary Tree Degenerates into a Linked List A Lopsided Binary Tree With Only Right Subtrees 6 7 8 10 12 15 18 20
    184. 184. Circumstances When a Binary Tree Degenerates into a Linked List <ul><li>However if you reverse the input as </li></ul><ul><li>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. </li></ul><ul><li>Such a tree will be conspicuous by the absence of its right subtree from the top. </li></ul>
    185. 185. Circumstances When a Binary Tree Degenerates into a Linked List A Lopsided Binary Tree With Only Left Subtrees 20 18 15 12 10 8 7 6
    186. 186. Deletion from a Binary Search Tree <ul><li>An important function for maintaining a binary search tree is to delete a specific node from the tree. </li></ul><ul><li>The method to delete a node depends on the specific position of the node in the tree. </li></ul><ul><li>The algorithm to delete a node can be subdivided into different cases. </li></ul>
    187. 187. Case I – Deletion Of The Leaf Node <ul><li>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. </li></ul><ul><li>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. </li></ul><ul><li>The following diagram illustrates this. </li></ul>
    188. 188. Case I – Deletion Of The Leaf Node 2 p 2 null 3 3 1
    189. 189. Case II – Deletion Of a Node With a Single Child <ul><li>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. </li></ul><ul><li>Because if you do so, you will lose all of the descendants of the node that you are deleting from the tree. </li></ul><ul><li>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. </li></ul>
    190. 190. Case II – Deletion Of a Node With a Single Child 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). Node to be deleted 5 4 3 2 Node to be deleted 5 4 3 2
    191. 191. Case III – Deletion Of a Node With Two Child Nodes <ul><li>Complications arise when you have to delete a node with two children. </li></ul><ul><li>There is no way you can make the parent of the deleted node to point to both of the children of the deleted node. </li></ul><ul><li>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. </li></ul>
    192. 192. Case III – Deletion Of a Node With Two Child Nodes <ul><li>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. </li></ul><ul><li>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. </li></ul><ul><li>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. </li></ul>
    193. 193. Case III – Deletion Of a Node With Two Child Nodes y Delete node x r q x t s u z Before Deletion of Node x r q t y s u z After Deletion of Node x
    194. 194. Code Implementation for Node Deletion for Cases I, II & III <ul><li>void delete (p) </li></ul><ul><li>struct tree *p </li></ul><ul><li>{ </li></ul><ul><li>struct tree *temp </li></ul><ul><li>if (p == null) </li></ul><ul><li>printf(“Trying to delete a non-existent node”); </li></ul><ul><li>else if (p->left == null) </li></ul><ul><li>{ </li></ul><ul><li>temp=p; </li></ul>p = p->right; free(temp); } else if (p->right == null) { temp = p; p = p->left; free (temp); }
    195. 195. Code Implementation for Node Deletion for Cases I, II & III <ul><li>else if(p->left != null && p->right!= null) </li></ul><ul><li>{ </li></ul><ul><li>temp = p->right; </li></ul><ul><li>while (temp->left != null) </li></ul><ul><li>{ </li></ul><ul><li>temp = temp->left; </li></ul><ul><li>} </li></ul><ul><li>temp->left = p->left; </li></ul><ul><li>temp = p; </li></ul><ul><li>p = p->right; </li></ul><ul><li>free (Temp); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
    196. 196. Code Implementation for Node Deletion for Cases I, II & III <ul><li>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. </li></ul><ul><li>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. </li></ul>
    197. 197. Search The Tree <ul><li>To search a tree, you employ a traversal pointer p, and set it equal to the root of the tree. </li></ul><ul><li>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. </li></ul><ul><li>If x is less than p->info, you search in the left subtree of p. </li></ul><ul><li>Otherwise, you search in the right subtree of p by making p equal to p->right. </li></ul>
    198. 198. Search the Tree <ul><li>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: </li></ul><ul><li>search (p,x) </li></ul><ul><li>int x; </li></ul><ul><li>struct tree *p; </li></ul><ul><li>{ </li></ul><ul><li>p = root; </li></ul><ul><li>while (p != null && p->info != x) </li></ul><ul><li>{ </li></ul><ul><li>if (p->info > x) </li></ul><ul><li>p = p->left; </li></ul><ul><li>else </li></ul><ul><li>p = p->right; </li></ul><ul><li>} return (p); </li></ul><ul><li>} </li></ul>
    199. 199. GRAPHS
    200. 200. SEARCHING
    201. 201. Searching <ul><li>Sequential Searches </li></ul><ul><ul><li>Time is proportional to n </li></ul></ul><ul><ul><li>We call this time complexity O(n) </li></ul></ul><ul><ul><li>Pronounce this “ big oh ” of n </li></ul></ul><ul><ul><li>Both arrays (unsorted) and linked lists </li></ul></ul><ul><li>Binary search </li></ul><ul><ul><li>Sorted array </li></ul></ul><ul><ul><li>Time proportional to log 2 n </li></ul></ul><ul><ul><li>Time complexity O( log n) </li></ul></ul>
    202. 202. Eliminative or a Binary Search <ul><li>The mode of accessing data in a linked list is linear. </li></ul><ul><li>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. </li></ul><ul><li>Therefore, search through a linked list is always linear . </li></ul>
    203. 203. Eliminative or a Binary Search <ul><li>A linear search is fine if the nodes to be searched in a linked list are small in number. </li></ul><ul><li>But the linear search becomes ineffective as the number of nodes in a linked list increase. </li></ul><ul><li>The search time increases in direct proportion with the size of the linked list. </li></ul><ul><li>It becomes imperative to have better searching mechanisms than a linear search. </li></ul>
    204. 204. Eliminative or a Binary Search <ul><li>You will now be exposed to a game that will highlight a new mechanism of searching. </li></ul>Coin Search in a Group A B C D E F G H I J K L M N O P Q R X YES NO
    205. 205. Eliminative or a Binary Search <ul><li>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. </li></ul><ul><li>The challenge facing X, the protagonist, is to find the person with the coin in the least number of searches. </li></ul>
    206. 206. Searching - Binary search <ul><li>Creating the sorted array </li></ul><ul><ul><li>AddToCollection </li></ul></ul><ul><ul><ul><li>adds each item in correct place </li></ul></ul></ul><ul><ul><ul><li>Find position c 1 log 2 n </li></ul></ul></ul><ul><ul><ul><li>Shuffle down c 2 n </li></ul></ul></ul><ul><ul><ul><li>Overall c 1 log 2 n + c 2 n </li></ul></ul></ul><ul><ul><ul><li>or c 2 n </li></ul></ul></ul><ul><ul><li>Each add to the sorted array is O(n) </li></ul></ul><ul><li>Can we maintain a sorted array with cheaper insertions? </li></ul>Dominant term
    207. 207. Employing the Linear Search <ul><li>X, familiar with a linear search, starts using it to search for the coin among the group. </li></ul><ul><li>Let us assume the worst-case scenario of the coin being with R. </li></ul><ul><li>If X was to start the search with A and progress linearly through B, C, ….M and finally to R, he would have taken a minimum of 18 searches (R being the 18th person searched in sequence to find the coin. </li></ul>
    208. 208. Employing the Linear Search <ul><li>As you can see, this kind of search is not very efficient especially when the number of elements to be searched is high. </li></ul><ul><li>An eliminative search, also called a binary search, provides a far better searching mechanism. </li></ul>
    209. 209. Employing the Eliminative or The Binary Search <ul><li>Assume that X can pose intelligent questions to the audience to cut down on the number of searches. </li></ul><ul><li>A valid question that he could pose is “Which side of the audience has the coin?” </li></ul><ul><li>‘ A’ in the audience to the left of him says that his side of the audience does not have the coin. </li></ul><ul><li>So X can completely do away with searching the audience to his left. That saves him 9 searches. </li></ul>
    210. 210. Employing the Eliminative or The Binary Search <ul><li>X has now got to search the audience to the right of him for searching out the coin. </li></ul><ul><li>Here too, he can split the audience into half by standing adjacent to the middle row, and posing the same question that he asked earlier “Which side of the audience has the coin?” </li></ul><ul><li>‘ M’ in the middle row replies that the coin is with the audience to the left of him. </li></ul>
    211. 211. Employing the Eliminative or The Binary Search <ul><li>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. </li></ul>J K L M N O P Q R X 6 searches saved NO YES
    212. 212. Employing the Eliminative or The Binary Search <ul><li>X is now left with row to the right of the middle row containing P, Q, and R that has to be searched. </li></ul><ul><li>Here too, he can position himself right at the middle of the row adjacent to Q and pose the same question, </li></ul><ul><li>“ Which side of the audience has the coin?” ‘Q’ replies that the coin is to his left. </li></ul>P Q R X NO YES
    213. 213. Eliminative or Binary Search <ul><li>That completes our eliminative or binary search. </li></ul><ul><li>It is called a binary search because at each stage of the search, the search is cut by more than half. </li></ul><ul><li>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. </li></ul>
    214. 214. Binary Vs. Linear Search <ul><li>As the name suggests, balanced binary search trees are very useful for searching an element just as with a binary search. </li></ul><ul><li>If we use linked lists for searching, we have to move through the list linearly, one node at a time. </li></ul><ul><li>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. </li></ul>
    215. 215. Linear Search in a Linked List 100 1 120 2 140 3 160 4 180 5 200 6 220 7 null
    216. 216. Binary Search in a Binary Search Tree 4 6 5 7 3 1 2 N N N N N N N N 7 Search Value 7 Search Value 7 Search Value
    217. 217. The Essence of a Binary Search <ul><li>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. </li></ul><ul><li>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. </li></ul><ul><li>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. </li></ul>
    218. 218. The Essence of a Binary Search <ul><li>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). </li></ul><ul><li>Thus a search that is hierarchical, eliminative and binary in nature is far efficient when compared to a linear search. </li></ul>
    219. 219. Searching - Re-visited <ul><li>Binary tree O(log n) if it stays balanced </li></ul><ul><ul><li>Simple binary tree good for static collections </li></ul></ul><ul><ul><li>Low (preferably zero) frequency of insertions/deletions </li></ul></ul><ul><li>but my collection keeps changing! </li></ul><ul><ul><li>It’s dynamic </li></ul></ul><ul><ul><li>Need to keep the tree balanced </li></ul></ul><ul><li>First, examine some basic tree operations </li></ul><ul><ul><li>Useful in several ways! </li></ul></ul>
    220. 220. Tree Traversal <ul><li>Traversal = visiting every node of a tree </li></ul><ul><li>Three basic alternatives </li></ul><ul><li>Pre-order </li></ul><ul><ul><li>Root </li></ul></ul><ul><ul><li>Left sub-tree </li></ul></ul><ul><ul><li>Right sub-tree </li></ul></ul>x A + x + B C x D E F L R L L R     
    221. 221. Tree Traversal <ul><li>Traversal = visiting every node of a tree </li></ul><ul><li>Three basic alternatives </li></ul><ul><li>In-order </li></ul><ul><ul><li>Left sub-tree </li></ul></ul><ul><ul><li>Root </li></ul></ul><ul><ul><li>Right sub-tree </li></ul></ul>          11 A x B + C x D x E + F L R L
    222. 222. Tree Traversal <ul><li>Traversal = visiting every node of a tree </li></ul><ul><li>Three basic alternatives </li></ul><ul><li>Post-order </li></ul><ul><ul><li>Left sub-tree </li></ul></ul><ul><ul><li>Right sub-tree </li></ul></ul><ul><ul><li>Root </li></ul></ul>          11 A B C + D E x x F + x L R L
    223. 223. Tree Traversal <ul><li>Post-order </li></ul><ul><ul><li>Left sub-tree </li></ul></ul><ul><ul><li>Right sub-tree </li></ul></ul><ul><ul><li>Root </li></ul></ul><ul><li>Reverse-Polish </li></ul><ul><li>Normal algebraic form </li></ul><ul><ul><li>which traversal? </li></ul></ul>(A (((BC+)(DE x ) x ) F +) x )           (A x(((B+C)(D x E))+F)) 11
    224. 224. Trees - Searching <ul><li>Binary search tree </li></ul><ul><ul><li>Produces a sorted list by in-order traversal </li></ul></ul><ul><li>In order: A D E G H K L M N O P T V </li></ul>
    225. 225. Trees - Searching <ul><li>Binary search tree </li></ul><ul><ul><li>Preserving the order </li></ul></ul><ul><ul><li>Observe that this transformation preserves the search tree </li></ul></ul>
    226. 226. Trees - Searching <ul><li>Binary search tree </li></ul><ul><ul><li>Preserving the order </li></ul></ul><ul><ul><li>Observe that this transformation preserves the search tree </li></ul></ul><ul><li>We’ve performed a rotation of the sub-tree about the T and O nodes </li></ul>
    227. 227. Trees - Rotations <ul><li>Binary search tree </li></ul><ul><ul><li>Rotations can be either left- or right-rotation s </li></ul></ul><ul><ul><li>For both trees: the inorder traversal is </li></ul></ul><ul><ul><ul><ul><ul><li>A x B y C </li></ul></ul></ul></ul></ul>
    228. 228. Trees - Rotations <ul><li>Binary search tree </li></ul><ul><ul><li>Rotations can be either left- or right-rotation s </li></ul></ul><ul><ul><li>Note that in this rotation, it was necessary to move B from the right child of x to the left child of y </li></ul></ul>
    229. 229. Trees - Red-Black Trees <ul><li>A Red -Black Tree </li></ul><ul><ul><li>Binary search tree </li></ul></ul><ul><ul><li>Each node is “coloured” red or black </li></ul></ul><ul><ul><li>An ordinary binary search tree with node colourings to make a red-black tree </li></ul></ul>
    230. 230. Trees - Red-Black Trees <ul><li>A Red -Black Tree </li></ul><ul><ul><li>Every node is RED or BLACK </li></ul></ul><ul><ul><li>Every leaf is BLACK </li></ul></ul>When you examine rb-tree code, you will see sentinel nodes (black) added as the leaves. They contain no data. Sentinel nodes (black)
    231. 231. Trees - Red-Black Trees <ul><li>A Red -Black Tree </li></ul><ul><ul><li>Every node is RED or BLACK </li></ul></ul><ul><ul><li>Every leaf is BLACK </li></ul></ul><ul><ul><li>If a node is RED , then both children are BLACK </li></ul></ul>This implies that no path may have two adjacent RED nodes. (But any number of BLACK nodes may be adjacent.)
    232. 232. Trees - Red-Black Trees <ul><li>A Red -Black Tree </li></ul><ul><ul><li>Every node is RED or BLACK </li></ul></ul><ul><ul><li>Every leaf is BLACK </li></ul></ul><ul><ul><li>If a node is RED , then both children are BLACK </li></ul></ul><ul><ul><li>Every path from a node to a leaf contains the same number of BLACK nodes </li></ul></ul>From the root, there are 3 BLACK nodes on every path
    233. 233. Trees - Red-Black Trees <ul><li>A Red -Black Tree </li></ul><ul><ul><li>Every node is RED or BLACK </li></ul></ul><ul><ul><li>Every leaf is BLACK </li></ul></ul><ul><ul><li>If a node is RED , then both children are BLACK </li></ul></ul><ul><ul><li>Every path from a node to a leaf contains the same number of BLACK nodes </li></ul></ul>The length of this path is the black height of the tree
    234. 234. Trees - Red-Black Trees <ul><li>Lemma </li></ul><ul><ul><li>A RB-Tree with n nodes has </li></ul></ul><ul><ul><ul><li>height  2 log (n+1) </li></ul></ul></ul><ul><ul><li>Proof .. See Cormen </li></ul></ul><ul><li>Essentially, height  2 black height </li></ul><ul><li>Search time </li></ul><ul><ul><ul><li>O( log n ) </li></ul></ul></ul>
    235. 235. Trees - Red-Black Trees <ul><li>Data structure </li></ul><ul><ul><li>As we’ll see, nodes in red-black trees need to know their parents, </li></ul></ul><ul><ul><li>so we need this data structure </li></ul></ul>Same as a binary tree with these two attributes added struct t_red_black_node { enum { red, black } colour; void *item; struct t_red_black_node *left, *right, *parent; }
    236. 236. Trees - Insertion <ul><li>Insertion of a new node </li></ul><ul><ul><li>Requires a re-balance of the tree </li></ul></ul>rb_insert( Tree T, node x ) { /* Insert in the tree in the usual way */ tree_insert( T, x ); /* Now restore the red-black property */ x->colour = red; while ( (x != T->root) && (x->parent->colour == red) ) { if ( x->parent == x->parent->parent->left ) { /* If x's parent is a left, y is x's right 'uncle' */ y = x->parent->parent->right; if ( y->colour == red ) { /* case 1 - change the colours */ x->parent->colour = black; y->colour = black; x->parent->parent->colour = red; /* Move x up the tree */ x = x->parent->parent; Label the current node x Insert node 4 Mark it red
    237. 237. Trees - Insertion rb_insert( Tree T, node x ) { /* Insert in the tree in the usual way */ tree_insert( T, x ); /* Now restore the red-black property */ x->colour = red; while ( (x != T->root) && (x->parent->colour == red) ) { if ( x->parent == x->parent->parent->left ) { /* If x's parent is a left, y is x's right 'uncle' */ y = x->parent->parent->right; if ( y->colour == red ) { /* case 1 - change the colours */ x->parent->colour = black; y->colour = black; x->parent->parent->colour = red; /* Move x up the tree */ x = x->parent->parent; While we haven’t reached the root and x’s parent is red x->parent
    238. 238. Trees - Insertion rb_insert( Tree T, node x ) { /* Insert in the tree in the usual way */ tree_insert( T, x ); /* Now restore the red-black property */ x->colour = red; while ( (x != T->root) && (x->parent->colour == red) ) { if ( x->parent == x->parent->parent->left ) { /* If x's parent is a left, y is x's right 'uncle' */ y = x->parent->parent->right; if ( y->colour == red ) { /* case 1 - change the colours */ x->parent->colour = black; y->colour = black; x->parent->parent->colour = red; /* Move x up the tree */ x = x->parent->parent; If x is to the left of it’s granparent x->parent x->parent->parent
    239. 239. Trees - Insertion /* Now restore the red-black property */ x->colour = red; while ( (x != T->root) && (x->parent->colour == red) ) { if ( x->parent == x->parent->parent->left ) { /* If x's parent is a left, y is x's right 'uncle' */ y = x->parent->parent->right; if ( y->colour == red ) { /* case 1 - change the colours */ x->parent->colour = black; y->colour = black; x->parent->parent->colour = red; /* Move x up the tree */ x = x->parent->parent; y is x’s right uncle x->parent x->parent->parent right “uncle”
    240. 240. Trees - Insertion while ( (x != T->root) && (x->parent->colour == red) ) { if ( x->parent == x->parent->parent->left ) { /* If x's parent is a left, y is x's right 'uncle' */ y = x->parent->parent->right; if ( y->colour == red ) { /* case 1 - change the colours */ x->parent->colour = black; y->colour = black; x->parent->parent->colour = red; /* Move x up the tree */ x = x->parent->parent; x->parent x->parent->parent right “uncle” If the uncle is red, change the colours of y, the grand-parent and the parent
    241. 241. Trees - Insertion while ( (x != T->root) && (x->parent->colour == red) ) { if ( x->parent == x->parent->parent->left ) { /* If x's parent is a left, y is

    ×