Chapter 8 Poniters and Linked Lists
Chapter 8 Poniters and Linked Lists
1. The pointers
Pointers play a fundamental role in many algorithms and programming languages, particularly in C.
In algorithmics, pointers allow to access and manipulate data efficiently by referencing their memory
address instead of direct values.
A pointer is a variable that contains the memory address of another variable. Therefore, when we
handle a pointer in C, we have direct control over the computer memory. This provides considerable
flexibility and power in designing efficient algorithms. Using pointers, programmers can dynamically
allocate and free up memory, pass data addresses between functions, and implement complex data
structures such as chained lists and trees.
1.1.Definition
A pointer is a variable that stores the memory address of another variable. It allows indirect access
to the value stored at that address. A pointer is defined by its :
name,
pointed type and
memory location reserved for a specific address (Value).
In Algorithmics In C language
Allocate(ptr); // Dynamic memory allocation ptr = malloc(sizeof(int));
free (ptr); // Dynamic memory deallocation free (ptr) ;
In Algorithmics In C language
ptr* or ptr *ptr
ptr
@
x ptr* or ptr
Notes :
The pointer must contain a valid address.
The address of a variable “x” is retrieved by : @x (In Algorithmics) and &x (In C language)
x x
ptr2 ptr2
ptr1* ptr1* or ptr2*
nil @x
Page | 2
Chapter 8 : Pointers and Linked Lists
Syntax
In Algorithmics In C language
ptr2* ptr1* ; *ptr2 = *ptr1 ;
Before copying the value After copying the value
ptr1 ptr1
@x @x
x x
42 42
ptr2 ptr2
@y y @y y
30 42
Note : The pointed values become the same but the pointers are different.
In this example, we will explore pointer operations by applying them to a Student structure. This
structure contains a set of fields such as name, first name, date of birth and average. In addition, we will
use an auxiliary structure, Date, to store birth date information. This example will illustrate how pointer
operations can be used to efficiently manipulate structures and access to their members dynamically.
The comments included in the code will provide detailed explanations for each step, thus allowing a
thorough understanding of the interactions between pointers and structures in a practical context.
In Algorithmics
Algorithm Example_pointer_operations;
Page | 3
Chapter 8 : Pointers and Linked Lists
write("Enter the birth date of student 1 (day, month and year): ");
read(st1.birth_date.day,st1.birth_date.month,st1.birth_date.year);
ptrst1 @st1; //Initialization of ptrst1 pointer with the address of st1 structure
write("Enter the birth date of student 2 (day, month and year): ");
read((*ptrst2).birth_date.day,(*ptrst2).birth_date.month,(*ptrst2).birth_date.year);
allocate(ptrst4);//Allocate memory dynamically for a Student structure and assign its address to the pointer ptrst4
*ptrst4*ptrst2;//Assigning the value of the structure pointed by ptrst2 to the structure pointed by ptrst4
Page | 4
Chapter 8 : Pointers and Linked Lists
free(ptrst2); // freeing the allocated memory dynamically pointed by the ptrst2 pointer
ptrst2 nil;
// free(ptrst3); // Cannot be used because the pointer ptrst3 refers the same structure as ptrst1
free(ptrst4); // freeing the allocated memory dynamically pointed by the ptrst4 pointer
ptrst4 nil;
End. /* ********** Main Algorithm ********** */
In C language
#include <stdio.h>
#include <stdlib.h>
// definition of Date Structure
struct Date {
int day;
int month;
int year;
};
// definition of Student structure
struct Student {
char name[20];
char f_name[20];
struct Date birth_date;
float average;
};
int main() { /* ********** Main Program ********** */
// 1. Dynamic memory allocation and deallocation
// Declaration of a Student structure instance (st1) without dynamic allocation
struct Student st1;
// reading information for student 1
printf("Name of student 1: ");
scanf("%s", st1.name);
printf("First name of student 1: ");
scanf("%s", st1.f_name);
printf("Birth date of student 1 (day month year): ");
scanf("%d%d%d",&st1.birth_date.day,&st1.birth_date.month,&st1.birth_date.year);
printf("Average of student 1: ");
scanf("%f", &st1.average);
// 2. Access to the pointed value
// Declaration of a pointer to Student structure ptrst1 and initialization with
// the address of st1
struct Student* ptrst1 = &st1;
// 3. Initializing and assignment of a pointer
// Declaration of other pointers (ptrst2,ptrst3) and dynamic allocation (ptrst2)
struct Student* ptrst2 = NULL;
struct Student* ptrst3 = NULL;
ptrst2 = malloc(sizeof(struct Student));
// Inputting information for student 2 via the ptrst2 pointer
printf("Name of student 2: ");
scanf("%s", (*ptrst2).name);
printf("First name of student 2: ");
scanf("%s", (*ptrst2).f_name);
printf("Birth date of student 2 (day month year): ");
Page | 5
Chapter 8 : Pointers and Linked Lists
Page | 6
Chapter 8 : Pointers and Linked Lists
2. Linked lists
2.1. Introduction
Linked lists are fundamental data structures in algorithmics and programming, used to store and
organize data dynamically. Unlike static arrays, linked lists provide enhanced flexibility in accommodating
insertions, deletions, and modifications of elements. They are constructed from a sequence of elements
called nodes, interconnected by links (or pointers). This dynamic Node allows linked lists to easily adapt
to changes in data size and structure, making them a preferred choice in many computer applications.
Linked lists were born out of the need to overcome the limitations of static arrays, particularly the
difficulty of managing data whose size is unknown in advance or may fluctuate during program execution.
Thanks to their dynamic structure, linked lists provide an elegant solution to these problems by allowing
memory allocation as needed, avoiding space wastage and optimizing resource utilization.
However, their main drawback resides in their use of additional memory to store pointers, which can
lead to inefficient memory usage in some cases. Additionally, sequential access to elements can result in
poorer performance compared to static arrays in certain situations.
Despite these disadvantages, linked lists are widely used in many areas of computer science,
including the development of text editors, web browsers, database management systems, and search
and sort algorithms. Furthermore, linked lists allow the implementation of complex data structures such
as stacks and queues, making them a versatile tool in software development.
In this chapter, we will explore in detail the fundamental concepts of linked lists in algorithmics, as
well as their practical implementation in the C language. We will explore different methods to create,
manipulate, and traverse linked lists, focusing on basic operations such as insertion, deletion, and
element search.
head tail
Page | 7
Chapter 8 : Pointers and Linked Lists
Where :
Node : A node in a linked list is a structure that contains two components:
Data: It holds the actual value or data associated with the node.
Next : It stores the link (pointer) to the next node in the sequence.
Head and Tail: The linked list is accessed through the head node, which points to the first node
in the list. The last node in the list points to “nil”, indicating the end of the list. This node is known
as the tail node.
2.4. Types of linked lists
There are three main types of linked lists : Singly, doubly or circular linked lists.
2.4.1. Singly linked lists
Singly linked lists, also known as simply linked lists, are a type of linked list where each
node contains a data element and a link (or pointer) to the next node in the sequence. The last
node in the list points to nil, indicating the end of the list. Traversal a singly linked list is
possible only in one direction, typically from the head (the first node) to the tail (the last node).
The singly linked list is shown in Figure 1.
head tail
Nil data_1 Next prev data_2 next ……… prev data_N-1 next prev data_N nil
……....
head tail
Static representation : in this representation a static linked list uses an array to store its nodes.
This means that the size of the list must be determined in advance and cannot be changed
dynamically.
The dynamic representation : this representation involves the allocation of memory for each node
dynamically, which consists of a value field and a pointer field to the next node ; The size of the list
can be changed during the program's execution. The dynamic linked list is often more flexible and
easier to use than the static linked list.
Notes :
In this course, we only study the dynamic representation of singly linked lists.
In C language, the nodes can be dynamically allocated using the malloc() function.
Figure 4 : Memory state of the dynamic singly linked list of integers 42, 9, 11, 0, and 55
Page | 9
Chapter 8 : Pointers and Linked Lists
Note :
The general syntax to define a new simple type is as follows:
In algorithmic : Type Name_NewType = Existing_Type ;
In C language : typedef Existing_Type Name_NewType ;
Where :
- Existing_Type : represents the existing data type used to define a new type.
- Name_NewType : is the name of the new defined type.
For example :
To define a new type (called Pointer) that points to Node structure we use :
* In algorithmics : Type Pointer = *Node ;
2.8. Operations on dynamic singly linked lists (case of the list of integers)
2.8.1. Initialization of the dynamic singly linked list
This operation initializes a dynamic linked list.
In Algorithmics
Procedure Initialize_list (var L : List)
Begin
L nil ;
End ;
Page | 10
Chapter 8 : Pointers and Linked Lists
In C Language
void Initialize_list (List *L){
(*L) = NULL ;
}
In C Language
int Is_empty_list(List L){
return (L == NULL) ;
}
This function "Is_empty_list" takes as input a dynamic linked list and returns a boolean
indicating whether the list is empty or not. It returns "true" if the list «L» is empty, otherwise it
returns "false".
2.8.3. Determination of the length of a dynamic singly linked list
This function calculates the length of a dynamic linked list by traversing all the nodes of the
list and counting their number.
In Algorithmics
Function Length_list (L : List) of integer
Var number : integer ;
current : pointer ;
Begin
current L ;
number 0 ;
while (current ≠ nil) do
current current*.next ;
number number + 1 ;
endwhile
return (number) ;
End ;
In C Language
int Length_list(List L){
List current = L ;
int number = 0 ;
while (current != NULL) {
current = currentnext ;
number ++ ;
}
return (number) ;
}
The function takes as input a single formal parameter which is a pointer to the first node (head)
of the linked list and returns an integer corresponding to the number of nodes present in the list.
Page | 11
Chapter 8 : Pointers and Linked Lists
In C Language
void Insert_beginning_list(List *L, int x){
Node* current = malloc(sizeof(Node)) ;
(*current).value = x ; //or currentvalue = x ;
currentnext = (*L) ;
(*L) = current ;
}
It takes as a formal parameter (passed by address) a linked list "L" and an integer "x" and
inserts a new element containing the value "x" at the beginning of the linked list "L".
The function starts by allocating memory for a new node, whose address is stored in the
"current" variable of pointer type. Then, the value of the element to be inserted is stored in the
"value" field of the "current" pointer. The "next" field of the "current" pointer is initialized with the
existing list "L". Finally, the pointer "L" is updated to point to the new node, so that it becomes
the first element of the list. In summary, the function creates a new node, inserts it at the
beginning of the list, and modifies the head of the list to point to this new node.
2.8.5. Insertion at the end of the dynamic singly linked list
This function inserts a new node at the end of a dynamic singly linked list.
In Algorithmics
Procedure Insert_end_list(var L: List, x: integer
Var current, p: pointer;
Begin
Allocate (current);
current*.value x ;
current*.next nil;
if (Is_empty_list(L)) then
L current ;
else
p L;
while (p*.next ≠ nil) do
p p*.next;
endwhile
p*.next current;
endif
End ;
Page | 12
Chapter 8 : Pointers and Linked Lists
In C Language
void Insert_end_list(List* L, int x){
Node *current = malloc (sizeof (Node));
currentvalue = x;
currentnext = NULL;
if (Is_empty_list(*L))
(*L) = current;
else {
Node* end = (*L);
while ((endnext) != NULL) {
end = endnext;
}
endnext = current;
}
}
Page | 13
Chapter 8 : Pointers and Linked Lists
In C Language
void Insert_middle_list (List* L, int x, int k){
/* we assume that the insertion position “k” is between 1 and the size of the list (with size>=2)*/
int pos=1;
Node* previous=(*L);
Node *current=previous->next;
while (pos!=k-1){
pos++;
previous=current;
current=current->next;
}
Node *p=malloc(sizeof(Node));
p->value=x;
p->next=current;
previous->next=p;
}
The Insert_middle_list procedure allows inserting a new node into a dynamic linked list at a
specific position k. To do this, we assume that the value of k passed as a parameter is between
1 and the size of the list, and the list size is greater than or equal to 2.
The insertion process begins by searching for the insertion position by traversing the linked
list from its head until the desired position is reached. We use the following variables to perform
this search:
The variable “pos” is initialized to 1 to follow the current position when traversing the list.
The pointer “previous” is initialized with the address of the list’s head and points to the node
that precedes the insertion position.
The current pointer is used to traverse the list from the second node and points to the node
following the insertion position.
Utilizing a while loop, the procedure iterates through the list until the current position (pos)
equals k-1, which is the position just before we insert the new element. At each iteration, the
"pos" variable is incremented, and the "previous" and "current" pointers are updated to point to
the next nodes in the list.
Once the insertion position is reached, a new node "p" is dynamically allocated in memory
using the "allocate" function (malloc in C language). The “x” value is assigned to the value field
of the newly created "p" node. Then, the next field of the "p" node is initialized with the "current"
pointer, which points to the current node at the insertion position. Finally, the next field of the
"previous" node is updated to point to the newly created "p" node, thus inserting the new
element between the "previous" and "current" nodes.
Page | 14
Chapter 8 : Pointers and Linked Lists
In C Language
void Delete_beginning_list (List *L){
if (Is_empty_list(*L))
printf("Deletion error : the list is empty \n") ;
else{
Node* current = (*L) ;
(*L) = (*L)next ;
free(current) ;
}
}
The function starts by calling the function "Is_empty_list" to check if the list is empty or not; if
the list is empty, it displays an error message, otherwise, it assigns to the current pointer (Node
to be deleted) the address of the first element of the list L. It updates the list L by pointing to the
next Node in the list. Finally, it frees the memory allocated to the first element of the list (the
space pointed to by current).
2.8.8. Deletion at the end of the dynamic singly linked list
This function allows to delete the last element of a dynamic singly linked list.
In Algorithmics
Procedure Delete_end_list (var L : List)
Var end,prev_end : pointer ;
Begin
if (Is_empty_list(L)) then
write ("Deletion error: the list is empty ") ;
else
if (L*.next=nil) then
end L ;
Initialize_list (L) ; // or : L nil ;
else
prev_end L ;
end prev_end*.next;
while (end*.next ≠ nil) do
prec_fin fin;
end end->next ;
endwhile
next_end*.next nil ;
endif
free(end);
endif
End ;
In C Language
void Delete_end_list (List* L){
if (Is_empty_list(*L))
printf("Deletion error: the list is empty \n") ;
else{
Node *end, *prev_end;
if ((*L)next==NULL) {
end=(*L);
Initialize_list(L);
}
else {
prev_end = (*L);
end = prev_endnext;
while (endnext!=NULL) {
prev_end = end;
end = endnext ;
}
prev_endnext=NULL;
Page | 15
Chapter 8 : Pointers and Linked Lists
}
free(end);
}
}
The procedure is define with a single formal parameter "L" of type List (passed by address),
representing a pointer to the first element of the list.
Initially, we verify whether the list is empty. If it is, we display an error message and exit the
procedure. In case the list isn’t empty, we check if it contains only one element. If so, we assign
its address to the “end” pointer and reset the list (list becomes empty).
If the list contains more than one element, we initialize the "prev_end" and "end" pointers by
pointing them to the first and second element of the list, respectively. We then traverse the list
by advancing the pointers until the "end" pointer points to the last element of the list. After
identifying the last node, the "prev_end" pointer points to the second-to-last node of the list,
which becomes the last node; subsequently, we update its next field to nil. Finally, we free the
memory space occupied by the "end" pointer using the "free" function.
In C Language
void Delete_middle_list (List *L, int k) {
Node *del,*prev_del;
int index_prev;
prev_del = (*L);
index_prev = 1;
del = prev_del->next;
while (index_prev != k-1){
prev_del = del;
del = del->next;
index_prev++;
}
prev_del->next = del->next;
free(del);
}
This function takes as formal parameters a dynamic linked list L (passed by address) and an
integer "k" (passed by value) which represents the index of the element to be deleted.
Page | 16
Chapter 8 : Pointers and Linked Lists
With while loop, we traverse the list to find the element to be deleted using two pointers: "del"
points to the element to be deleted and "prev_del" points to the element before the one to be
deleted, along with a counter "index_prev" to track the index of the element before the one to be
deleted.
Once the traverse is complete, we update the list (the next of the element pointed by
"prec_del" pointer becomes the next of the element pointed by "del" pointer) and free the space
allocated by the deleted element (pointed by the "del" pointer).
2.8.10. Filling (reading) a dynamic singly linked list
This procedure allows the filling a dynamic singly linked list with "N" values.
In Algorithmics
Procedure Read_list (var L: List, N: integer)
Var i, val, choice, pos: integer;
Begin
for (i1 to N) do
write ("Enter the ", i,"th value");
read(val);
write ("Choose the operation to apply: ");
write ("1: to insert at the beginning ");
write ("2: to insert in the middle ");
write ("3: to insert at the end ");
According to (choice) do
1: Insert_beginning_list(L, val);
2: write ("Enter the insertion position: ");
repeat
read(pos);
if((pos<=1)||(pos>=i))then
printf("Re-enter the insertion position: ");
endif
Until((pos>1)and(pos<i));
Insert_middle_list(L, val, pos);
3: Insert_end_list(L, val);
endaccording;
endfor
End ;
In C Language
void Read_list (List* L, int N){
int i, val, choice, pos;
Page | 17
Chapter 8 : Pointers and Linked Lists
if(((choice!=1)&&(choice!=2)&&(choice!=3))||((choice==2)&&(i<=2)))
printf("Re-enter your choice : ");
}while(((choice!=1)&&(choice!=2)&&(choice!=3))||((choice==2)&&(i<=2)));
switch (choice){
case 1: Insert_beginning_list(L,val); break;
case 2: printf("Enter the insertion position: ");
do{
scanf("%d",&pos);
if((pos<=1)||(pos>=i))
printf("Re-enter the insertion position: ");
}while((pos<=1)||(pos>=i));
Insert_middle_list(L, val, pos);
break;
case 3: Insert_end_list(L,val); break;
}
}
}
This function takes two formal parameters: the linked list "L" (passed by address) and the
number of values to insert in the linked list.
For each element to insert, the function asks the user to enter the value, then offers three
choices of insertion operations: insertion at the beginning, in the middle, or at the end of the list.
The user must choose one of these options by entering the corresponding number. If the
user enters an invalid choice, the function asks them to enter a valid choice again.
The function uses a switch-case to call the corresponding insertion function based on the
user's choice. If the user has chosen insertion in the middle, the function also asks for the
position to insert the element.
currant L;
write ("The values of the list are : ");
In C Language
void Print_list (List L){
Node* currant=L;
Page | 18
Chapter 8 : Pointers and Linked Lists
The procedure is defined with a single formal parameter: the linked list L, passed by
reference. Utilizing a while loop and initializing the current pointer with the address of the first
node in the list, we iterate through the list until its end is reached. During each iteration of the
loop, we print the value of the current node and then update the "current" pointer to refer to the
next node in the list.
2.8.12. Search in a dynamic linked list:
This function checks the existence of a given value "x" in the linked list.
In Algorithmics
Function Search (L: List, x: integer) of Pointer
Var current : Pointer ;
Begin
current L ;
while ((current ≠ nil)and(current*.value ≠ x)) do
current current*.next ;
endwhile
return (current);
End ;
In C Language
Node* Search (List L, int x){
Node * current = L;
while((current!=NULL)&&(currentvalue != x)){
current = currentnext;
}
return(current);
}
This function takes two parameters the linked list "L" and the value searched for "x", and it
returns a pointer to the element of the linked list containing the first occurrence of the searched
value "x" if it exists, otherwise the function returns "nil".
2.9. Example
Let the following algorithm :
In Algorithmics
Algorithm Test_list;
Type Pointer = *Node ;
structure Node
value : integer ;
next : Pointer ;
endstructure ;
List = *Node ;
Page | 19
Chapter 8 : Pointers and Linked Lists
In C Language
#include <stdio.h>
#include <stdlib.h>
struct Node {
int value ;
struct Node* next ;
} ;
typedef struct Node Node ;
typedef Node* List ;
void Print_list (List L);
void Read_list (List* L, int N);
void Delete_middle_list (List *L, int k);
void Delete_end_list (List* L);
void Delete_beginning_list (List *L);
void Insert_middle_list (List* L, int x, int k);
void Initialize_list (List *L);
int Is_empty_list(List L);
int Length_list(List L);
void Insert_beginning_list(List *L, int x);
void Insert_end_list(List* L, int x);
int main(){
int n;
List L ;
printf("Enter the number of values to be added into list : ");
scanf("%d",&number);
Read_list(&L,number);
Print_list(L);
Delete_end_list (&L);
Delete_beginning_list (&L);
Delete_middle_list (&L,3);
Print_list(L);
return 0;
}
/* Here we add all the previous procedures and functions. */
This C program implements various manipulation operations on dynamic singly linked lists. It
begins by defining a structure called "Node" to represent each element of the list. Each node
contains two fields: "value" (of integer type) to store the value of the element and "next" (a
pointer) to point to the next element in the list.
Subsequently, functions and procedures are defined to perform different operations such as
displaying the list, reading values from input, deleting an element from the end, beginning, or
middle of the list, inserting an element at the beginning, middle, or end of the list, initializing the
list, checking if the list is empty, and calculating its length.
The program starts by initializing an empty list, then prompts the user to specify the number
of elements they want to add to the list. Next, the program uses the "Read_liste()" function to
insert the specified number of elements into the list. For example, if the user wants to add 6
elements with the following values: 10, 18, 5, 2, 47, and 36, the program adds them in this
order: 10 at the beginning, 18 at the end, 5 at the end, 2 at position 2, 47 at the beginning, and
36 at position 4.
After inserting the elements, the program uses the "Print_list()" procedure to display the list.
Then, it performs three deletion operations: deletion from the end, deletion from the beginning,
and deletion from the middle (position 3). Finally, the modified list is displayed again using the
"Print_list()" procedure.
These different steps of this program are illustrated by the following figures:
L nil
10 nil
node 1
Figure 2 : state of the list : call of Read_lis() and after insertion 10 (at beginning)
10 18 nil
node 1 node 2
Figure 3 : state of the list : call of Read_lis() and after insertion 18 (at end)
10 18 5 nil
Figure 4 : state of the list : call of Read_lis() and after insertion 5 (at end)
10 2 18 5 nil
Figure 5 : state of the list : call of Read_lis() and after insertion 2 (at position 2)
47 10 2 18 5 nil
Figure 6 : state of the list : call of Read_lis() and after insertion 47 (at beginning)
47 10 2 36 18 5 nil
Figure 7 : state of the list : call of Read_lis() and after insertion 36 (at position 4)
47 10 2 36 18 nil
10 2 36 18 nil
10 2 18 nil
Page | 22