Lists Lists
Container ClassesContainer Classes
Many applications in Computer Science require the Many applications in Computer Science require the storage of information for collections of entities e.g. storage of information for collections of entities e.g. a student registration program will store records for a student registration program will store records for students, a payroll system will store records for students, a payroll system will store records for employees etcemployees etc..
Computer Scientists have developed many techniques Computer Scientists have developed many techniques over the years for storing collections of data. These over the years for storing collections of data. These storage structures are known as storage structures are known as containerscontainers..
For the remainder of the course we will study some For the remainder of the course we will study some commonly used containers in Computer Science commonly used containers in Computer Science along with their implementation in an OOP language.along with their implementation in an OOP language.
ListsListsThe List ADTThe List ADT
The list is probably the simplest container type used to The list is probably the simplest container type used to store collections of data. Its specification is as follows:store collections of data. Its specification is as follows:
A list A list LL of type of type TT is a finite sequence of elements of type is a finite sequence of elements of type TT, with , with the following operations defined on it: the following operations defined on it:
Test whether Test whether LL is empty; is empty; Determine the length of Determine the length of LL (or the number of elements in (or the number of elements in LL); ); Insert an element Insert an element ee at position at position pp in List in List LL; ; Find the position of an element Find the position of an element ee in List in List LL; ; Retrieve the element at position Retrieve the element at position pp in List in List LL; ; Delete an element Delete an element ee from from LL; ; Traverse Traverse LL, applying an operation to each element in , applying an operation to each element in LL. .
ListsListsSince a list is a finite sequence of elements it makes Since a list is a finite sequence of elements it makes
sense to identify each element with sense to identify each element with a positiona position in the in the list.list.
This is different from a This is different from a setset of objects where no position of objects where no position is associated with each object. is associated with each object.
We will also assume that the first position in the list is We will also assume that the first position in the list is identified as identified as position 0position 0..
This follows the convention for labelling array elements This follows the convention for labelling array elements in C and C++. in C and C++.
ListsListsSome of the behaviours in the List ADT can be specified Some of the behaviours in the List ADT can be specified
quite trivially.quite trivially.
The following behaviours are more complicated to The following behaviours are more complicated to specify;specify;
InsertInsert LocateLocate DeleteDelete TraverseTraverse
ListsListsinsertinsert
Input:Input: Element e, Element e, Position p. Position p. Process:Process: IF (p > length() + 1) IF (p > length() + 1) THEN THEN PRINT("Error. Inserting beyond end of List") PRINT("Error. Inserting beyond end of List") ELSE ELSE { Move all elements between p and the end of { Move all elements between p and the end of the the list up i.e., to next position in list. } list up i.e., to next position in list. } { Make e the element at position p. } { Make e the element at position p. } { Increment the length. } { Increment the length. } ENDIF ENDIF Output:Output: None None
ListsListsLocateLocate
Input:Input: Element e Element eProcess:Process:{ Note that, if the first position in the list is position 1, rather than{ Note that, if the first position in the list is position 1, rather than position 0, we would have had to initialize the index position 0, we would have had to initialize the index to 1, rather than 0 } to 1, rather than 0 } index index 0 0 WHILE (index < length() AND element at index != e) WHILE (index < length() AND element at index != e) index <- index + 1 index <- index + 1 ENDWHILE ENDWHILE IF (index = length()) IF (index = length()) THEN THEN PRINT("Error. Element did not occur in List") PRINT("Error. Element did not occur in List") RETURN –1 RETURN –1 ELSE ELSE RETURN index RETURN index ENDIF ENDIF Output:Output: position of e in list or –1 if e does not occur in list position of e in list or –1 if e does not occur in list
ListsListsdeletedelete
Input:Input: Element e Element e Process:Process: pos <- locate(e) pos <- locate(e) IF (pos = -1) IF (pos = -1) THEN THEN { Notice that the locate has already printed an error { Notice that the locate has already printed an error message. The current message is therefore not message. The current message is therefore not strictlystrictly necessary ) necessary ) PRINT("Error. Could not delete element") PRINT("Error. Could not delete element") ELSE ELSE { Move all elements between end if list and pos + 1 { Move all elements between end if list and pos + 1 to one before the position where they were } to one before the position where they were } { Decrease the size by 1 } { Decrease the size by 1 } ENDIF ENDIF Output:Output: None None
ListsListstraversetraverse
Input:Input: Operation O Operation O Process:Process: index <- 0 index <- 0 WHILE(index < length()) WHILE(index < length()) { Apply O to the element at index } { Apply O to the element at index } { Store the result at index } { Store the result at index } index <- index + 1 index <- index + 1 ENDWHILE ENDWHILE Output:Output: None None
ListsListsMost applications that maintain information about Most applications that maintain information about
collections of entities usually require that one be collections of entities usually require that one be able to apply a single operation to all the entities in able to apply a single operation to all the entities in the collectionthe collection
e.g. e.g. all employees in a payroll system may be given a all employees in a payroll system may be given a bonus on their salarybonus on their salary
all accounts in a banking system may have interest all accounts in a banking system may have interest calculated on them at the end of each month.calculated on them at the end of each month.
The traverse operation is implemented to perform this The traverse operation is implemented to perform this function. The input to the function is an operation function. The input to the function is an operation which is applied to all objects in the list.which is applied to all objects in the list.
ListsListsArray-Based List ImplementationsArray-Based List Implementations
Now that the List ADT has been specified there are Now that the List ADT has been specified there are many ways in which it can be implemented in a many ways in which it can be implemented in a programming language.programming language.
The easiest implementation is probably an The easiest implementation is probably an implementation based on implementation based on arraysarrays..
When we create a list we declare an array of fixed size. When we create a list we declare an array of fixed size. We can then implement the List ADT operations We can then implement the List ADT operations using array elements e.g. using array elements e.g. if we insert an element at if we insert an element at position p, we move all the elements at position p position p, we move all the elements at position p and above up one ( assuming there is space ) and and above up one ( assuming there is space ) and insert the new element in the free array locationinsert the new element in the free array location. .
ListsListsSome ExamplesSome Examples
An empty list
23.6
56.7
45.2
Inserting numbers into the first three positions
ListsLists
23.6
56.7
45.2
Now insert 5.4 in position 1
23.6
5.4 56.7
45.2
Insert value in position 1
Move elements up
ListsLists
23.6
5.4 56.7
45.2
Deleting 56.7
23.6
5.4 45.2
Remove 56.7 and shift elements down
Locate element
ListsLists
List TerminationList Termination
How does one know where the array-based list terminates ?How does one know where the array-based list terminates ?( remember all array elements will contain values initially – ( remember all array elements will contain values initially –
usually garbage )usually garbage )
There are two possible solutions to the problem;There are two possible solutions to the problem;
1.1. Use a dummy value to indicate the end of the list e.g.Use a dummy value to indicate the end of the list e.g.
23.6
5.4 56.7
45.2
0.0
dummy value
ListsLists
2.2. Keep a length attribute that stores the current length of Keep a length attribute that stores the current length of the list.the list.
Both methods are valid means of determining the length of a Both methods are valid means of determining the length of a list although both will generate different implementations. list although both will generate different implementations.
23.6
5.4 56.7
45.2
end=3
ListsLists
No matter what method we use there are some problems No matter what method we use there are some problems associated with array-based list implementations associated with array-based list implementations particularly that of particularly that of overflowoverflow and and wasted space.wasted space.
Due to the nature of arrays when implemented in a Due to the nature of arrays when implemented in a programming language we always need to specify their programming language we always need to specify their maximum size.maximum size.
Arrays cannot be dynamically created and therefore we have Arrays cannot be dynamically created and therefore we have problems when we run out of array space and are problems when we run out of array space and are required to add new elements – required to add new elements – overflow.overflow.
On the other hand if we declare large arrays and only use a On the other hand if we declare large arrays and only use a fraction of the space then we are wasting valuable fraction of the space then we are wasting valuable memory resources. memory resources.
ListsLists
Array-based List implementation ( Array-based List implementation ( using dummy value for end of using dummy value for end of listlist ) )
Assume the list stores positive floating point numbers and the label Assume the list stores positive floating point numbers and the label DUMMY is defined as -1 to record the end of the list. The class DUMMY is defined as -1 to record the end of the list. The class declaration is as follows:declaration is as follows:
class List class List {{
private:private: float elements[SIZE]; float elements[SIZE];public:public: List(); List(); Bool empty(); Bool empty(); int length(); int length(); void insert(float, int); void insert(float, int); int find(float); int find(float); float retrieve(int); float retrieve(int); void del(float); void del(float); void traverse(float(*op)(float)); void traverse(float(*op)(float));
};};
ListsLists
TheThe traverse() notation traverse() notation
void traverse(float(*op)(float));void traverse(float(*op)(float));
tells the system to expect an operation tells the system to expect an operation *op*op that takes a that takes a float as an input and returns a float as output.float as an input and returns a float as output.
If If LL is a pointer to a list and is a pointer to a list and print()print() has been defined as a has been defined as a function that prints out a float and returns that function that prints out a float and returns that number then the traverse() function can be used as number then the traverse() function can be used as follows;follows;
L->traverse(print);L->traverse(print);
ListsLists
where where print()print() is specified as: is specified as:
float print(float f)float print(float f){{ cout << f << endl; cout << f << endl; return f; return f;} }
Now that the class is declared we can specify its Now that the class is declared we can specify its behaviour.behaviour.
ListsLists
/* To create a list, we simply put the DUMMY element in the first /* To create a list, we simply put the DUMMY element in the first position in the list. */position in the list. */
List::List()List::List(){{
elements[0] = DUMMY;elements[0] = DUMMY;}}
Bool List::empty()Bool List::empty(){{
if ( elements[0] == DUMMY )if ( elements[0] == DUMMY )return true;return true;
elseelsereturn false;return false;
}}
ListsLists
/* To determine the length of the list, we look at all elements in/* To determine the length of the list, we look at all elements in the list until we either find the DUMMY element, or we the list until we either find the DUMMY element, or we reach thereach the end of the array of the elements making up the array */ end of the array of the elements making up the array */
int List::length()int List::length(){{
int i = 0; int i = 0;
while(elements[i] != DUMMY && i < SIZE) while(elements[i] != DUMMY && i < SIZE) i++; i++;
return i; return i;}}
ListsLists
void List::insert(float e, int pos)void List::insert(float e, int pos){{ int i = 0; int i = 0;
/* First go to the end of the list */ /* First go to the end of the list */ while(elements[i] != DUMMY && i < SIZE) while(elements[i] != DUMMY && i < SIZE) i++; i++;
/* Generate an error if there is no space left in the list */ /* Generate an error if there is no space left in the list */ if (i == SIZE) if (i == SIZE) cout << "Error. List is full." << endl; cout << "Error. List is full." << endl; else else /* Generate an error if beyond end of list */ /* Generate an error if beyond end of list */ if (pos > i) if (pos > i) cout << "Error. Beyond end of list." << endl; cout << "Error. Beyond end of list." << endl; else else
ListsLists
{{/* Note that i must be pointing to the DUMMY element./* Note that i must be pointing to the DUMMY element.
We now shift all elements between the end of the list We now shift all elements between the end of the list and the position in which the new element is to be and the position in which the new element is to be inserted one down. However, we must be careful not inserted one down. However, we must be careful not to fall of the end of the array. If the DUMMY element to fall of the end of the array. If the DUMMY element was in the last position in the array, we cannot was in the last position in the array, we cannot shift it as we would try to shift it beyond the end shift it as we would try to shift it beyond the end of the array. In this case we therefore first go of the array. In this case we therefore first go one position back. */ one position back. */ if (i == (SIZE - 1)) if (i == (SIZE - 1)) i--; i--;
for(; i >= pos; i--) for(; i >= pos; i--) elements[i + 1] = elements[i]; elements[i + 1] = elements[i];
/* Now insert the new element */ /* Now insert the new element */ elements[pos] = e; elements[pos] = e; } }}}
ListsLists
int List::find(float e)int List::find(float e){{ /* We initialize the return value ret to -1. It only gets /* We initialize the return value ret to -1. It only gets changed, when we actually find the element we are looking changed, when we actually find the element we are looking for. In other words, if the element does not occur in the for. In other words, if the element does not occur in the list, we return position -1 */ list, we return position -1 */ int i = 0, ret = -1; int i = 0, ret = -1;
while(elements[i] != DUMMY && i < SIZE) while(elements[i] != DUMMY && i < SIZE) { { if (elements[i] == e) if (elements[i] == e) { { ret = i; ret = i; break; break; } } else i++; else i++; } }
ListsLists
if (ret == -1)if (ret == -1) cout << "Error. Element not in list." << endl; cout << "Error. Element not in list." << endl;
return ret; return ret;}}
ListsLists
float List::retrieve(int pos)float List::retrieve(int pos){{ int i = 0; int i = 0;
/* First determine whether the position is actually a legal /* First determine whether the position is actually a legal position. So, we go down the list until we either reach position. So, we go down the list until we either reach the position, the DUMMY element or the end of the list */ the position, the DUMMY element or the end of the list */ while(elements[i] != DUMMY && i < pos && i < SIZE) while(elements[i] != DUMMY && i < pos && i < SIZE) i++; i++;
if (elements[i] == DUMMY || i == SIZE) if (elements[i] == DUMMY || i == SIZE) { { cout << "Error. Illegal position." << endl; cout << "Error. Illegal position." << endl; return DUMMY; return DUMMY; } } else else return elements[pos]; return elements[pos];}}
ListsLists
void List::del(float e)void List::del(float e){{ int i = 0; int i = 0;
/* We go down the list until we find the element */ /* We go down the list until we find the element */ while(elements[i] != DUMMY && i < SIZE) while(elements[i] != DUMMY && i < SIZE) if (elements[i] == e) if (elements[i] == e) break; break; else else i++; i++;
if (elements[i] == DUMMY || i == SIZE) if (elements[i] == DUMMY || i == SIZE) cout << "Error. Element not in list." << endl; cout << "Error. Element not in list." << endl; else else
ListsLists
{{ /* We move all elements one down */ /* We move all elements one down */ while(elements[i + 1] != DUMMY && ((i + 1) < SIZE)) while(elements[i + 1] != DUMMY && ((i + 1) < SIZE)) { { elements[i] = elements[i + 1]; elements[i] = elements[i + 1]; i++; i++; } }
/* Now re-insert the DUMMY */ /* Now re-insert the DUMMY */ elements[i] = DUMMY; elements[i] = DUMMY; } }}}
ListsLists
void List::traverse(float (*f)(float))void List::traverse(float (*f)(float)){{ int i = 0; int i = 0;
while(elements[i] != DUMMY && i < SIZE) while(elements[i] != DUMMY && i < SIZE) { { elements[i] = (*f)(elements[i]); elements[i] = (*f)(elements[i]); i++; i++; } }}}
Apply the operation *f to an element and store the new element in the array.