Académique Documents
Professionnel Documents
Culture Documents
Outline
Why linked lists? Linked lists basics Implementation Basic primitives
- Searching - Inserting - Deleting
Arrays are efficient for many purposes (e.g., fast access to elements), but have several limitations
In theory it is possible, for a dynamically allocated array to resize it using realloc but it tends to be quite inefficient
- E.g., reallocate a large array to add 1 element
Linked lists:
- allocate space for each element separately in its own block of memory called a "linked list element" or "node". - The list gets is overall structure by using pointers to connect all its nodes together like the links in a chain.
Array
int v[4];
List
???? 2
2 5 3 -1
?
5 -1 3
Linked lists
Each list node contains two fields:
- a "data" field to store whatever element type the list holds - and a "next" field which is a pointer used to link one node to the next node.
Each node is allocated with malloc() and it continues to exist until it is explicitly deallocated with free()
Arrays
Access time + (independent of array size random access) (over allocation typical) (requires movement of elements)
Lists
(proportional to list size sequential access) + (allocate what you need) + (insert where needed by moving pointers)
Lists (cont.)
Variants:
- Double linked lists The element possesses a pointer also to the previous element
- Circular lists The last element in the list is linked to the head
Lists (cont.)
Variants:
- Lists with sentinel Head or tail or both exist as fictitious elements to manage special cases at the boundary - Ordered Lists Starting from the head the elements (i.e., the keys) have an order (increasing or decreasing)
Array
int v[4];
List
List* Head; Head 2
2 5 3 -1
5 -1 3
Lists - (cont.)
Primitives - Insert (at the head of the list) - Search - Delete - InsertSorted
NOTE: The ordering of a list is not immediate
- It requires double pointers or auxiliary lists
Lists: Basic operations Different from vector based data structures, operation on a list requires pointer manipulation Element creation:
- Using malloc()
Initialization of a list
- A pointer to list initialized to NULL
Insertion/deletion of an element
- Movement of pointers
Lists - list.h
typedef struct e{ int key; List* next; } List; List* Insert(List*,int); /* modifies the head */ List* Search(List*, int); void Display(List*); List* Delete(List*,int, int*); /* modifies the head */ List* InsertSorted(List*,int); /* modifies the head*/
Lists - list.c (3) void Display(List* head) { List* p; p = head; while( p != NULL) { printf(%5d\n,p->val); p = p->next; }
Lists - list.c (4) The previous examples are in fact two applications of a generic visit function that does something on ALL list elements
void Visit (List* head) { List* p; p = head; while( p != NULL) { /* do something on p->key */ p = p->next; }
Example of usage
int val; List* head, p; val = 1; p = Search (head, val); if (p == NULL) printf(Value not found!\n); else printf(Value found!\n);
Before
p->next q->next
After
p = q = head; if (head != NULL){ if (p->key == val) { /* found */ head = p->next; Delete from head free(p); *status = SUCCESS; return head; } else { Search where while(q->next != NULL) { p = q; q = q->next; if (q->key == val) { p->next = q->next; Delete it free(q); *status = SUCCESS; return head;}}}} *status = FAILURE; return head;
is it
Before
p
3
p->next
p->next
5
q
After
return head;
}
} q = q->next;
}
/* tail insertion: q->next is null here*/ p = newE(); p->key = val; p->next = NULL; q->next = p; return head; }
Queues
Implements a FIFO (First In First Out) policy
- First inserted item is the first to be extracted (deleted) - E.g., a queue of persons to be served
Dynamic Dequeue
- Extract from the head
Given the huge number of accesses to the tail of the list, it is convenient to use an explicit pointer tail for the queues
Dynamic Dequeue
head = dequeue (head, &pTail, &val); List* dequeue(List* head, List** pTail,int* val) { List* p; if (head==NULL) { printf(Queue underflow\n); } else { *val = head->key; p = head; if (head == *pTail) { /* one-element queue */ *pTail=NULL; head=NULL; } else { head = head->next;} free (p); } return head; }
Circular queues
Dynamic Enqueue
- Insert in tail
Dynamic Dequeue
- Extract from the head
Usage of pointer pTail for insertion and deletion: last element points to first one
pTail->next pTail
Dynamic Dequeue
Function call: pTail = dequeue(pTail, val); List* dequeue(List* pTail, int* val, int* status) { List* pOld; if (pTail=!=NULL) { *status = SUCCESS; if (pTail == pTail->next){ *val = pTail->key; free(pTail); pTail = NULL;} else{ pOld = pTail->next; *val = pOld->key; pTail->next = pOld->next; free(pOld);}} return pTail; }