This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
haas:fall2014:data:projects:dska0-solution [2014/10/31 20:20] – [Appending] wedge | haas:fall2014:data:projects:dska0-solution [2014/10/31 22:38] (current) – [Linked List Code Inversion] wedge | ||
---|---|---|---|
Line 29: | Line 29: | ||
{{ : | {{ : | ||
- | Note that the NULL terminating the list is to the left of the list start... so again, an inversion or negation what we are used to seeing. | + | Note that the NULL terminating the list is to the left of the list start... so again, an inversion or negation |
- | The trick here is that we still view this list from left to right-- start is on the left (or beginning), and end is on the right (the contextual end). In this scenario, the connections work against the grain when using the list. | + | The trick here is that we still "view" |
=====Appending===== | =====Appending===== | ||
Line 53: | Line 53: | ||
So, positive becomes negative, plus becomes minus. All inclusive becomes conditionally inclusive (and vice versa). | So, positive becomes negative, plus becomes minus. All inclusive becomes conditionally inclusive (and vice versa). | ||
- | Here with the previous-oriented linked-list, | + | =====Linked List Code Inversion===== |
+ | Here with the previous-oriented linked-list, | ||
+ | |||
+ | * **insert()** in a next-oriented singly-linked list is functionally equivalent to **append()** in a previous-oriented singly-linked list | ||
+ | * **append()** in a next-oriented singly-linked list is functionally equivalent to **insert()** in a previous-oriented singly-linked list | ||
+ | * To display the list from start to end in a previous-oriented singly-linked list, we effectively need to use the **displayb()** logic to assist us in our task. | ||
+ | |||
+ | More precisely, to convert the next-oriented linked-list **insert()** into a previous-oriented linked-list **append()**: | ||
+ | * all **next** references become **prev** | ||
+ | * all references to **start** become **end** | ||
+ | * all original references to **end** become **start** | ||
+ | |||
+ | ====positioning tmp==== | ||
+ | You'll see that, instead of seeking from the start of the list, we must seek from the end. And just as with next-insert(), | ||
+ | |||
+ | The only difference here (if we were to implement **getpos()/ | ||
+ | |||
+ | <code c> | ||
+ | // next-oriented insert() tmp placement | ||
+ | tmp = setpos(theList, | ||
+ | </ | ||
+ | |||
+ | We instead need to: | ||
+ | |||
+ | <code c> | ||
+ | // prev-oriented append() tmp placement | ||
+ | tmp = setpos(theList, | ||
+ | </ | ||
+ | |||
+ | Note the -1 and +1 difference. | ||
+ | ====navigating with while==== | ||
+ | Obviously, since **getpos()/ | ||
+ | |||
+ | Likely, something of the form: | ||
+ | |||
+ | <code c> | ||
+ | tmp = myList -> end; | ||
+ | while (tmp -> prev != place) | ||
+ | { | ||
+ | tmp = tmp -> prev; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====average case connection==== | ||
+ | And then we could perform the necessary hook-ups (average case consideration): | ||
+ | |||
+ | <code c> | ||
+ | newNode -> prev = place; | ||
+ | tmp -> prev = newNode; | ||
+ | </ | ||
+ | |||
+ | ====edge case connections==== | ||
+ | As for edge cases, aside from dealing with similar NULL and empty list scenarios, the edge cases for prev-append() would be the same as those considered in next-insert(). | ||
+ | |||
+ | * next-insert() had special consideration for inserting before the start of the list | ||
+ | * prev-append() therefore has special consideration for appending after the end of the list | ||
+ | |||
+ | Similarly, as we just observed with the while loop logic: | ||
+ | |||
+ | * next-insert() had to locate the node one list position prior to place (coming from the direction of theList -> start) | ||
+ | * prev-append() therefore has to locate the node one list position after place (coming from the direction of theList -> end) | ||
+ | |||
+ | =====example append() implementation===== | ||
+ | To aid in understanding, | ||
+ | |||
+ | <code c 1> | ||
+ | #include " | ||
+ | |||
+ | ////////////////////////////////////////////////////////////////////// | ||
+ | // | ||
+ | // append() - add newNode to the list after place | ||
+ | // | ||
+ | List *append(List *theList, Node *place, Node *newNode) | ||
+ | { | ||
+ | ////////////////////////////////////////////////////////////////// | ||
+ | // | ||
+ | // tmp will be our " | ||
+ | // to the right of place to connect in to newNode | ||
+ | // | ||
+ | Node *tmp = NULL; | ||
+ | |||
+ | ////////////////////////////////////////////////////////////////// | ||
+ | // | ||
+ | // edge case: should the list pointer be NULL, allocate a new List | ||
+ | // and initialize theList -> start and end to sane | ||
+ | // initial values. This is essentially what mklist() | ||
+ | // does in the sll projects. | ||
+ | // | ||
+ | if (theList | ||
+ | { | ||
+ | theList | ||
+ | theList -> start = NULL; | ||
+ | theList -> end = NULL; | ||
+ | } | ||
+ | |||
+ | ////////////////////////////////////////////////////////////////// | ||
+ | // | ||
+ | // edge case: the only thing that can gum up the works now is a | ||
+ | // NULL newNode; so if one is encountered, | ||
+ | // and return the list as-is. | ||
+ | // | ||
+ | if (newNode | ||
+ | { | ||
+ | ////////////////////////////////////////////////////////////// | ||
+ | // | ||
+ | // edge case: if we have an empty list, newNode becomes the | ||
+ | // list contents. | ||
+ | // | ||
+ | if ((theList -> start == NULL) && | ||
+ | (theList -> end == NULL)) | ||
+ | { | ||
+ | ////////////////////////////////////////////////////////////// | ||
+ | // | ||
+ | // this is the simplest case- nothing exists so newNode IS | ||
+ | // the list contents. Merely update the start and end | ||
+ | // pointers and we're all set. | ||
+ | // | ||
+ | theList -> start = newNode; | ||
+ | theList -> end = newNode; | ||
+ | } | ||
+ | |||
+ | ////////////////////////////////////////////////////////////// | ||
+ | // | ||
+ | // edge case: if we're appending after end, we need to take | ||
+ | // care to update theList -> end | ||
+ | // | ||
+ | else if (place | ||
+ | { | ||
+ | ////////////////////////////////////////////////////////////// | ||
+ | // | ||
+ | // this is the next simplest case to connect, as we only have | ||
+ | // to deal with one connection (hooking newNode into the list; | ||
+ | // then update theList -> end as appropriate). | ||
+ | // | ||
+ | newNode -> prev = theList -> end; | ||
+ | theList -> end = newNode; | ||
+ | } | ||
+ | |||
+ | ////////////////////////////////////////////////////////////// | ||
+ | // | ||
+ | // average case: if we're dealing with a typical append, we | ||
+ | // do the following. We're also protecting | ||
+ | // | ||
+ | // | ||
+ | // | ||
+ | // | ||
+ | // | ||
+ | else if (place | ||
+ | { | ||
+ | ////////////////////////////////////////////////////////////// | ||
+ | // | ||
+ | // positioning tmp: we can only navigate from the end of the | ||
+ | // list, so we must start tmp there; then, | ||
+ | // keep seeking until we arrive at the node | ||
+ | // one prev away from place. | ||
+ | // | ||
+ | tmp = theList -> end; | ||
+ | while (tmp -> prev != place) | ||
+ | { | ||
+ | tmp = tmp -> prev; | ||
+ | } | ||
+ | |||
+ | ////////////////////////////////////////////////////////////// | ||
+ | // | ||
+ | // connecting it: newNode connects to place, and the existing | ||
+ | // list node connecting to place hooks to | ||
+ | // newNode. | ||
+ | // | ||
+ | newNode -> prev = place; | ||
+ | tmp -> prev = newNode; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | ////////////////////////////////////////////////////////////////// | ||
+ | // | ||
+ | // whether we modify the list or bail out, we'll always return | ||
+ | // the right thing. | ||
+ | // | ||
+ | return(theList); | ||
+ | } | ||
+ | </ |