# linked lists. outline why linked lists? linked lists basics implementation basic primitives...

of 35/35

Linked Lists

Post on 16-Dec-2015

227 views

Embed Size (px)

TRANSCRIPT

- Slide 1
- Linked Lists
- Slide 2
- Outline Why linked lists? Linked lists basics Implementation Basic primitives Searching Inserting Deleting
- Slide 3
- Why linked lists? The default implementation for storing a set of objects is an array int v[10]; denotes the allocation of 10 int variables Arrays are efficient for many purposes (e.g., fast access to elements), but have several limitations
- Slide 4
- Limitations of arrays (1) Their size must be fixed in advance At compile time (statically allocated) int mydata[100]; At run time (dynamically allocated) int *mydata; mydata = (int*)malloc(sizeof(int)*100); 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
- Slide 5
- Limitations of arrays (2) Because of first limitation, arrays are often over-sized To deal with dynamically changing sets of data, programmers usually allocate arrays which seem "large enough. Problems: Low utilization of arrays (you allocate a size X but use at most a fraction of it) and the space is wasted If the program ever needs to process more than X data, the code breaks
- Slide 6
- Limitation of arrays (3) Inserting elements to keep a given order is inefficient Example: If elements of the array represent an order of arrival ( a[0] is last arrived) inserting a new element implies moving all elements one position ahead
- Slide 7
- Array vs. linked lists Linked lists solve limitations of arrays by paying in terms of efficiency of access of elements Arrays: allocate memory for all its elements stored in contiguous memory locations (appears as one block of memory) 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.
- Slide 8
- Array vs. linked lists Array int v[4]; List ???? 2 5 3 v[0] v[1] v[2] v[3] v 2 5 3 ?
- Slide 9
- 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()
- Slide 10
- Arrays vs. linked lists ArraysLists Access time + (independent of array size random access) - (proportional to list size sequential access) Utilization - (over allocation typical) + (allocate what you need) Modification of element order - (requires movement of elements) + (insert where needed by moving pointers)
- Slide 11
- 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
- Slide 12
- 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)
- Slide 13
- Array vs. linked lists Array int v[4]; List List* Head; 2 5 3 v[0] v[1] v[2] v[3] v 2 5 3 Head
- Slide 14
- 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
- Slide 15
- 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
- Slide 16
- 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 */
- Slide 17
- Lists - list.c (1) #include List* Insert( List* head, int val) { List* p; p = newE(); p->key = val; /* pi vari campi */ p->next = head; head = p; return head; }
- Slide 18
- Lists - list.c (2) List* Search( List* head, int val) { List* p; p = head; while(p != NULL) { if( p->key == val) return(p); else p = p->next; } return(NULL); }
- Slide 19
- Lists - list.c (3) void Display(List* head) { List* p; p = head; while( p != NULL) { printf(%5d\n,p->val); p = p->next; }
- Slide 20
- 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; }
- Slide 21
- 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);
- Slide 22
- Lists: Deleting an element Deleting an element q (after element p) After p->next = q->next; free(q); Before p q p->next q->next p q
- Slide 23
- Delete it Search where is it Delete from head Lists - list.c (3) List* Delete( List* head, int val, int* status) { List *p, *q; p = q = head; if (head != NULL){ if (p->key == val) { /* found */ head = p->next; free(p); *status = SUCCESS; return head; } else { while(q->next != NULL) { p = q; q = q->next; if (q->key == val) { p->next = q->next; free(q); *status = SUCCESS; return head;}}}} *status = FAILURE; return head; }
- Slide 24
- Lists: Inserting an element Insertion of node q after node p Before After 3 6 p 5 q p->next 3 6 p 5 qq->next q->next = p->next; p->next = q;
- Slide 25
- Lists - list.c (4) List* InsertSorted(List* head, int val) { List *p, *q=head; /* head insertion */ if( (q == NULL) || (q->key > val)) { p = newE(); p->key = val; p->next = head; head = p; return head; }
- Slide 26
- Lists - list.c (4) /* search where to insert */ while( q->next != NULL) { if( q->next->key > val){ p = newE(); p->key = val; p->next = q->next; q->next = p; return head; } q = q->next; } q is != NULL, so q->next is defined
- Slide 27
- Lists - list.c (4) /* tail insertion: q->next is null here */ p = newE(); p->key = val; p->next = NULL; q->next = p; return head; }
- Slide 28
- Stacks and Queues
- Slide 29
- Stack Use a LIFO (Last In First Out) policy The last element inserted is the first that will be deleted Eg.: a stack of books Implementation in terms of lists Primitives: Push Pop Top Empty
- Slide 30
- Dynamic stacks and queues To overcome this limitation: Use the function realloc in case of overflow To implement as a list The second solution is particularly advantageous for queues
- Slide 31
- Stack and queues Dynamic Push Insertion in head Dynamic Pop Deletion from head List* head=NULL; //init. head = Push (head, val); //call List* Push (List*,int val) { List* p; p = newE(); p->next = head; p->key = val; head = p; } List* Pop(List* head, int* val) { List* p; if (head==NULL) { printf(Stack Underflow\n); } else { *val = head->key; p = head; head = p->next; free(p); return head; }
- Slide 32
- 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
- Slide 33
- Queues and lists Dynamic Enqueue Insert in tail 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
- Slide 34
- Linear queues with lists Dynamic EnqueueDynamic Dequeue Function call: pTail = enqueue (&head, pTail, val); List* enqueue(List** head, List* pTail,int val) { List* p; p = newE(); p->key = val; if (pTail==NULL) { //first elem *head = p; p->next = NULL; } else { pTail->next = p; } pTail = p; return pTail; } 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; } List* head=NULL, tail; //init.
- Slide 35
- 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 pTailpTail->next
- Slide 36
- Queues and lists - (cont.) Dynamic EnqueueDynamic Dequeue Function call: pTail = enqueue(pTail, val); List* enqueue(List* pTail, int val) { List* pNew; pNew = newE(); p->key = val; /* . */ if (pTail==NULL) { pTail = pNew; pTail->next = pTail; } else { pNew->next = pTail->next; pTail->next = pNew; pTail = pNew; } return pTail; } 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; }