1 / 56

Abstract Data Structures

Abstract Data Structures. Self-referential data structures, dynamic memory allocation, linked lists, stacks, queues, trees …. Outline. Self-referential data structures Statically versus Dynamically allocated memory Concept of linked structures Linked lists Stacks and Queues

edena
Download Presentation

Abstract Data Structures

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Abstract Data Structures Self-referential data structures, dynamic memory allocation, linked lists, stacks, queues, trees …

  2. Outline • Self-referential data structures • Statically versus Dynamically allocated memory • Concept of linked structures • Linked lists • Stacks and Queues • Trees and advanced data structures

  3. Self-referential data structures • In previous lectures on struct’s we introduced the notion of self-referential data structures • These are struct’s that contain a pointer sub-field that is intended to point at a struct of the same type definition • Example: structNodeStruct { int ID ; char Name[50] ; double Score ; structNodeStruct * NextPtr ; } ; typedefstructNodeStructNode_t ; Node_t Node = { 0, “”, 0.0, NULL }, Node2 ; Node_t * NodePtr = &Node ; Node.NextPtr = &Node2 ; // This way … NodePtr->NextPtr = &Node2 ; // … or that way!

  4. Statically vsDynamically allocated memory • C provides for support of a logical (ie. conceptual) model in which user allocated memory (for a given program) is divided into several distinct blocks • These include a block for code, for static data, for stack data, for buffers (I/O) and for dynamical memory allocation (on the Heap) • When programmers write programs, they utilize names to declare variables and data structures so that they can refer to those memory locations within their code • These named variables refer to a statically assigned Namespace (once compiled, names and logical locations do not change)

  5. Statically vsDynamically allocated memory • We are now going to consider dynamically allocated memory and techniques for exploiting it • This is done at execution time, hence we cannot create variable names to refer to locations – we must use pointers instead • We focus on malloc(), free() and sizeof– all three are important! • Example: • NodePtr = malloc( sizeof( Node_t ) ) ; • if( NodePtr != NULL )printf( “Memory allocation successful!\n” ) ;elseprintf( “Memory not allocated!\n” ) ; • free( NodePtr ) ;

  6. structNodetype { structNodetype * NextPtr ; . . . } ; typedefstructNodetypeNode_t ; Node_t * RootPtr ; Memory block used for program variables with assigned names Memory block used for dynamically allocated blocks for storing data, each block addressable only using pointers (no names of variables!) Statically allocated namespace Dynamically allocated memory – The Heap RootPtr Pointer Data Pointer Data Pointer Data NULL sizeof( Node_t )

  7. Concept of linked structures • We will be creating dynamic (ie. runtime) data structures that will contain pointers to other structures • These are called linked structures. • Example:Node_t Node1, Node2 ; Node1.NextPtr = &Node2 ; // points at Node2 Node2.NextPtr = NULL ; // points nowhere! Node1 Node2 Data NULL Data Pointer

  8. Linked lists • The concept of a linked list refers to a set of dynamically allocated structures that contain pointer sub-fields, so that each pointer points at another allocated structure • By linking all elements of the set together, starting from a known address location, the entire set is called a linked list. • All linked lists must have an associated root pointer that is a named pointer variable. This provides the known address location to enter the list • There will be a last, or final, element and that one must have a NULL value in its link pointer to indicate the logical end-of-list. • There are several kinds of linked list structures • Singly linked list • Doubly linked list

  9. Linked lists • To illustrate the concept with code, we consider the example problem • Input data from a direct access file into a linked list. typedefstruct { int ID ; char Name[50] ; double Score ; } Payload_t ; structNodeStruct { Payload_t Data ; structNodeStruct * NextPtr ; } ; typedefstructNodeStructNode_t ; FILE * cfPtr ;Node_t * NodePtr, * Nptr, * RootPtr; intPayloadSize = sizeof( Payload_t ), NodeSize = sizeof( Node_t ) ;

  10. Linked lists • Example – continued • Always deal with the named root pointer first as a special case – Head of the List cfPtr = fopen( “DAFile.dat”, “rb” ) ; RootPtr = malloc( NodeSize ) ; fread( RootPtr, PayloadSize, 1, cfPtr ) ; RootPtr->NextPtr = NULL ; RootPtr Data 0 Pointer

  11. Linked lists • Example – continued • And now deal with the dynamically allocated list nodes and links Nptr = RootPtr ; while( ! feof( cfPtr ) ) { NodePtr= malloc( NodeSize ) ; fread( NodePtr, PayloadSize, 1, cfPtr ) ; Nptr->NextPtr = NodePtr ;Nptr = NodePtr ; Nptr->NextPtr = NULL ; } fclose( cfPtr ) ; RootPtr Data Nxt Data Nxt Data 0 Pointer

  12. Linked lists • Another example: Traversal of a list to find an ID • Note that this is a sequential search with complexity O(N) for a list with N nodes Nptr = RootPtr ; // Head of the list while( Nptr != NULL) ) { // list traversal loop if( Nptr->Data.ID == IDval ) break ; // search criterion Nptr = NPtr->NextPtr ; // point at successor (next) element } if( Nptr != NULL ) printf( “ID = %d, Name = %s, Score = %lf\n”, Nptr->Data.ID, Nptr->Data.Name, Nptr->Data.Score ) ; else printf( “ID: %d not found\n”, IDval ) ;

  13. Linked lists • And another example: Traversal of a list to output data to stdout • Always start from the named root pointer Nptr = RootPtr ; while( Nptr != NULL) ) { printf( “ID = %d, Name = %s, Score = %lf\n”, Nptr->Data.ID, Nptr->Data.Name, Nptr->Data.Score ) ; Nptr = NPtr->NextPtr ; }

  14. Linked lists • Deletion of a list • Also called de-allocation of memory • This is an extremely important issue for programmers IMPORTANT POINT ! Since memory blocks are allocated in unpredictable ways (effectively random), freeing those blocks may leave gaps, or holes, in the Heap. A systematic freeing up of all blocks eventually returns the Heap to its original unallocated state. For every malloc() call there must be a matching free() call ! Nptr = RootPtr ; while( Nptr != NULL) ) { // Traverse the list // CAUTION: Order of statements is important NodePtr = Nptr ; // 1. Save current node address Nptr = NodePtr->NextPtr ; // 2. Point at successor node free( NodePtr ) ; // 3. Release memory for node } RootPtr = NULL ; // list is now empty!

  15. Linked lists • Input data into a linked list so that it is sorted with each insertion of a node – Insertion Sort • Recall from a previous example: typedefstruct { int ID ; char Name[50] ; double Score ; } Payload_t ; structNodeStruct { Payload_t Data ; structNodeStruct * NextPtr ; } ; typedefstructNodeStructNode_t ; FILE * cfPtr ;Node_t * NodePtr, * Nptr, * RootPtr; intPayloadSize = sizeof( Payload_t ), NodeSize = sizeof( Node_t ) ; cfPtr = fopen( “DAFile.dat”, “rb” ) ; RootPtr = malloc( NodeSize ) ; fread( RootPtr, PayloadSize, 1, cfPtr ) ; RootPtr->NextPtr = NULL ;

  16. Linked lists • We must consider three different special cases • 1. Insert at Head of List • 2. Insert between two list nodes • 3. Insert at End of List 3 1 2 RootPtr Data Nxt Data Nxt Data 0 Pointer

  17. \\ ASSUMPTION: File has been opened using cfPtr while( ! feof( cfPtr ) ) { NodePtr= malloc( NodeSize ) ; fread( NodePtr, PayloadSize, 1, cfPtr ) ; if( RootPtr == NULL ) { NodePtr->NextPtr = NULL ; RootPtr= NodePtr; } else if(NodePtr->Payload.ID < RootPtr->Payload.ID ) { NodePtr->NextPtr = RootPtr ; RootPtr = NodePtr ; } else { Node_t * PrevNodePtr = RootPtr ; Nptr = RootPtr->NextPtr ; while( Nptr != NULL ) { if( NodePtr->Payload.ID <NPtr->Payload.ID ) { NodePtr->NextPtr = PrevNodePtr ; PrevNodePtr->NextPtr = NodePtr; break ; } else { PrevNodePtr = Nptr;Nptr = Nptr->NextPtr ; } } if( PrevNodePtr->NextPtr == NULL ) { NodePtr->NextPtr = NULL ; PrevNodePtr->NextPtr = NodePtr; } } } Linked lists • Input data from a direct access file into a linked list • Here is a complete listing of the C code for this algorithm

  18. \\ ASSUMPTION: File has been opened using cfPtr while( ! feof( cfPtr ) ) { NodePtr= malloc( NodeSize ) ; fread( NodePtr, PayloadSize, 1, cfPtr ) ; if( RootPtr == NULL ) { NodePtr->NextPtr = NULL ; RootPtr= NodePtr; } else if(NodePtr->Payload.ID < RootPtr->Payload.ID ) { NodePtr->NextPtr = RootPtr ; RootPtr = NodePtr ; } else { Node_t * PrevNodePtr = RootPtr ; Nptr = RootPtr->NextPtr ; while( Nptr != NULL ) { if( NodePtr->Payload.ID <NPtr->Payload.ID ) { NodePtr->NextPtr = PrevNodePtr ; PrevNodePtr->NextPtr = NodePtr; break ; } else { PrevNodePtr = Nptr;Nptr = Nptr->NextPtr ; } } if( PrevNodePtr->NextPtr == NULL ) { NodePtr->NextPtr = NULL ; PrevNodePtr->NextPtr = NodePtr; } } } Linked lists NodePtr Pointer Data \\ ASSUMPTION: File has been opened using cfPtr while( ! feof( cfPtr ) ) { NodePtr= malloc( NodeSize ) ; fread( NodePtr, PayloadSize, 1, cfPtr ) ; \\ Logic to find where to insert this inputted data block \\ in the singly linked list. }

  19. \\ ASSUMPTION: File has been opened using cfPtr while( ! feof( cfPtr ) ) { NodePtr= malloc( NodeSize ) ; fread( NodePtr, PayloadSize, 1, cfPtr ) ; if( RootPtr == NULL ) { NodePtr->NextPtr = NULL ; RootPtr= NodePtr; } else if(NodePtr->Payload.ID < RootPtr->Payload.ID ) { NodePtr->NextPtr = RootPtr ; RootPtr = NodePtr ; } else { Node_t * PrevNodePtr = RootPtr ; Nptr = RootPtr->NextPtr ; while( Nptr != NULL ) { if( NodePtr->Payload.ID <NPtr->Payload.ID ) { NodePtr->NextPtr = PrevNodePtr ; PrevNodePtr->NextPtr = NodePtr; break ; } else { PrevNodePtr = Nptr;Nptr = Nptr->NextPtr ; } } if( PrevNodePtr->NextPtr == NULL ) { NodePtr->NextPtr = NULL ; PrevNodePtr->NextPtr = NodePtr; } } } Linked lists RootPtr Pointer if( RootPtr == NULL ) { NodePtr->NextPtr = NULL ; RootPtr = NodePtr ; } Data 0

  20. \\ ASSUMPTION: File has been opened using cfPtr while( ! feof( cfPtr ) ) { NodePtr= malloc( NodeSize ) ; fread( NodePtr, PayloadSize, 1, cfPtr ) ; if( RootPtr == NULL ) { NodePtr->NextPtr = NULL ; RootPtr= NodePtr; } else if(NodePtr->Payload.ID < RootPtr->Payload.ID ) { NodePtr->NextPtr = RootPtr ; RootPtr = NodePtr ; } else { Node_t * PrevNodePtr = RootPtr ; Nptr = RootPtr->NextPtr ; while( Nptr != NULL ) { if( NodePtr->Payload.ID <NPtr->Payload.ID ) { NodePtr->NextPtr = PrevNodePtr ; PrevNodePtr->NextPtr = NodePtr; break ; } else { PrevNodePtr = Nptr;Nptr = Nptr->NextPtr ; } } if( PrevNodePtr->NextPtr == NULL ) { NodePtr->NextPtr = NULL ; PrevNodePtr->NextPtr = NodePtr; } } } Linked lists RootPtr Pointer else if( NodePtr->Payload.ID < RootPtr->Payload.ID ) { NodePtr->NextPtr = RootPtr ; RootPtr = NodePtr ; } else { \\ Locate insertion point in SL list } Data ?

  21. \\ ASSUMPTION: File has been opened using cfPtr while( ! feof( cfPtr ) ) { NodePtr= malloc( NodeSize ) ; fread( NodePtr, PayloadSize, 1, cfPtr ) ; if( RootPtr == NULL ) { NodePtr->NextPtr = NULL ; RootPtr= NodePtr; } else if(NodePtr->Payload.ID < RootPtr->Payload.ID ) { NodePtr->NextPtr = RootPtr ; RootPtr = NodePtr ; } else { Node_t * PrevNodePtr = RootPtr ; Nptr = RootPtr->NextPtr ; while( Nptr != NULL ) { if( NodePtr->Payload.ID <NPtr->Payload.ID ) { NodePtr->NextPtr = PrevNodePtr ; PrevNodePtr->NextPtr = NodePtr; break ; } else { PrevNodePtr = Nptr;Nptr = Nptr->NextPtr ; } } if( PrevNodePtr->NextPtr == NULL ) { NodePtr->NextPtr = NULL ; PrevNodePtr->NextPtr = NodePtr; } } } Linked lists Node_t* PrevNodePtr = RootPtr ; Nptr = RootPtr->NextPtr; while( Nptr != NULL ) { if( NodePtr->Payload.ID < NPtr->Payload.ID ) { NodePtr->NextPtr = NPtr; PrevNodePtr->NextPtr = NodePtr ; break ; } else { PrevNodePtr = Nptr ;Nptr = Nptr->NextPtr ; } } RootPtr Pointer Data Nxt Data Nxt Data 0

  22. \\ ASSUMPTION: File has been opened using cfPtr while( ! feof( cfPtr ) ) { NodePtr= malloc( NodeSize ) ; fread( NodePtr, PayloadSize, 1, cfPtr ) ; if( RootPtr == NULL ) { NodePtr->NextPtr = NULL ; RootPtr= NodePtr; } else if(NodePtr->Payload.ID < RootPtr->Payload.ID ) { NodePtr->NextPtr = RootPtr ; RootPtr = NodePtr ; } else { Node_t * PrevNodePtr = RootPtr ; Nptr = RootPtr->NextPtr ; while( Nptr != NULL ) { if( NodePtr->Payload.ID <NPtr->Payload.ID ) { NodePtr->NextPtr = PrevNodePtr ; PrevNodePtr->NextPtr = NodePtr; break ; } else { PrevNodePtr = Nptr;Nptr = Nptr->NextPtr ; } } if( PrevNodePtr->NextPtr == NULL ) { NodePtr->NextPtr = NULL ; PrevNodePtr->NextPtr = NodePtr; } } } Linked lists • Rgn • Input data from a direct access file into a linked list • \iggffi if( PrevNodePtr->NextPtr == NULL ) { NodePtr->NextPtr = NULL ; PrevNodePtr->NextPtr = NodePtr ; } RootPtr Pointer Data 0 Data Nxt

  23. \\ ASSUMPTION: File has been opened using cfPtr while( ! feof( cfPtr ) ) { NodePtr= malloc( NodeSize ) ; fread( NodePtr, PayloadSize, 1, cfPtr ) ; if( RootPtr == NULL ) { NodePtr->NextPtr = NULL ; RootPtr= NodePtr; } else if(NodePtr->Payload.ID < RootPtr->Payload.ID ) { NodePtr->NextPtr = RootPtr ; RootPtr = NodePtr ; } else { Node_t * PrevNodePtr = RootPtr ; Nptr = RootPtr->NextPtr ; while( Nptr != NULL ) { if( NodePtr->Payload.ID <NPtr->Payload.ID ) { NodePtr->NextPtr = PrevNodePtr ; PrevNodePtr->NextPtr = NodePtr; break ; } else { PrevNodePtr = Nptr;Nptr = Nptr->NextPtr ; } } if( PrevNodePtr->NextPtr == NULL ) { NodePtr->NextPtr = NULL ; PrevNodePtr->NextPtr = NodePtr; } } } Linked lists WHEW ! This example illustrates the Top-Down approach where the original problem has been broken down into sub-problems. These are organized methodically, and the algorithm developed accordingly. There are many details to consider and care must be taken when writing code with pointers, ensuring that the order of statements is correct. Always test code !!

  24. \\ ASSUMPTION: File has been opened using cfPtr while( ! feof( cfPtr ) ) { NodePtr= malloc( NodeSize ) ; fread( NodePtr, PayloadSize, 1, cfPtr ) ; if( RootPtr == NULL ) { NodePtr->NextPtr = NULL ; RootPtr= NodePtr; } else if(NodePtr->Payload.ID < RootPtr->Payload.ID ) { NodePtr->NextPtr = RootPtr ; RootPtr = NodePtr ; } else { Node_t * PrevNodePtr = RootPtr ; Nptr = RootPtr->NextPtr ; while( Nptr != NULL ) { if( NodePtr->Payload.ID <NPtr->Payload.ID ) { NodePtr->NextPtr = PrevNodePtr ; PrevNodePtr->NextPtr = NodePtr; break ; } else { PrevNodePtr = Nptr;Nptr = Nptr->NextPtr ; } } if( PrevNodePtr->NextPtr == NULL ) { NodePtr->NextPtr = NULL ; PrevNodePtr->NextPtr = NodePtr; } } } Linked lists \\ USING LINKED LIST FUNCTIONS \\ ASSUMPTION: File has been opened using cfPtr while( ! feof( cfPtr ) ) { NodePtr= malloc( NodeSize ) ; fread( NodePtr, PayloadSize, 1, cfPtr ) ; NodePtr->NextPtr = NULL ; Nptr = FindNode( RootPtr, NodePtr->Payload.ID ) ; InsertNode( RootPtr, Nptr, NodePtr ) ; } \\ This still leaves the functions to write, but it makes \\ it easier to understand the primary algorithm, \\ and the start of the Top-Down design process.

  25. Linked lists • From the previous example it is seen that developing and using functions with well defined algorithms is worthwhile when working with dynamically allocated list data structures • This separates high-level intention from low-level details • Design focuses on Use-Cases, while details focus on how it works • Some other example functions to consider: • Node_t* FindNode ( Node_t * Head, intIDval ) ; • void * InsertNode ( Node_t * Root, Node_t * InsertPtr, Node_t * InsertNodePtr ) ; • Node_t* FindLast( Node_t * Head ) ; • void * OutputList( Node_t * Head ) ; • void * DeleteList( Node_t * Head ) ; • intisEmpty( Node_t * Head ) ; • long intListLength( Node_t * Head ) ;

  26. Doubly linked lists – Conceptual View structNodetypeDL { Payload_t Data ; structNodetypeDL * PrevPtr ; structNodetypeDL* NextPtr; } ; typedefstructNodetypeDLNode_tDL; Node_tDL *HeadPtr = NULL, *TailPtr = NULL ; TailPtr Pointer HeadPtr Pointer Data PrvNxt Data NULL Nxt Data Prv NULL

  27. Doubly linked lists • Doubly linked lists feature two self-referential pointers, usually called Predecessor (Previous) and Successor (Next) links • There are two named pointers, usually called Head and Tail pointers, the latter pointing to the last node in the list • Traversal can be performed in both directions • Limited traversals (to adjacent, or nearby, nodes) can be performed in both directions • Typical operations are similar to those for singly linked lists • InsertNode, DeleteNode, DeleteList, FindNode, and so on Bi-directional traversal

  28. Doubly linked lists – Useful functions • InsertHeadNode • InsertTailNode • FindNodebyID • InsertNodebyID • DeleteNodebyID • DeleteList

  29. Doubly linked lists – Useful functions \\ Assume that Nptr points to the data structure to \\ be inserted at the head of the list, and it is fully \\ initialized, including both Next and Prev pointers \\ with NULL values. \\ Note: Both head and tail pointer arguments can be \\ modified (use call-by-reference). void InsertHeadNode( StackNodePtr_t * Hptr, StackNodePtr_t * Tptr, StackNodePtr_tNptr ) { if( *Hptr == NULL ) \\ empty list, update tail *Tptr = Nptr ; else \\ update new node to point Nptr->NextPtr = *Hptr ; \\ to rest of list *Hptr = Nptr; \\ update head and exit return ; // success } \\ NOTE: It is possible to achieve this in different \\ ways. • InsertHeadNode • InsertTailNode • FindNodebyID • InsertNodebyID • DeleteNodebyID • DeleteList

  30. Doubly linked lists – Useful functions \\ Assume that Nptr points to the data structure to \\ be inserted at the tail of the list, and it is fully \\ initialized, including both Next and Prev pointers \\ with NULL values. \\ Note: Both head and tail pointer arguments can be \\ modified (use call-by-reference). void InsertTailNode( StackNodePtr_t * Hptr, StackNodePtr_t * Tptr, StackNodePtr_tNptr ) { if( *Tptr == NULL ) \\ empty list, update head *Hptr = Nptr ; else \\ update new node to point Nptr->PrevPtr = *Tptr ; \\ to rest of list *Tptr= Nptr; \\ update tail and exit return ; // success } • InsertHeadNode • InsertTailNode • FindNodebyID • InsertNodebyID • DeleteNodebyID • DeleteList

  31. Doubly linked lists – Useful functions \\ Assume that IDval contains the search value and \\ Hptr points to the head of the list. \\ Returns a pointer to the first node containing IDval, \\ otherwise returns NULL. StackNodePtr_t * FindNodebyID( StackNodePtr_tHptr, intIDval ) { StackNodePtr_tNptr ; if( *Hptr == NULL ) \\ empty list return NULL ; else \\ search list Nptr = Hptr ; while( Nptr != NULL ) { if( Nptr->Payload.ID == Idval ) return Npr ; \\ search successful Nptr = Nptr->NextPtr ; } return NULL ; } • InsertHeadNode • InsertTailNode • FindNodebyID • InsertNodebyID • DeleteNodebyID • DeleteList

  32. Linked lists – Some Deeper Details • Logical model view of the Heap Space • Memory allocation model • Based on heap pointers • Unpredictable address associations with the memory block • Requires care – once a pointer is lost, the memory block (and all data inside it) is lost and cannot be accessed • Management • Overhead costs – time and space • Allocation Tables and Heap Limits • Holes and fragmentation • Defragmentation RootPtr Data Nxt Data Nxt Data 0 ? ? Pointer

  33. Stacks and Queues • There are two kinds of singly linked list structures that merit special attention • A Stack is a linked list with an access pointer called the Stack Pointer, and whose nodes are added or deleted only at the beginning of the list • They are referred to as LIFO (Last In, First Out) lists • A Queueis a linked list with two access pointers, called Head and Tail, and whose nodes are added only to the Tail, or deleted only from the Head of the list • They are referred to as FIFOlists (First In, First Out)

  34. Stacks • There are two basic operations on stacks. Both are applied only at the beginning of the list, otherwise called the Top of the Stack • PUSH – insert a new node at the top of the stack • POP – remove (delete) a new node from the top of the stack • The named entry pointer is typically called the Stack Pointer • \\ ASSUMPTIONS:structstackNode{Data_tData ;structstackNode * Nextptr;}typedefstructstackNodeStackNode;typedefStackNode * StackNodePtr_t;StackNodePtr_tStackPtr = NULL ; LIFO StackPtr Data Nxt Data Nxt Data 0 Pointer

  35. StackNodePtrPush( StackNodePtr * SP, Data_tD ) { StackNodePtrNewPtr ; NewPtr = malloc( sizeof( StackNode ) ) ; if( NewPtr == NULL ) return NULL ; Copy( NewPtr->Data, D ) ; NewPtr->NextPtr = NULL ; if( *SP != NULL ) NewPtr->NextPtr = *SP ; *SP = NewPtr; return SP ; } \\ USE CASE if( Push( &stackPtr, Data ) != NULL ) printf( “Push to stack successful\n” ) ; else printf( “Allocation failed, no Push\n” ) ; Stacks – Push() • Push() inserts a node into the first list position of the stack • The new top-of-stack node must point to the rest of the list • The stack pointer must point at the new top-of-stack node • Account for possible error • This is one alternative coding of Push() – there are others (eg. textbook) StackPtr Top-of-stack node Data Nxt Data Nxt Data 0 Pointer

  36. int Pop( StackNodePtr* SP, Data_t* Dptr ) { StackNodePtrtempPtr ; Data_t D ; if( *SP == NULL ) return 0 ; \\ Stack is empty so fail Copy( (*SP)->Data, Dptr) ; \\ Copy-return payload data tempPtr= (*SP)->NextPtr ; \\ Get address of next node free( *SP ) ; \\ Deallocate top node*SP = tempPtr ; \\ Update stack pointer return 1 ; \\ Success is TRUE } \\ USE CASE :: Assume: Data_tDataRec; \\ StackNodePtr* StackPtr ; if( Pop( &StackPtr, &DataRec ) ) printf( “ID = %d\n”, DataRec.ID ) ; else printf( “No data found on empty stack\n” ) ; Stacks – Pop() • Pop() obtains and returns the payload data from the top-of-stack node, then it removes this node from the stack • A return mechanism must be chosen and implemented for the payload data • Scalar data can be returned directly through function return statements – this limits the ability to report errors such as an empty stack • Call-by-reference is always useful, but necessary for complex data structures • The stack pointer must be updated to point at the second node, which then becomes the new top-of-stack node • The used node is then de-allocated (and its memory block can be re-used) StackPtr Top-of-stack node Data Nxt Data Nxt Data 0 Pointer

  37. Queues NodePtr_tenqueue( NodePtr_t * NQP, Data_t D ) { NodePtr_tNPtr ; NPtr = malloc( sizeof( Node_t ) ) ; if( NPtr == NULL ) return NULL ; NPtr->NextPtr = NULL ; Copy( NPtr->Data, D ) ; if( *NQP != NULL ) NPtr->NextPtr = *NQP ; *NQP = NPtr; return NPtr ; } • The basic queue is a singly linked list with two pointers, Head and Tail • There are two fundamental operations • enqueue() – insert a node at the Tail position • dequeue() – obtain payload data from the node at the Head position, then delete the node intdequeue( NodePtr_t* DQP, Data_t* Dptr ) { NodePtr_ttempPtr ; Data_t D ; if( *DQP == NULL ) return 0 ; \\ Stack is empty so fail Copy( (*DQP)->Data, Dptr) ; \\ Copy-return payload data tempPtr = *DQP ; \\ Get address of next node *DQP = (*DQP)->NextPtr; \\ Update stack pointer free( tempPtr ) ; \\ Deallocate node return 1 ; \\ Success is TRUE } TailPtr Pointer Enqueue HeadPtr Data Nxt Data Nxt Data 0 Dequeue Pointer

  38. Queues • There are different kinds of queues • Circular Queues are singly linked lists with only a single named pointer, sometimes called CQptr • All enqueue and dequeue operations are performed using CQptr • A further distinction is that the last node points to the first node – the NextPtr field is never NULL! • Not necessarily FIFO • Sometimes called Round-Robin queue • Used for print spoolers, concurrency handling and many other applications Enqueue CQptr Data Nxt Data Nxt Data Nxt Dequeue Pointer

  39. Advanced data structure abstractions Exploring the abstract connections between dynamically allocated data structures and designing efficient algorithms for ADTs

  40. I think that I shall never see, A program lovelier than a Tree … - with apologies to Joyce Kilmer

  41. RootPtr Ptr RootPtr Ptr Data L R Data L R Data L R Data L R Data L R Data L R Data L R Data L R Data Nxt Data Nxt Data 0

  42. Binary Trees In the ideal case of N = 2K nodes with perfect balancing of Left and Right child nodes, there is: 1 root node – level-1 2 level-2 nodes 4 level-3 nodes, and so on, up to log2(N/2) level-(K-1) nodes. Recall that log2(N/2) = K-1. For N=1024 nodes there are only K=10 levels. • This implies that for ordered nodes, a binary search strategy can be implemented. • A binary tree is a data structure that is based on a specific relationship that exists between two neighbour nodes • Example: P1->Data.ID is less than P2->Data.ID • Each node contains two pointer fields • A Left child pointer • A Right child pointer • Nodes that do not have a Left (Right) child assign NULL values to that pointer field • When both node pointers are NULL, the node is called a Leaf node.

  43. structDataRec { int SID ; char Lname[30] ; char Fname[20] ; float GPA ; } typedefstructDataRec Data ; structBinTreeRec { DataD ; structBinTreeRec * Left ; structBinTreeRec * Right ; } A Binary Tree RootPtr Ptr Root node Left child Right child Data L R Data L R Data L R Data L R Data L R Data L R Data L R Data L R Left child Left child Right child Right child Leaf Leaf Leaf Leaf

  44. Tree Constructions • Constructing a tree is done by inserting each new node at a leaf position • We assume the Left Child is less than the Right Child. • Consider three different input orders • 10, 20, 30, 40, 50, 60, 70 • 20, 50, 30, 40, 10, 60, 70 • 40, 20, 10, 30, 60, 50, 70 • This produces a highly unbalancedtree – it is actually a singly linked listwith wasted left children! Only linearsearch can be applied. 10 20 30 40 50 60 70

  45. Tree Constructions • Constructing a tree is done by inserting each new node at a leaf position • We assume the Left Child is less than the Right Child. • Consider three different input orders • 10, 20, 30, 40, 50, 60, 70 • 20, 50, 30, 40, 10, 60, 70 • 40, 20, 10, 30, 60, 50, 70 • This version is better balanced, but search isbetween O(n) and O(log n) 20 10 50 30 60 40 70

  46. Tree Constructions • Constructing a tree is done by inserting each new node at a leaf position • We assume the Left Child is less than the Right Child. • Consider three different input orders10, 20, 30, 40, 50, 60, 70 • 20, 50, 30, 40, 10, 60, 70 • 40, 20, 10, 30, 60, 50, 70 • This version is perfectly balancedand search is O(log n) 40 20 60 10 70 30 50

  47. Tree Traversal Techniques • Traversals • Inorder • Preorder • Postorder 40 60 20 10 30 50 70 25 35 5 15 45 55 65 75

  48. inOrder: 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75 Tree Traversal Techniques void inOrder ( TreeNodePtr_tTPtr ) { if( TPtr != NULL ) { inOrder( TPtr->leftChild ) ; \\ First, go left outputNode( TPtr ) ; \\ output node inOrder( TPtr->rightChild ) ; \\ then go right } return ; } • Traversals • Inorder • Preorder • Postorder 40 60 20 10 30 50 70 25 35 5 15 45 55 65 75

  49. preOrder: 40, 20, 10, 5, 15, 30, 25, 35, 60, 50, 45, 55, 70, 65, 75 Tree Traversal Techniques void preOrder ( TreeNodePtr_tTPtr ) { if( TPtr != NULL ) { outputNode( TPtr ) ; \\ output node preOrder( TPtr->leftChild ) ; \\ then, go left preOrder( TPtr->rightChild ) ; \\ then go right } return ; } • Traversals • Inorder • Preorder • Postorder 40 60 20 10 30 50 70 25 35 5 15 45 55 65 75

  50. postOrder: 5, 15, 10, 25, 35, 30, 20, 45, 55, 50, 65, 75, 70, 60, 40 Tree Traversal Techniques void postOrder ( TreeNodePtr_tTPtr ) { if( TPtr != NULL ) { postOrder( TPtr->leftChild ) ; \\ First, go left postOrder( TPtr->rightChild ) ; \\ then go right outputNode( TPtr ) ; \\ output node } return ; } • Traversals • Inorder • Preorder • Postorder 40 60 20 10 30 50 70 25 35 5 15 45 55 65 75

More Related