04 - Lists
04 - Lists
Programmazione
LISTS
Goal
This lecture aims at presenting Lists as ADT,
focusing first on some typical operations and then
on possible implementation.
Prerequisites
Lectures:
◦ Introduction to ADT
Outline
Lists
Operations on lists
Possible implementations of lists
Sentinel value
Double and multiple lists
Outline
Lists
Operations on lists
Possible implementations of lists
Sentinel value
Double and multiple lists
Lists
Lists are a particularly flexible ADT:
◦ allows the insertion, search and deletion of any element
◦ its dimensions can be dynamically changed according to specific needs.
Array vs List
Arrays store elements in contiguous memory
locations, resulting in easily calculable
addresses for the elements stored and this
allows faster access to an element at a
specific index
Linked lists are less rigid in their storage
structure and elements are usually not stored
in contiguous locations, hence they need to
be stored with additional tags giving a
reference to the next element
Array vs List
Array List
Data can exist at scattered (non-contiguous)
Data stored in a contiguous block of memory.
addresses;
Size cannot be altered at runtime. Size can be changed at runtime
Memory allocation at compile time Memory allocation at runtime
In list, each element requires extra space for the
In vector, each element only requires the space
node which holds the element, including pointers
for itself only.
to the next (and previous elements) in the list.
Supports sequential access, so an element can be
Supports random access, so an element can be
accessed only sequentially by going through each
directly accessed using the index.
element until reaching the required element
Array vs List
Array List
Elements are not independent as they contain a
Elements are independent of each other
pointer to the next (previous) node of the list
Insertion is cheap no matter where in the list it
Insertion could be costly.
occurs.
Deletion at the head or end of the vector needs Deletion is cheap no matter where in the list it
constant time but for the rest it is O(n). occurs.
Iterators become invalid if elements are added to Iterators are valid if elements are added to or
or removed from the vector. removed from the list.
Lists
A list is a data structure based on the use of pointers and dynamic memory allocation /
deallocation. It allows greater flexibility in the use of memory than other data structures (e.g.,
arrays) at the cost of lower efficiency.
1 2 5 3 4 1 1 1 1 1
array list
Introduction
Lists: A sequence of zero or more elements of
the same type:
a1, a2,…, an n≥0
Definitions
if n = 0 the list is called empty list
a1 is the first element or head of the list
an is the last element or tail of the list
ai precedes ai + 1 Ɐi: 1 ≤ i <n
ai follows ai-1 Ɐi: 1 <i ≤ n
The element ai is in the i-th position
In the following, the meaning of the term position will depend both on the context in which it is
inserted and on the specific implementation of the list considered.
It may be synonymous with index, address, pointer, ...
Definitions
It is convenient to assume the existence of a position after the last item on the list.
We will assume that the function
eol(L)
returns the value of that position.
Outline
Lists
Operations on lists
Possible implementations of lists
Sentinel value
Double and multiple lists
Outline
The operations on a list are:
◦ insert(x, p, L)
◦ delete(p, L)
◦ locate(x, p , L)
◦ retrieve(p, L)
◦ next(p, L), previous(p, L)
◦ makenull(L)
◦ first(L)
insert(x, p, L)
Inserts the element x at position p, moving all subsequent ones one position ahead.
Returns:
◦ OK if the operation completed successfully
◦ ERROR otherwise
delete(p, L)
Deletes the element at position p, moving back of one position all the elements after p
Returns:
◦ OK if the operation completed successfully
◦ ERROR otherwise
locate(x, p, L)
Returns the position of the first occurrence of x following the position p; if no such position
exists, it returns eol(L)
retrieve(p, L)
Returns the element that is in position p; if this element does not exist, it returns ERROR
next(p, L)
previous(p, L)
Return the position that follows or precedes position p, respectively
If p ∉ [1, n], returns ERROR
Examples
◦ next(n,L) = eol(L)
◦ next(eol(L),L) = ERROR
◦ previous(1,L) = ERROR
makenull(L)
Converts the list L into an empty list and returns eol(L)
first(L)
Returns the first position in the list L.
If L is an empty list, it returns eol(L).
Outline
Lists
Operations on lists
Possible implementations of lists
Sentinel value
Double and multiple lists
Implementation of lists
Different implementations are possible.
The main distinction concerns the position of the next element, which can be
◦ Implicit:
◦ using vectors
◦ Explicit:
◦ using pointers
◦ using indexes.
Implementation using vectors
The elements of the list are stored in contiguous cells of a vector:
◦ cell i contains the element located at position i
◦ the position of the next element is obviously i + 1
last an
.
.
.
maxlength
Implementation using vectors
Advantages and disadvantages:
◦ The vector must be sized according to the maximum number of elements the list has to contain. This
value must be known and specified in advance.
◦ This limitation can be easily overcome in those programming languages, such as C, which allow
dynamically allocating the space occupied by vectors.
◦ The insertion and deletion of any element requires the movement of all subsequent elements.
◦ Only the insertions and deletions at the end of the list are particularly easy
◦ It is used when the number of insertions and deletions in a given position is negligible with respect to
the number of these operation executed at the end of the list
Implementation using pointers
The list is implemented through a set of structs: the i-th struct contains both the i-th element of
the list and a pointer to the (i + 1)-th struct.
A variable (header) is required that points to the first element of the list.
Each item is allocated/deallocated separately
Each element is linked to the others (and therefore accessible) through pointers
Implementation using pointers
header
NULL
Implementation using pointers
header
A struct contains data and
the pointer to the next
element (next)
NULL
Implementation using pointers
header
Pointer to the first
element of the list
(header)
NULL
Implementation using pointers
/* defining a list */
typedef struct list_el{
int code;
char *name; Data
char *surname;
struct list_el *succ; Pointer to the next element
}list_el;
...
NULL
Insert at the beginning
t header
NULL
Dati
p
1. Create a new item
2. Store the Data
Insert at the beginning
t header 3. Copy the reference
pointed by header in the
next field of the new struct
NULL
p
Insert at the beginning
t header 4. Update the header with
the address of the new
struct
NULL
p
Insert at the beginning
t header
NULL
Insert at the beginning
int insert_head(list_el **t,int new_val, char *new_name, char *new_surname){
if (p==NULL)
return (-1);
return (0);
}
Insert at the beginning
int main() {
/* defining the header */
list_el *header = NULL;
...
Why a double pointer?
There are two ways to pass parameters in C: Pass by Value, Pass by Reference.
Pass by Value: Pass by Value, means that a copy of the data is made and stored by way of the
name of the parameter. Any changes to the parameter have no affect on data in the calling
function.
Pass by Reference: A reference parameter "refers" to the original data in the calling function.
Thus any changes made to the parameter are also made to the original variable.
There are two ways to make a pass by reference parameter:
◦ Arrays
◦ Pointers
Using a double pointer
header
list_el* Data
main
Using a double pointer
insert_head(&header, …)
header
list_el* Data
list_el
t
pass the pointer list_el**
main insert_head
Using a double pointer
list_el *p;
p =(list_el*)malloc(sizeof(list_el));
header
list_el* Data
list_el
t p
list_el** list_el*
Data
main insert_head NULL
Using a double pointer
p->succ=*t
header
list_el* Data
list_el
t p
list_el** list_el*
Data
main insert_head
Using a double pointer
*t=p;
header
list_el* Data
list_el
t p
list_el** list_el*
Data
main insert_head
Using a double pointer
return(0);
header
list_el*
Data Data
list_el list_el
main
Using a single pointer
header
list_el* Data
main
Using a single pointer
insert_head(header, …)
header
list_el* Data
list_el t
Data
main insert_head
Using a single pointer
list_el *p;
p =(list_el*)malloc(sizeof(list_el));
header
list_el* Data p
list_el*
Data
list_el t NULL
Data
list_el*
main insert_head
Using a single pointer
p->succ=t
header
list_el* Data p
list_el*
Data
list_el t
Data
list_el*
main insert_head
Using a single pointer
t=p;
header
list_el* Data p
list_el* Data
list_el t
list_el*
Data
main insert_head
Using a single pointer
return(0);
header
list_el* Data
main
Insert at the end
header
NULL
Insert at the end
t header
NULL
NULL
Data
p
NULL
Insert at the end
t header
q
Data Data Data
if (p==NULL)
return (-1);
NULL
Insert in the middle
t header
NULL
NULL
Dati
p
Insert in the middle
t header
4. Scan the list to the
desired position
Insertion position
NULL
Dati
p
Insert in the middle
t header
NULL
q p
NULL
//5. Update the next field of the previous element to the insertion point
q->succ = p;
return(0);
}
Delete
The deletion operations are executed to remove an element from the list
Delete
header
NULL
Delete
t header 1. Check for
head deletion
NULL
Delete
t header
2. Scan the list to the
desired position
NULL
q p
Item to delete
Delete
header p
t Data
Data Data
NULL
NULL
4. Free the memory
Data Data
NULL
q
Delete
int delete (list_el **t, int val){
list_el *p, *q;
q = *t;
List supports sequential access, so an element can be accessed only sequentially by going
through each element until reaching the required element
/* defining an array */
Array
// Access to the second element
...
printf("%d",array[1]);
Access to an element /* defining a list */
typedef struct list_el{
List int data;
// Access to the second element struct list_el *succ;
... struct list_el *prev;
while(q->succ != NULL) }list_el;
{
if(q->data == val){
// Element found do somethings ...
printf("%d", p->code);
}
q = q->succ; // Move to the next element
q = q->prev; // Move to the previous element
}
Implementation using pointers
Advantages and disadvantages:
◦ It is not necessary to know in advance the number of elements
◦ The space occupied is determined only by the number of elements
◦ Inserting and deleting requires only simple changes to the pointers
◦ Pointers take up space.
Implementation using indices
In those languages in which pointers are not available, it is possible to emulate the behavior of
linked lists by using a vector of structs: the second field of each struct contains the position in
the vector of the next element.
Data Data
NULL
sentinel sentinel
Sentinels: unordered lists
In this case two sentinels are usually used (one in the tail and one in the head)
The sentinels allow to simplify the code (the cases of insertion / cancellation at the head and in
the tail no longer have to be considered separately).
Sentinels: ordered lists
In this case the two sentinels contain the minimum and maximum values that can be stored. In
this way:
◦ The code is simplified (you no longer have to consider separately the cases of insertion / cancellation at
the top and at the bottom)
◦ It makes execution faster, as the end-of-list test can be eliminated.
Sentinels
header
prev_node
Insert in the middle 1. Create a new item
2. Store the Data
new
header node Data tail
NULL
NULL
prev_node
Insert in the middle 3. Update the next
pointer of the new node
new
header node Data tail
NULL
prev_node
Insert in the middle
new
header node Data tail
prev_node
Insert in the middle
new
header node Data tail
prev_node
Insert in the middle
header tail
new node
row2 3
5 0 2
0 3 0
1 0 0 row3 1
0 10 9
row4 10 9
References
◦ A.V. Aho, J.E. Hopcroft, J.D. Ullman:
“Data Structures and Algorithms,”
Addison Wesley, Reading MA (USA), 1983, pp 37-74
◦ J. Esakow. T. Weiss
“Data structure: an advanced approach using C,”
Prentice Hall, Englewood Cliffs NJ (USA), 1982, pp 54-237
◦ C.J. Van Wyk:
“Data Structures and C Programs,” Addison Wesley, Reading
MA (USA), 1988, pp 49-128
References
◦ R. Sedgewick:
“Algorithms in C,”
Addison Wesley, Reading MA (USA), 1990, pp 15-34
◦ E. Horowitz, S. Sahni:
“Fundamentals of Computer Algorithms,”
Pittman, London (UK), 1978, pp 65-202
◦ N. Wirth:
“Algorithms + Data Structures = Programs,”
Prentice Hall, Englewood Cliffs NJ (USA), 1976, pp 162-188
Riferimenti
Queste slide sono una rielaborazione del materiale realizzato dal prof. P. Prinetto per il corso di
Algoritmo e Programmazione A.A. 2020/2021 presso il Politecnico di Torino.