Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Linked Liststo be viewed This presentation is intended in slideshow mode. If you are reading this text, you are not in slide show mode. Hit the F5 function key to enter slideshow mode. Linked Lists Introduction and Motivation Linked Lists versus Linear Lists Abstract Properties and Operations Animation of a Dynamic Linear Structure Implemented as a Linked List Building (Insertion Into) a Linked List: Simplest Case Traversal and Traversal-Based Operations Variations, Embellishments, and Elaborations Bi-directional Lists Circular Lists Headed Lists Summary MSJ-2 Motivation Both Real World Engineering and Pedagogical Linked lists are the simplest of the fundamental computer science objects known as dynamically linked structures They are dynamic in that their size can vary during execution as individual items are inserted into or removed from the list, like the list of students in CS315, which can and usually does change after the semester starts as some students add the course and others drop it Linked lists are extremely useful in and of themselves – e.g., the CS315 roster – and they provide the basis for even more interesting (both theoretically and practically) objects such as stacks, queues, and binary trees, the most beautiful data structures in the universe Implementation of even the simplest of linked list operations requires careful thought, selection among multiple design alternatives, and excruciating care with the coding details – all skills that apprentice software engineers need to cultivate MSJ-3 Linked Lists and Linear Lists Linked lists are one of two common implementations of a slightly more abstract concept known as a linear list (a one-dimensional array is the other implementation) Unlike, for example, binary trees, the abstract properties of linear lists by themselves are not to me that fascinating; what makes them worth our study here is their tremendous real world utility and the importance of mastering their linked implementation alternatives that we’ll use throughout CS315 (and that you’ll often use professionally later) But I promised in my introduction to this course that we would begin our discussion of every data structure this semester with a discussion of its abstract properties so I’ll include them here for logical consistency – but I’ll be brief (for me ;-) MSJ-4 Abstract Properties of Linear Lists Each item in the list except the first has a unique predecessor and each except the last has a unique successor George Washington John Adams Thomas Jefferson … George Bush Barack Obama The uniqueness of the predecessor/successor relationship means that the list can be laid out as illustrated above and is the reason that simple lists are often known as linear lists A list may or may not be empty, but, in the abstract, there is no maximum size Note that an array does have a maximum size, the one you declare, and, as far as C is concerned, its size is constant This is a theme we’ll see again and again – an implementation of an abstract data structure may induce properties or limitations that the abstract structure itself does not have MSJ-5 Abstract Operations on Linear Lists Traversal – visiting (e.g., printing out) each item in the list; comes in two flavors: first-to-last and last-to-first Search or find – determining whether a given item is present Deletion – of a given item Note that insertion can’t be defined without more information There are many possible places where insertion could occur In front of the front Behind the last Somewhere in the middle (where?) So a definition of “insertion” will require more knowledge about where and why; for linear lists, its behavior is not uniquely defined just from the linearity property MSJ-6 Abstract Operations on Linear Lists (cont’d) Any abstract data structure has certain operations and characteristics defined even in the abstract, regardless of implementation Whether or not a given application needs a given operation on some data structure is an engineering issue, not a theory of data structures one As an engineer, you need to know what the properties and operations are so that you can pick an appropriate structure for your problem ─ it may have more than what you need, but it better not have less MSJ-7 Abstract Operations on Linear Lists (cont’d) For linear lists, the abstract properties and operations may seem pretty obvious and hence this discussion mostly unnecessary, but later this semester we’ll be dealing with data structures whose properties and operations are much less obvious and I want to start out doing things properly, i.e., defining our structures in the abstract before moving to questions of implementation MSJ-8 Linked Lists Introduction and Motivation Linked Lists Versus Linear Lists Abstract Properties and Operations Animation of a Dynamic Linear Structure Implemented as a Linked List Building (Insertion Into) a Linked List : Simplest Case Traversal and Traversal-Based Operations Variations, Embellishments, and Elaborations MSJ-9 Example of a Dynamically Linked Data Structure Suppose we want to write a program that works with a set of numbers whose cardinality varies widely – perhaps a variable number of temporary data entry clerks input the numbers one at a time until they get tired and tell our program “no more”; we couldn’t know in advance how many clerks we would have available on any given day nor how many items any given clerk would actually enter in any given day If our code declared too big of an array size, most of the time we'd be wasting most of it But if we declared too small an array and filled it up, we couldn't keep going without major problems A dynamic data structure whose size can increase one cell at a time whenever we want it to is what we need A linked list implemented with pointers is an important technique for implementing such a dynamic data structure MSJ-10 Dynamically Growing a Linked List start 18 -2 When we get a number, we use malloc to dynamically obtain some storage 461 23 the list 314 space for it and link it into And so on ... Notice that the structures in the list have no names; they Thecan nextonly timebewe get a new we accessed vianumber, some pointer – names are part of again some new space and link theget communication fromfor theit programmer to the compiler; it in these to the objects, end of the growing above, are list not compiled objects; they are during program Notecreated that thedynamically list elements are not simpleexecution, the compiler is long integer cellsgone but structures of some sort, since each must have space for a pointer in it as well as the integer MSJ-11 Linked Lists Introduction and Motivation Building (Insertion Into) a Linked List: Simplest Case Traversal and Traversal-Based Operations Variations, Embellishments, and Elaborations MSJ-12 Example of Building a Linked List The previous animation was designed for simplicity of imagery, to get us started visualizing the concept of a linked list start pointed to the original (oldest) item in the list, new items being inserted to the right, at the far end of the list, after previous items; the list growing in the direction of its links start ••• older newer Although the code for this version of insertion is simple enough, it’s not actually the very simplest version to code. Insertion at the front of the list, new items being inserted before (to the left of) previous items is actually slightly simpler, so to keep our sample code as simple as possible, that’s what we’ll look at Later, we’ll also look at why and how we might insert in the middle of the list rather than at one of the ends Animation for the Code Example We’re About to Do start will always point to the most recent item in the list, new items being inserted in front (to the left) of older items And we’ll color code the various components of our structure so help us tell them apart This is what we’ll code: integer pointer to our structure 0 start ••• MSJ-14 The Creation of a Linked List: At this point wehave theused insertion of the Notecompleted that the & is normally to first element First Insertion into a previously empty list The Although this very firstnew time through provide aninsert address to scanf for this in front of Since we want to our LIST_ITEM insertion loop we could store the address the location ofpoints the storage the current start ofstill our list, we need to make new The tempthe pointer actually into our list, too; our but so what? We'll also need afor temporary typedef struct listItem returned byitem malloc directly intoagain start instead input onewith point to the currently at the i.e., pointed We're done ituser's for now and won't to until we pointer to refer hold theitfront, address { of temp, that won't work start for any equal subsequent statement, by setting to bythis start create a And new item returned by malloc for a new As it happens, we could have left Note the use of sizeof(LIST_ITEM) int someInteger; to temp makes start point to the same insertions cell before it's linked in to the list out the parentheses and just At this point in our example here there aren't to make sure that malloc obtains theany items struct *next; as temp does, thus completing the Our listlistItem isplace the structure (set of items) pointed to by start; the On the next slide, we'll see that this logic we’re written &temp->someInteger, in the list so we just wind up setting temp->next to right amount space for us } LIST_ITEM; of the new LIST_ITEM into our list existenceinsertion and properties of that list are not affected by theit writing here is general enough so that it actually NULL (the current value of start) which is fine, but unless one remembers the atofthe very front ; *temp; , LIST_ITEM *start = NULL existence other “leftover” pointers pointing toinany oflist, those items works for allprecedence our insertions operator tables, indicates that this item is the last item the which it This definition defines a recursive structure, also known as while (???) Note this statement and the one safe than sorry; and Iadd think will be –better when there's one item inbefore the list, as always there start, or whatever other name you choose in your code, that Now let's look atonly the logic toitem a this a self-referential structure, since each of type { have to be in exactly the right or theindone version herethe isitem easier to read is forcomplete now this current example, itaorder is both thewould start and Without typedef, this line be to the list; temp is just that, “working” variable new LIST_ITEM into the list (sizeof(LIST_ITEM)); temp malloc ??? =points contains a pointer to another of the same type Note that we still need to put the * in front Notetothat name LIST_ITEM here needn't thethe list will wind uplist, looking rather strange! in any event, no? end of integer: the list, no? struct listItem *start = –NULL; used help construct the but not part of the list itself a more printf( " Enter your " ); The malloc will return the address for the of the variable name to indicate that what Also note that at this point we actually Now that our typedef is set up, we need to have any relationship to the name listItem, embedded We'll do name this inside of auser while loop Here it's within typedef just to make the rest which isacompletely equivalent, except descriptive (i.e., better) for it would have been Anyway, now we'll ask the to provide an scanf( " %d ", &(temp->someInteger) ); we want to declare here is aispointer to a likeitems new memory just allocated to us have aof completely valid linked list; it'sreadability declare a pointer to the start of the list, the one above, asitinteger far as the compiler concerned since don't know how many the program awe little easier to read possibly for newItemForInsertion, but I didn’t have room for anything that value for our new list item temp->next = start; LIST_ITEM, not a LIST_ITEM itself, just empty at the moment wethese saw incharts the user simpler earlier the will animation want to add long on We need to store that address in a pointer however, itwe seems good idea to which, Look at next line (when itatogets here ;-) and think about I'mthe sure you remember, isstore what start=Stylistically, temp; Here's where want the user variable somewhere or never be able make them somehow, and C is case we First, we get some new how you'd write itthis if need there were no typedef } we'd get ifrelated leftwe'll out the *to in front of the input; and integer cell can be referenced use this new chunk of memory in the future sensitive, so all caps for typedef's is a memory for our new cell with our old name temp as temp->someInteger, correct? That's one of the major uses for a typedef, brevity and common convention (butmalloc not a requirement) friend the function start improved comprehensibility temp 37 MSJ-15 The Creation of a Linked List: More Insertions This little while loop of five statements – and two of them are printf and scanf, which are really typedef struct listItem not about linked lists at all loop – is all takes to build athat we just This little while of itfive statements { linked list developed as long as –we want, our operator and two until of them are printf and scanf, int someInteger; tells us towhich quit, for it'slinked 5PM lists at all – is all it areexample, really notsince about struct listItem *next; takes to build a linked list as long as we want, until our } LIST_ITEM; Let's watch it at work a few times operator tells us to quit, for example, since it's 5PM ; *temp; LIST_ITEM *start = NULL, Make the new item point to the item work a few more times in our at the front of the list, loop thus while (???) Let's watch it atcurrently positioning the new { Reset the start pointer to item pointintofront of temp = malloc (sizeof(LIST_ITEM)); oldthat start theatlist the newthe item is of now the front printf("Enter your integer: "); of the list scanf("%d", &(temp->someInteger)); temp->next = start; start = temp; And so on … } start temp -2 23 5 37 MSJ-16 Linked Lists Introduction and Motivation Building (Insertion Into) a Linked List : Simplest Case Traversal and Traversal-Based Operations Traversal Search/Find Deletion Insertion into the middle (building an ordered list) Variations, Embellishments, and Elaborations MSJ-17 Traversal A Fundamental Operation on Linked Lists (and Linked Structures in General, For That Matter) Since the traversal pointer isWe’re still pointing a valid still nottodone with item, we’re done with And since trvPtr is nowto first,not toone know what theBut traversal yet … while (trvPtr != NULL) To traverse a data structure is to go through it item the traversal yet … wethe NULL, we’ve reached end Note that we do not want to use our start store in trvPtr, have at a time, “visiting” each item in turn { of the list and are pointer as our traversal pointer totoevaluate the therefore expression We’ll need a traversal pointer keep -2 23 5 37 printf( “ %d ” , trvPtr->someInteger); done with the traversal trvPtr->next The purpose of the visit is application dependent; track of wepointer currently in to thethe liststart If start is where the only we are have trvPtr = trvPtr->next; maybe, for example, we just want to add up all the ofour listinitialize and we itchange it, asof we’re certainly We’ll to the start the list, in the list, or maybe we’re searching to find here } values do Since next is a pointer, what’s stored going to with our trvPtr, we’ll lose forever asvalue theaddress, name suggests, …the so let’s visit the some since, specificis an whichstart wethat’s represent in these allability to access the actual of our list Thisstatement, statement’s syntax Since this is an assignment usual starting point for arrow, doing much ofvalue current itemand … diagrams as an so the of the So the current value of the expression For our example here, where we’re concerned with the semantics are very typical of we’re going the pattern anything withtothe a change linked list ;-) Currently, trvPtr contains address ofbit So Here’s the traversal code expression trvPtr->next is this arrow trvPtr->next is the bit pattern stored the result of this mechanics ofstored traversal and not those what we oninanavigating visit, wedo use that’s in trvPtr (designates, or points to) this structure here, in the component named next of the tovisit advance We’ve lost access to -2item … and then move on let’s just print out the integer in each as is we itlet’s step linked structures in general, so statement It’s pretty simple; So it is this address (arrow) that will be structure currently pointed to by trvPtr trvPtr toit the next item Since stored trvPtr a pointer, that means and can never get itthe back to the next one let’s make sure we understand through in is trvPtr by assignment our listof how it works we’re statement changing what points to executing theindetails we’reitcurrently LIST_ITEM *trvPtr = start; start -2 trvPtr 23 a separate 5 So we need traversal pointer that we can change 37 MSJ-18 Reminder On Interpreting and Using the Graphics of Linked Structures start -2 5 23 37 The arrows portray the logical connectivity, or topology, of our structures, which is all that we really care about The picture above emphasizes that this structure is a linear list (one item after another), other topologies are possible and used for other purposes; we’ll look at some later this semester The actual layout in memory need not look anything at all like our logical view, so long as the pointers still provide the correct topology; here, below, is a partial memory map showing how our linear list might actually be laid out in memory: ••• 37 -2 5 23 ••• MSJ-19 Traversal Is a Fundamental Operation It is important all by itself: E.g., print out the list of all students enrolled in CS315 It is used at the beginning of several other key operations: Search/find Delete Insert-in-order MSJ-20 Search/Find E.g., Look Up a Phone Number Given a Last Name Traverse the list, checking each item to see if it is the one being searched for, stopping either when we reach the searched for item or when we complete the traversal and have no more items to check If we find what we were searching for, the search is said to be successful If the traversal completes without finding the desired data, the search is unsuccessful; the target item is not present in the list MSJ-21 Delete a Specified Item free is the systemdeleteItem(5); service call that returns memory to the OS, e.g., free(delPtr); As a programming matter, we should save the address of this some pointer variable (it’s currently in delPtr) free is item thus in the opposite of malloc High level Maybe we’d to insert into a different structure after Note thatpseudocode: we arewant freeing up theitmemory that delPtr points we actually deleted delPtr it from this one – e.g., after a student flunks to, not itself CS315, remove student recordissues from the list let’s of current There arethe some interesting here; look at Search for the item to be deleted using the standard Science majors andpractice, then insert it into a list of As aComputer matter good programming everything anofexample search/traversal logicshould eventually be explicitly returned Sociology majors obtained via malloc via Here’s the situation after wea have traversed the list to the OS a free call – as part of program’s cleanup Item 5 has been deleted from the list Non trivial question: Justwere At the very least, if we really all done with this item, and successfully found the item to be– deleted If the search is unsuccessful, do nothing or maybe But how do we designate before-shutdown processing, if not before exactly how do weitself we’dwe want to return its still storage to it’s the operating system Note that the item exists; thisreport cell that want to know what to set 5it to: As far as the list itself is Let’s say, for example, that it’sWe the item containing that what the item towith be deleted was not found designate the pointer we But since we do an item after deleting it from a just not part of the linked list anymore: set to delPtr->next ? concerned, all we need that we want dependent, to delete we won’t need23 toisnow adjust? ???show = delPtr->next; structure application this step in item points to item 37 to do to delete the item ??? If the itemhere; to bewe’re deleted is found, with adjust pointers as = delPtr->next; our code only concerned the list theory and is to of adjust one pointer practice deletion necessary to remove it from the list start -2 23 5 37 delPtr MSJ-22 Reminder Item (cont’d) Delete a Specified the In terms of codeAdjusting development, do thePointers general case first; in this case, that’s when item to be deleted is somewhere in the middle of the list deleteItem(2); deleteItem(5); Then figure out if you need special cases – i.e., places where the logic for generalmethod case won’t workthe use of two Onethe standard involves traversal pointers: a leader a trailer, where Typical special cases involveand working at the ends of the list trailer is always kept one item behind the leader Inso this example, theleader general purpose logic works correctly to that when the thedeletion item to code: be So here’s thefinds actual Note that there will be a special case to handle the of the so very deleted, next is the that delete thethe lasttrailer’s item, but failspointer to delete the one first deletion item properly, first item inwe the list: trail->next = lead->next; here needed one special case; sometimes must beonly adjusted to actually do the deletion of the you’ll need /* Traverse/search toby find item to be deleted */ item pointed to thethe leader more, sometimes fewer; but when trying to figure out your Designating the pointer to the item to be deleted algorithm, start with/*the generalfirst case item that is to be deleted if (lead == start) It is the (bypassed) will require some additional work start = start->next; And start by drawing/annotating pictures showing which There areget two morelogic orwhen less approaches else /* “Normal” deletion */ obvious pointers adjusted and to point to where trail->next = lead->next Only then, when you’re sure you’ve got a workable algorithm, worry about translating it into code start That’s 5 -2 known as separation 23 37 formal of concerns, a more name for doing one thing at a time trail lead */ delPtr MSJ-23 Another Way to Delete a Specified Item deleteItem(5); Note that if the illustration here were the whole An alternative to the saved two pointer (leader/trailer) method is to only use a story, we never the address of this cell single, trailing pointer at the cost of slightly more complicated anywhere (it’s what used to be in delPtr->next), expressions in both “find-the-item-to-be-deleted” logic and the so now we can’tthe access this cell anymore actual deletion logic itself We can’t even give it back to the OS, since the free call requires the address of!= the5cell while ( delPtr->someInteger ) to ((delPtr->next)->someInteger !=be5) freed up,=and our code has no record of it anymore delPtr delPtr->next; Since the next component Oopsy, we probably should haveAnd saved theaold have, as before, delPtr->next = (delPtr->next)->next; iswe itself pointer type, the Here’s the loop condition we now … leaving us noalist, way to Since item,note: designated by delPtr->next, is itself structure that valuethis of delPtr->next somewhere deleted item 5 from the C programming C evaluates multiple -> value of the expression want to use so that the loop stops designate the pointer that delPtr->next designates has components, (delPtr->next)->someInteger designates this … but whose next field is the one It’s important to understand the although, as before, the Similarly, (delPtr->next)->next delPtr->next is an operators from left to right so the parentheses can be Here’s our old traversal loop, that with delPtr pointing to the item … so here’s the pointer needed to be adjusted to the next component the to themeaning component, namedof someInteger, so the value ofto the expression ofwill expressions like this that have be adjusted to item itself continues to exist designates the next field here … omitted from (delPtr->next)->someInteger address/arrow that points to stops when delPtr points before the itemtotoby bedelPtr deleted … adjustment logic that does actually do the deletion item pointed (delPtr->next)->someInteger is 5multiple that have -> operators actually do the desired this, the next item of ourdeletion list item to be deleted … the actual deletion … E.g., delPtr->next->next evaluates to what we want and to me, is slightly easier to read; this is a rare case where knowing and taking advantage of start -2 23 and leaving5 out 37 the C operator precedence rules unnecessary parentheses makes code more … by copying readable, not less … into this cell delPtr this value … MSJ-24 Still Need to Check for Special Cases if valueToBeDeleted) As(start->someInteger before, when we check for== problems we’ll see that the general case start = start->next; /* Delete logic doesn’t handle all possible casesthe very first item in the list */ else /* Traverse to find the valueToBeDeleted */ To make this general case look truly general, let’s replace 5, the specific { value used in the last example, Here’s special casevalueToBeDeleted our with the a more general delPtr = start; general logic can’t handle, deleting the first item in the list valueToBeDeleted) while ((delPtr->next)->someInteger != 5) delPtr = delPtr->next; delPtr->next = (delPtr->next)->next; /* Do the deletion */ And there’s no this way“single to initialize delPtr to approach anything earlier problem with trailing pointer” is that than sincethe }Onestart so that thistocode could the first item in the list‘if’ Noteisthat this time the general case traversal is inside an delPtr initialized start, thecheck first item the traversal loop, above, statement whereas last time (separate leading and of trailing pointers) actually looks is in for traversal the valueToBeDeleted is the actually the second item Putting the ‘while’ loop as one case an The result that this code, above, can’t find item containing -2 And the same general case logic we used thelisttraversal logic the same all and only the actual in the – can’t i.e., itdelete starts looking atsolves 23,in not atcases 2 enclosing ‘if’ was statement the problem and so it before correctly handles everything else deletion logic itself had the special case which resulted in the ‘if’ statement being inside the while loop There’s no general pattern to23 special cases;5 the only firm start -2 37rule to follow is that one should develop the general case logic first, then figure out what the special cases are (they vary from problem to problem) , then add logic for them wherever and however necessary delPtr MSJ-25 More Special Cases, Still Using Only Note that we need to add a check to our traversal/search loop to keep us from advancing beyond the end of the listPointer and then trying One (Trailing) Traversal to de-reference a NULL pointer, which would cause our program if to(start blowup== NULL) /*List is empty */ printf(“Can’t delete from an empty list, loop dummy”); The search completed The search loop is looks Alsoifnote that we are relying on== thevalueToBeDeleted) fact thatever the && operator a for else (start->someInteger without finding the the valueToBeDeleted We should also deal with the case that the valueToBeDeleted isn’t in short-circuit operator to keep code from trying to list evaluate valueToBeDeleted start = start->next; /*our Delete first item from */ our list at all, which comes in two flavors: delPtr->next->someInteger when delPtr->next is NULL else /* Traverse the list looking for the valueToBeDeleted */ is empty { The Thelistempty list case The special case for deleting delPtr = start; /* Initialize the traversal pointer */ The search loop terminated it found the the valueToBeDeleted thevalueToBeDeleted first item in the list The list isn’t empty butwhen doesn’t contain while (delPtr->next != example NULL) &&illustrated here, not 23), (that’s 5, in(this single trailing pointer (delPtr->next->someInteger != valueToBeDeleted) We didn’t check for these (leading and trailing) ) this line of code deletes it cases in the two pointer delPtr delPtr->next; /* Move to have next item */ code that we= developed earlier; we should if (delPtr->next == NULL) /* Fell off the end of the list */ Maybe the same code can cover both cases here, maybe it can’t; we’ll printf(“Can’t find %d in the list”, valueToBeDeleted) have to check and see else /* The traversal loop found the item to be deleted */ In any event, let’s look here at the complete code/*for using delPtr->next = (delPtr->next)->next; Do deletion the deletion */ only a} single (trailing) traversal pointer start -2 23 5 The code above covers all the bases delPtr 37 MSJ-26 Linked Lists Introduction and Motivation Building (Insertion Into) a Linked List : Simplest Case Traversal and Traversal-Based Operations Traversal Search/Find Deletion Insertion into the middle (Building an Ordered List) Variations, Embellishments, and Elaborations MSJ-27 Ordered Lists In an ordered list, items are ordered on the basis of some data item, known as the key, as illustrated in the ascending list below Traversal, search, and deletion are the same as we saw before; but insertion is different The earlier (simpler) insertion animations only inserted at one end of the list Now, to keep the list in order, we’ll usually need to insert into the middle, no? Suppose we wish to insert a node with a key of 24 into the list below, how would you go about it? Here’s some pseudocode: Traverse the list to find the place to insert the new item so as to preserve the ordering Adjust appropriate pointers to actually do the insertion We need tothe insert the new 24 newItemForInsertion 24 node before the 30 one start 5 10 30 37 MSJ-28 Linked Lists Introduction and Motivation Building (Insertion Into) a Linear Linked List Traversal and Traversal-Based Operations Variations, Embellishments, and Elaborations Bi-directional lists, including fascinating (or at least important ;-) sidebars on: Topology again l-values, r-values, and how compilers really process an assignment operator; all of which are necessary to understand expressions like a->b->c->d->e = a->b->c->d->e Circular Lists Headed Lists Summary MSJ-29 Bi-Directional Lists Since all we’re interested in is the list Just as a mechanics, structure can have more than making one integer or floating I’m not going to bother up point component, if items we find it useful for list whatever other data to be past of our items problem we’re trying to solve, it can certainly have more than one pointer struct biDirectionalListItem { … struct biDirectionalListItem *previous, *next; }; So here’s what a bidirectional list might look like: start Note that here we used multiple (two, to be precise) pointers of the same type; since that’s all we need for a bidirectional list More complex problems (not simply bidirectional lists) might require multiple pointers of multiple types Later we’ll look at other topologies – multi-linked structures that are not linear (a bi-directional list is still linear) such as orthogonal lists (for sparse matrices) or binary trees, the most beautiful data structures in the universe MSJ-30 Benefits for Bi-Directional Lists It makes deletion a lot cleaner (neither of our two previous deletion algorithms was exactly elegant, were they?) After we find the item to be deleted, we don’t need leading or trailing pointers to help with the deletion delete(D) start A B C D E delPtr Insertion into an ordered list is simplified, too, much as deletion is Traversal in reverse order becomes possible; without a great deal of difficulty, it isn’t possible with a one directional list i. Is that important? It depends; some applications need to be able to traverse in both directions, some don’t ii. Your job, as an engineer, is to know the capabilities, plusses, and minuses of each of the tools/techniques in the standard armamentarium MSJ-31 Sidebar: Programmers and Topologies It’s up to you, the programmer, to insure that you set up the pointers so that your lists have the desired topologic properties – e.g., bidirectional linearity If you wanted to, you could use the struct biDirectionalListItem that we defined on the last slide to make a linked thingy that looked like so: start A B C D E I can’t imagine why anyone would want to create a monstrosity like that (although some of you will probably manage something like it the first few times you try to create a bidirectional linear list ;-) but each item in the monster thingy would still be a struct biDirectionalListItem; only the connection topology would be different C provides features to support pointers and structures, lists are up to you; there are languages that directly support lists and basic list operations (LISP, for example), but neither C nor any of its descendants are among them: the topologic properties of linked structures are up to the programmer MSJ-32 Another Sidebar (Fairly Important): The Assignment Operator (or ‘=’ Sign) The point of this digression is make sure you know what really happens when you write things like delPtr->next->previous = delPtr->previous, and delPtr->previous->next = delPtr->next, the two lines used to do the deletion from the bidirectional list delPtr->next->previous = delPtr->previous Delete(D) start A B C delPtr->previous->next = delPtr->next D E delPtr There’s nothing particularly new or tricky here, but its crucial to what we’re doing so I want to go over the concepts pretty precisely MSJ-33 Another Sidebar: The Assignment Operator In More Detail delPtr->next->previous = delPtr->previous The compiler must do three things for an assignment (=) 1. For the left side: a) Figure out the type of the value of the expression to the left of the = sign (e.g., delPtr->next->previous); it must be an address value (pointer type) or the expression won’t compile b) Generate the code to evaluate that expression in real time when the assignment is executed 2. For the right side: a) Figure out the type of the value for the expression to the right of the = sign; it must be a type that’s legal for the address from the left-side expression to contain (e.g., delPtr->previous = 3.1416*diameter won’t compile) b) Generate the code to evaluate that expression in real time when the assignment is executed 3. Generate the code to store (assign) the computed value from the right hand expression in the memory location whose address will be computed by the code generated for the expression on the left side MSJ-34 The Problem with an Overly Simplistic View of the Assignment Operator (cont’d) But given, for example, the declarations int x,y; if the assignment operation looks like x = y+2, the compiler would seem to have a problem: As far as we know so far, the type of a simple variable, like x, in an expression is the type the variable was declared as, int, in this case But when we look more formally at the details, as we just did, the assignment operator requires the expression on the left to be an address type, not an integer MSJ-35 The Solution So here’s the compiler’s real logic: Ordinarily, the value of a simple variable name like x is what we expect, i.e., the value stored there and the type of that value is the type the variable was declared as But, if the next operator to the right is an assignment operator*, the value of the variable name is the address of the variable and the type of that value/expression is the correct pointer type – e.g., an integer pointer if x is an integer * Not just =, but +=, -=, *=, /=, %=, &=, &&=, ^^=, etc; several of these are used rarely, if ever, but the compiler allows them and they are assignments MSJ-36 The Exception in Evaluating the Assignment Operator (cont’d) To continue being technically precise, a simple variable name is thus overloaded in C, as in most modern imperative languages: It can be evaluated by the compiler to one of two completely different values (bit patterns) depending on where it is in a larger expression To the immediate left of an assignment operator, its value is its address; elsewhere, it’s the bit pattern stored at that address (unless we explicitly use the & operator to ask for its address) The two different possible values for the same variable name are referred to as its l-value and its r-value The l- (or left) value being the address The r- (or right) value being the bit pattern stored there Which value the compiler chooses to use depends on where the variable name is, if it’s in an assignment statement MSJ-37 Sidebar: Assignment Evaluation (cont’d) The evaluation of an expression like delPtr->next->previous = delPtr->previous has the same issue and the same resolution Because it’s to the right of the = sign, Because it’s to the left of the assignment the value operator, the value of this expression will of this expression will be the bit pattern stored in the cell designated be the address of the cell designated by the expression delPtr->previous delPtr->next->previous, namely the address of the cell for the component named previous of the structure 5 10 15 pointed to by delPtr->next Because both expressions are the same type, struct biDirectionalListItem *, or pointer to a struct biDirectionalListItem, the compiler is happy to generate the code to do the evaluations and make the assignment delPtr MSJ-38 Last Sidebar: C is Being Nice to Us for a Change delPtr->next->previous = delPtr->previous Got it? OK, then how about this as a possible exam question: Given the delPtr->next->previous->previous->next->someInteger picture below, consider the expression, expression Each here … -> Theoperator highlighted above, is designating the delPtr->next->previous->previous->next->someInteger … but if ever the highlighted above were notusually to the left of an I can’t imagine needing toexpression write such an expression, we’re component cell named previous in the structure Note the lovelyto (and hardly coincidental) correspondence between theto, working “closer” some named pointer (e.g., delPtr), butr-value, if we need assignment operator, it would be evaluated to its which, pointed to by the highlighted arrows in this picture a) Is it C legal (will itpictures compile)? and we tocell understand thetype, semantics thethe type know of a previous is ait pointer is the Csyntax allows of itsince and you now howneed to decipher (congratulations ;-) address If what the expression is oncell, the left of an = sign, as it is here, ofits some other b) If so, is r-value? its value is the l-value (address) of this cell … 5 … corresponds to one arrow that must be followed to evaluate (understand) the expression 10 15 delPtr MSJ-39 Linked Lists Introduction and Motivation Building (Insertion Into) a Linear Linked List Traversal and Traversal-Based Operations Variations, Embellishments, and Elaborations Bi-directional Lists Circular Lists Headed Lists Summary MSJ-40 Circular Lists aPtrToTheList An empty Andawhat linelist of to be circular list Note that many authors (but not all) consider circular What’s this? you each item merely an implementation technique for acode linearcould list, since Thisand is the general case illustrated write to achieve it? here still has a unique predecessor a unique successor There are ittwo special The successor of an item is the item points to cases, one pretty Not as ubiquitous as linear lists, but still usefulone not normal for linked listsquite in general, The predecessor of an item is the one that points to it Often used in operating systems – which we’ll explore in a If, as shown here, the list is implemented uni-directionally, it won’t bit more detail in CS420, e.g.: have pointers to predecessors, but that’s an implementation issue, not Round robin scheduling a topological one; i.e. – the property that each node has a unique predecessor and amemory unique management successor is the topological, Contiguous withabstract, a first fitor allocation policy property, known as linearity, regardless of whether or not the underlying implementation is uni- or bi-directional (or circular) MSJ-41 Linked Lists Introduction and Motivation Building (Insertion Into) a Linear Linked List Traversal and Traversal-Based Operations Variations, Embellishments, and Elaborations Ordered lists Bi-directional lists Circular lists Headed lists Basic Concept Example: Sparse Matrices Summary MSJ-42 Headed Lists A.k.a. Lists With Sentinels Anyway, the motivation for node) all this is stuff I hope, become that A list header (a.k.a sentinel anwill, item For our simple example, here’s a list in is a to linked containlist only much clearer when we look at orthogonal lists to represent positive integers, but for this example, assume we also Issparse the first item in the list (the head of the list) matrices, coming up next need to keep track of the length of the list for some reason May not be deleted It (sparse matrices) really is a pretty solution to an Rather than declaringspecific) a separate variableorfor length, Has some special (application semantics meaning important problem store the length in a node, but make ititnegative so associated with some value in sentinel some field that identifies a sentinel … and the sentinel must be created dynamically the header isis a adeclared node thatmistaken for Note the Sometimes, difference: Here, start declared as to ensure that the header (sentinel) is not (header) Remind me to bring in and show you a fairly good textbook node ─ the examples will make this clearer, Whereas here, start is nodes and any deletion algorithm mustbeing be sure not I hope; bear starts the list, all other list created variable, but it is a simple pointer not a structure … an Computer ordinary (positive) node (Gollmann, Security), where the author, whom I with me normally to later delete it by accident a declared structure … during execution) (i.e., dynamically, otherwise mostly like, states that a certain implementation of a key data in computer security can’t some be used Headed lists arestructure very useful and very common; problems in most cases because there’s no good, general solution to cannot easilywith be solved any of other way a problem one aspect the implementation start start Bullshit you’ll know to 2and solveimportant that problem after52 a when few we We’ll see-4a–particularly lovely 78 example 17 how here lookmore at ancharts implementation of sparse matrices via orthogonal lists, but let’s start with a simpler example MSJ-43 Linked Lists Introduction and Motivation Building (Insertion Into) a Linear Linked List Traversal and Traversal-Based Operations Variations, Embellishments, and Elaborations Ordered Lists Bi-directional Lists Circular Lists Headed Lists Basic Concept Example: Sparse Matrices Summary MSJ-44 Introduction and Motivation for Sparse Matrices Although sparse matrices themselves are interesting and important objects, they don’t really belong here since they’re not linear lists But they are built from linear lists and what interests us here is that the lists must be headed or we can’t get this sparse matrix structure to do what we need to So looking at sparse matrices will give us a chance to see what drives the need for headed lists and how we work with them MSJ-45 A Multi-Linked Structures Example: Orthogonal Lists for Sparse Matrices A sparse matrix is one where the majority of the entries in the matrix are 0 Economists, for example, might want to keep track of the extent to which changes in the price of one commodity, product, or service (CPS) are correlated with changes in others They prepare a complete list of CPS’s, possibly millions of entries long for a large national economy, then make a square matrix M, where each entry 0 ≤ mi,j ≤ 1 is the correlation coefficient between CPSi and CPSj But the vast majority of the mi,j are all 0; I mean, how much do you think the price of steel correlates with the price of, for example, bubble gum? MSJ-46 A Multi-Linked Structures Example: Orthogonal Lists for Sparse Matrices (cont’d) The natural representation of a matrix in a programming language like C is obviously a 2-dimensional array But if there were a million commodities, the array would have a trillion entries; if most of them were 0, that would be filling a lot of memory with zeroes; that seems wasteful Very few modern systems will let you use a 106 x 106 array in any event Even if such a declaration compiled, it would probably blow up in execution (that’s what happens on prclab) MSJ-47 A Multi-Linked Structures Example: Orthogonal Lists for Sparse Matrices (cont’d) What we want is some other data structure (not an array) that just stores the non-zero elements of M To find the value of some mi,j, we search the structure; if the search is successful, we know the (non-zero) value of mi,j, if the search is unsuccessful, we know its value is 0 One implementation technique for sparse matrices involves what are called orthogonal lists MSJ-48 A Multi-Linked Structures Example: Orthogonal Lists for Sparse Matrices (cont’d) Here’s a diagram of what each member of this sparse matrix structure* would look like: mi,j i nextInColPtr j nextInRowPtr i and j are commodity numbers mi,j is the correlation coefficient between commodity i and commodity j Each item is a member of two separate lists All the non-zero items in row i form a single ordered list, ordered by j (their column number), linked by their nextInRowPtr All the non-zero items in column j form a single ordered list, ordered by i (their row number), linked by their nextInColPtr * The term structure is overloaded here. The sparse matrix is an example of the sort of theoretic object called a data structure that we study in computer science, particularly in CS315. As an implementation matter, each element of the sparse matrix will be a structure in the C programming sense, (Many other languages don’t use the word “structure” this way; Ada, for example, calls such things “records”.) Anyway, the figure, above, is a pictorial representation of the C structure that would comprise one element of a sparse matrix; the next slide illustrates how such elements fit together to make a sparse matrix MSJ-49 An Example of a Multi-Linked Structure: We’re trying to insert m Orthogonal Lists for Sparse Matrices (cont’d) Inserting the new node into the correct row list is easy enough: 882, 713 The problem now is finding the - to find the - correct row - number,- 882 Search the column row headers column listoffor column 713, -1 -1 if it even -1 326 -1 667 -1 713 -1 801 startOfTheMatrix exists,here, whichrow in this it does,but if that row header didn’t exist, In the example 882 example already exists; but ofthat course won’t be elements in that row yet; so we’d it would mean therewe were noalways non-zero 0.31 Assume, example, that you wish existing columns create for theinserting new rowinto by inserting a to new node 152 -1 152 with 326 row number 882 and column know the value of m 56,492 number ofIfis -1 into the column row to headers OK, things are going column 713 exist, weof need Now the search logic easy: The answer to add a ordered row of column One answer couldisbe todoes pretty so far; we can Since thiswell data structure iswe notcan an find array, it so that this node, soor created, find Once the correct row header is found headers to our structure search the entire matrix, 0.62 0.38 you the column of row search this structure toinsert can’t simply ask C to retrieve it168 for that we can new insert mnow into that row Traverse So here’s an illustration a our (very!) -1 node after 168 667 168 801 which we can do 882,713 of headers by following the you Then, after we insert new node find if itm is in it the by out writing by adjusting thehaving nextInColPtr i,jm[56][492] sparse 1000 x 1000 matrix that we added the The solution here is to add a nextInColPtr pointer Since the lists by column number, into the correct row listrow by searching here and init:are our ordered new node only 7 non-zero elements in column of row headers, column of row headers the How about insertion? As discussed earlier, your has the new node,code m ,,to in column of row headers, we can 0.22 - this example, must be 882,713 m , m , m , m looking for entries that 152,326 168,667 168,801 617,713 search Each time you move down to 617 713 Let’s try inserting m 617 -1 for it So how do we find this node, or figure inserted between the nodes for m882,667 and m882,801 882,713 then search the row of column m , m , and m The column is an ordered the column number 882,667 882,801 927,713 ahave new row, traverse it by out that the entire column headers for the correct column (713 indoesn’t linked list, ordered by row #, Where do you start? And what happens, we want following the nextInRowPtr even exist yet? this case), creating a new column if Not very realistic, but it makes the linked bymoving thewhen nextInColPtr for example, youto get to m152,326 0.17 0.53 - or 0.05 before down the necessary … artwork here a lot easier and it will 882 801 882 667 882 -1 But that seems awfully 882 713 mnext , where do you go next and how row ? still show the key issues we’ll have do A927,713 row of sparse matrix …toand then insert the new node into inefficient; there’s got you getour there? to deal with will a row header in its this column list, column lists being behave a better way 0.46row number, of course 0.46 column if and onlyasking if the here, ordered by What we’re really of course, 927 -1 927 713 haswe attraverse least onethis nonismatrix how do beast? It’s zero value in the row not obvious MSJ-50 Here is the Complete Algorithm (Pseudocode) for the Insertion of mi,j start Create the node for mi,j Row insertion: Search the column of row headers to find the header for row i If row i does not yet exist, create a header node for row i and insert it into the column of row headers Insert the mi,j node into row i Column insertion: Search the row of column headers to find the header for column j Note that in this particular problem (insertion into a sparse matrix), the If order column j does not create lists a header node you for column of insertion intoyet rowexist, and column is irrelevant; could doj and the column insertion first, followed by the row insertion, or the row insert it into the row of column headers insertion first followed by the column insertion; there’s no ultimate Insert the msince j are independent of one another i,j node difference, theinto two column operations MSJ-51 … and a negative row # Sentinel Marks for a column header - startOfTheMatrix Note that these header But to a node is of nodes areC,not elements node ismatrix a node theasparse from the Alternatively, for thisisexample The sentinel mark mathematical standpointsome matrix, I could havevalue set the attribute of some for msome value data negative item in the nodefor i,j to some row and column headers, that’s “special” so can be Inboth this example soand far, our since valid correlation usedany to haven’t identify a node as a algorithms actually coefficient be ≥rather 0 sentinel ormust header than needed to identify sentinel a node containing real data nodes, butmatter, let’s complicate For that I could our life a littleI bit ;-) used have mi,j = 0 to actually Here, used a negative mark a header, no real … column # for asince row header element in this structure would have a 0 for mi,j; by definition, the only elements that are supposed to be here are those with non-zero mi,j values -1 -1 326 152 -1 0.31 152 326 -1 168 -1 -1 667 0.62 168 667 927 -1 -1 801 0.38 168 801 0.22 617 713 617 -1 882 -1 -1 713 0.53 882 667 0.05 882 713 0.17 882 801 0.46 927 713 The “real” sparse matrix elements MSJ-52 Sentinel Marks (cont’d) And a row traversal pointer initialized to currentRowPtr - startOfTheMatrix travPtr currentRowPtr Let’s look in slightly more detail at how the traversal algorithm for this structure would work -1 -1 326 152 -1 0.31 152 326 -1 168 -1 -1 667 0.62 168 667 -1 713 -1 801 0.38 168 801 We’ll need a pointer to the current row being traversed, And here’s a complete traversal algorithm: initialized to startOfTheMatrix->nextInColPtr 0.22 617 713 617 -1 while (currrentRowPtr != NULL) { travPtr = currentRowPtr->nextInRowPtr; 0.17 0.53 0.05 while (travPtr 882 801 882 667 882 -1 != NULL) 882 713 { visit(travPtr); travPtr = travPtr->nextInRowPtr; 0.46 0.46 927 -1 927 713 } currentRowPtr = currentRowPtr->nextInColPtr; } MSJ-53 … and then traverse the new(cont’d) row as before by repeatedly Sentinel Marks setting travPtr = travPtr->nextInRowPtr until we reached thefrom end (NULL) Starting the startOfTheMatrix… - startOfTheMatrix -1 -1 -1 326 -1 667 -1 713 -1 801 travPtr 0.31 We already saw the row 152 -1 152 326 The problem comes That’s it; the implementation Mosttraversal: implementations major We at the end of a row … of a… sparse matrix, a multitaking travPtr would probably traversed the column ofright Now at the end of a row, linked structure, built out of back to where we circularize the column row headers and traversed 0.62 0.38 …orthogonal we need tocircular get travPtr back 168 -1 the normal row traversal lists with 168 667 168 801 Of course we recognize by the need itcan towe be so that lists new as well as the row each row came to to here so we move down movement follows this headers with sentinel marks sentinel mark that we’ve just No sweat; let’s just we can move down to lists, so that the matrix Now let’s do it with only a single the next row by following the nextInRowPtr pointer… traversal To do a column major arrived at a row header rather circularize the row lists! the next could be traversed in pointer ‒ no separate …slick, wewe’d could certainly move travPtr down to a new row Pretty no?traverse nextInColPtr; but wethe have 0.22 traversal, than an ordinary sparse matrix either row major order currentRowPtr to keep track 617 713 617 -1 by setting travPtr = travPtr->nextInColPtr … no way to get back here ofrow column headers and element and so we must have or column order ofrow what ismajor being traversed traverse each new column just completed traversing a row as we came to it and so it’s time 0.17 0.53 to follow 0.05the 882 801 882 667 882 -1 882 713 nextInColPtr to get down to a new row rather than following the nextInRowPtr again and going 0.46 0.46 into an infinite loop ─927 circular lists 927 -1 713 being easily prone to that ;-) MSJ-54 Summary of the Sparse Matrix The sparse matrix implementation we just saw built a multilinked structure out of orthogonal circular headed lists The sparse matrix is our first, it surely will not be our last, example of a multi-linked structure, one where each element has more than one pointer component This is a theme we will see over and over again in CS315: complex data structures being built up out of simpler data structures which in turn are built out of simpler data structures until we eventually get to something the language itself supports (pointers, in this example) MSJ-55 Summary of the Sparse Matrix (cont’d) There are multi-linked structure like (yes!) binary trees where the linked elements are not organized into linear lists, but let’s leave that for another day (month, actually ;-) The reason I put this sparse matrix problem here rather than waiting until later in this course has less to do with multilinked structures, although it is a great example, than that the sparse matrix is a pretty example (well, I think it’s pretty ;-) of an important real world problem that uses linked lists and that can’t be done without making those lists headed lists The circularity was added mostly just to torture you – but it did eliminate the need for a separate named pointer to the current row, hardly worth the effort in this case, but under other circumstances, circularity can be more important MSJ-56 Linked Lists Introduction and Motivation Building (Insertion Into) a Linear Linked List Traversal and Traversal-Based Operations Variations, Embellishments, and Elaborations Ordered Lists Bi-directional Lists Circular Lists Headed Lists Summary MSJ-57 Linear List Variants and Embellishments: Summing It All Up You can mix and match all the linked list variants discussed here as your application requires; they are completely independent and every possible combination – e.g., unordered circular unidirectional with a header, bidirectional with a header but not circular, ordered circular no header, etc, etc – has been used for some problem or other Your job, as always, is to know what techniques are available (that’s where Gollmann slipped up) and which are called for by what types of problems Sometimes it’s obvious – if you need to print out ordered lists both forward and backward, bi-directional lists are clearly the way to go Sometimes it’s not – which is why there’s more to good software engineering than merely being a good code-slinger MSJ-58