| Topic #3 |
| Ordered List ADTs | ||
| What are they | ||
| Discuss two different interpretations of an “ordered list” | ||
| Are manipulated by “position” | ||
| Use of Data Structures for Ordered Lists | ||
| arrays (statically & dynamically allocated) | ||
| linear linked lists | ||
| Now that we have seen a simple list example, and started to examine how we can use different data structures to solve the same problem | ||
| We will move on to examine an ordered list ADT | ||
| implemented using a linear linked list, circular linked list, linked list of linked lists, doubly linked lists, etc. | ||
| So, what is an ordered list? | ||
| Immediately, a sorted list comes to mind | ||
| But...this isn’t the only definition! | ||
| think of lists ordered by time, | ||
| grocery lists? In the order you think about it | ||
| to-do lists? In priority order... | ||
| In fact, typically an ordered list is: | ||
| ordered by “position” | ||
| whereas a “sorted” list is a “value oriented list”, this is a “position oriented list” | ||
| But, an ordered list has many many different interpretations | ||
| We will discuss two: | ||
| absolute lists | ||
| relative lists | ||
| Why? Well, there are a variety of interpretations of where data is inserted and whether or not the list has “holes” | ||
| An absolute ordered list | ||
| may have holes | ||
| which means if the first data inserted is at position 12, there are 11 holes (1-11) prior | ||
| may “replace” data if inserted at the same position (another insert at position 12 could replace the previously inserted data at that position...it never shifts the data) | ||
| similar to “forms” such as a tax form | ||
| A relative ordered list | ||
| may not have holes | ||
| which means if the first data inserted is at position 12, it would actually be inserted at the first position! | ||
| may “shift” data if inserted at the same position (another insert at position 1 would shift what had been at position 1 -- now to be at position 2) | ||
| similar to “editors” such as vi | ||
| For an absolute ordered list, what are the operations? | ||
| insert, retrieve, remove, create, destroy, display | ||
| insert, retrieve, and remove would all require the client program to supply a position number | ||
| notice we are not inserting in sorted order, or retrieving by a “value” | ||
| instead we insert at an absolute position, retrieve the data at that position, remove data at a given position --- not affecting the rest of the list! | ||
| For a relative ordered list, what are the operations? (the same!) | ||
| insert, retrieve, remove, create, destroy, display | ||
| insert, retrieve, and remove would all require the client program to supply a position number | ||
| instead we insert at a relative position, retrieve the data at that position, remove data at a given position --- this time affecting the rest of the list! | ||
| A remove at position 1 would require every other piece of data to shift down (logically) | ||
| Notice what was interesting about the last two slides | |
| The operations for a relative and absolute list are the same | |
| One exception is that a relative list might also have an “append” function | |
| And, an absolute list might restrict insert from “replacing”, and add another function to specifically “replace” |
| Therefore, the client interface for these two interpretations might be identical! | ||
| so...how would the application writer know which type of list the ADT supports? | ||
| Documentation! Critical whenever you implement a list | ||
| What does it mean to insert? Where? | ||
| What implications does insert and remove have on the rest of the data in the list? | ||
| class ordered_list { | |
| public: | |
| ordered_list(); | |
| ~ordered_list(); | |
| int insert(int, const data & ); | |
| int retrieve(int, data &); | |
| int display(); | |
| int remove(int); |
| With the previous class public section | ||
| the constructor might be changed to have an integer argument if we were implementing this abstraction with an array | ||
| the int return types for each member function represent the “success/failure” situation; if more than two states are used (to represent the error-code) ints are a good choice; otherwise, select a bool return type | ||
| Now let’s examine various data structures for an ordered list and discuss the efficiency tradeoffs, based on: | ||
| run-time performance | ||
| memory usage | ||
| We will examine: | ||
| statically/dynamically allocated arrays | ||
| linked lists (linear, circular, doubly) | ||
| Statically Allocated array... | ||
| private: | ||
| data array[SIZE]; | ||
| int number_of_items; | ||
| Absolute lists: | ||
| direct access (insert at pos12 : array[11] = ... | ||
| remove only alters one element (resetting it?) | ||
| problem: memory limitations (fixed size) | ||
| problem: must “guess” at the SIZE at compile time | ||
| Relative lists: (statically allocated arrays) | ||
| direct access for retrieve | ||
| problem: searching! Insert might cause the data to be inserted somewhere other than what the position specifies (i.e., if the position # is greater than the next “open” position) | ||
| problem: shifting! Remove, insert alters all subsequent data | ||
| problem: memory limitations (fixed size) | ||
| problem: must “guess” at the SIZE at compile time | ||
| Dynamically Allocated array... | ||
| private: | ||
| data * array; | ||
| int number_of_items; | ||
| int size_of_array; | ||
| Absolute lists: | ||
| direct access (insert at pos12 : array[11] = ... | ||
| remove only alters one element (resetting it?) | ||
| problem: memory limitations (fixed size) | ||
| Relative lists: (dynamically allocated arrays) | ||
| direct access for retrieve | ||
| problem: searching for the correct position for insert | ||
| problem: shifting with insert and remove | ||
| problem: memory limitations (fixed size) | ||
| What this tells us is that a dynamically allocated list is better than a statically allocated list (one less problem) | ||
| if the cost of memory allocation for the array is manageable at run-time. | ||
| may not be reasonable if a large quantity of instances of an ordered_list are formed | ||
| is not required if the size of the data is known up-front at compile time (and is the same for each instance of the class) | ||
| We also should have noticed from the previous discussion that... | |||
| absolute ordered list are well suited for array implementations, since they are truly direct access abstractions | |||
| relative ordered list are rather poorly suited for arrays, since they require that data be shifted | |||
| therefore, hopefully the array consists of pointers to our data, so that at least when we shift we are only moving pointers rather than the actual data!! | |||
| Linear Linked list... | ||
| private: | ||
| node * head; | ||
| node * tail; //???helpful? | ||
| Absolute lists: (a poor choice) | ||
| holes: how to deal with them? add a position number to the contents of each node....don’t really allocate nodes for each hole!!!! | ||
| insert, retrieve, removal requires traversal | ||
| how can a tail pointer help? if data is entered in order by position! | ||
| Relative lists: (linear linked lists) | ||
| no holes -- so no extra position needed in each node | ||
| insert, retrieve, remove requires traversal | ||
| a tail pointer assists if appending at the end | ||
| no shifting!! | ||
| So, while we still have to “search”, and the search may be more expensive than with an array -- this is greatly improved for a relative list, since there is not shifting!! | ||
| Circular Linked list... | |
| private: | |
| node * head; | |
| node * tail; //???helpful? | |
| There is nothing in an ordered list that will benefit from the last node pointing to the first node | |
| A circular linked list will require additional code to manage, with no additional benefits | |
| Doubly Linked list... | |
| private: (each node has a node * prev) | |
| node * head; | |
| node * tail; //???helpful? | |
| Again, there is nothing in an ordered list that will benefit from each node having a pointer to the previous node. | |
| UNLESS, there were operations to backup or go forward from the “current” position. In this case a doubly linked list would be ideal | |
| What about a linked list of arrays | ||
| where every n items are stored in the first node, the next n items are stored in the second node, etc. | ||
| This still requires traversal to get to the right node, but then from there direct access can be used to insert, retrieve, remove the data | ||
| May be the best of both worlds for relative lists, limiting the amount of shifting to “n” items while at the same time keeping the traversal to a manageable level | ||
| Are there other alternatives? | ||
| How about an array of linked lists? | ||
| How about “marking” data in a relative list as “empty” to avoid shifting with an array?! | ||
| Given the data structures discussed, which is best and why for: | ||
| absolute ordered list | ||
| relative ordered list | ||
| Now that we have applied data structures to an ordered list | |
| We will move on to examine stack and queue abstractions | |
| Again, we will examine them from the standpoint of arrays, linked list, circular linked list, linked list of linked lists, doubly linked lists, etc. beginning next time! |
| Programming Assignment Discussion |