slides

Linked List
C and Data Structures
Baojian Hua
[email protected]
Recap

The extensible array-based implementation of
linear list:

may be too slow


may waste too much space


insert or delete operations involve data movement
only a small portion of the allocated space is occupied
with data
General computer science idea:

“pay as you go”
Polymorphic Abstract Data
Types in C
// recall the poly ADT:
#ifndef LIST_H
#define LIST_H
typedef void *poly;
typedef struct List_t *List_t;
List_t List_new ();
int List_length (List_t l);
poly List_nth (List_t l, int n);
void List_insert (List_t l, poly x, int i);
poly List_delete (List_t l, int i);
void List_foreach (List_t l, void (*f)(poly));
#endif
Implementation Using Linked
List

Linked list is a self-reference structure:
head

…
to simplify operations, we add a unique
head node “head”


“head” does not belong to the list
may hold meta information of the list
Linked List-based
Implementation
// Turn the above figure into C, we have:
// in file “linkedList.c”
#include <stdlib.h>
#include “list.h”
struct List_t
{
poly data;
list next;
};
head
data
next
data
next
data
next
…
Operation: “newList”
// “new” returns an empty list, which consists of
// a single head node.
List_t List_new ()
{
List_t l = malloc (sizeof (*l));
l->data = 0;
// Why this?
l->next = 0;
return l;
}
l
/\
/\
Operation: “length”
int List_length (List_t l)
{
List_t p = l->next;
int n = 0;
while (p) {
p = p->next;
n++;
}
return n;
n==0
}
l
p
data
next
data
next
data
next
…
Operation: “length”
int List_length (List_t l)
{
List_t p = l->next;
int n = 0;
while (p) {
p = p->next;
n++;
}
return n;
n==1
}
l
data
next
p
data
next
data
next
…
Operation: “length”
int List_length (List_t l)
{
List_t p = l->next;
int n = 0;
while (p) {
p = p->next;
n++;
}
return n;
n==2
}
l
data
next
data
next
p
data
next
…
Operation: “length”
int List_length (List_t l)
{
List_t p = l->next;
int n = 0;
while (p) {
p = p->next;
n++;
}
return n;
n==3
}
l
data
next
data
next
data p
next
…
Operation: “nth”
poly List_nth (List_t l, int n)
{
List_t p = l->next;
int i = 0;
if (n<0 || n>=List_length(l))
error (“invalid index”);
while (i!=n) {
p = p->next;
i++;
}
return p;
}
Operation: “nth”
n==2
i==0
l
p
data
next
data
next
data
next
n==2
i==1
l
data
next
p
…
data
next
data
next
…
i==2 n==2
l
data
next
data
next
p
data
next
…
Operation: “insert”
void List_insert (List_t l, poly x, int n)
{
// 1. change the “next” field of pointer t;
// 2. change the “next” field of element (n-1)
…;
}
n==2
we’d search pointer p
l
data
next
data
next
t
data
next
x
next
…
Operation: “insert”
void List_insert (List_t l, poly x, int n)
{
List_t p;
if (n<0 || n>List_length(l))
error (“invalid index”);
// search pointer p points to position n-1
p = n? (List_nth (l, n-1)) : l;
Operation: “insert”
// continued…
// Step #1: cook list node:
List_t temp = malloc (sizeof (*temp));
temp->data = x;
// Step #2: temp points to n-th data item
temp->next = p->next;
// Step #3: link temp onto list
p->next = temp;
return;
}
Operation: “delete”
poly
{
//
//
//
…;
}
l
List_delete (List_t l, int n)
The key step is to search pointer p
Leave this as exercise.
See Lab #3.
we’d search pointer p
data
next
data
next
n==2
data
next
…
Operation: “foreach”
void List_foreach (List_t l, void (*f)(poly))
{
List_t p = l->next;
while (p) {
f (p->data);
p = p->next;
}
}
l
data
next
data
next
data
next
…
Linked List Summary

Linked list:


better space usage---no waste
good time complexity



insert or delete take linear time
but have to search the data sequential, :-(
Several other variants:



circular linked list
doubly linked list
doubly circular linked list
Circular Linked List
All the pointers forms a circle

l
head
tail

data
next
data
next
data
next
Note that the first node has two fields


head: points to the head of the list
tail: points to the tail of the list
Circular Linked List--Implementation
// in file “clist.c”
struct Clist_t
{
struct node *head;
struct node *tail;
};
l
head
struct node
tail
{
poly data;
struct node *next;
}
data
next
data
next
data
next
Linear List Application #1:
Polynomials

Polynomials:



where ciR and n Nat
uniquely determined by a linear list:
For this representation, all the list
operations apply
Linear List Application:
Polynomials


Space waste:

Consider this:

20001 items with 3 non-zeros
A refined representation:


ci<>0 for 0<=i<=m
Ex:
Polynomial ADT: Interface

Abstract data type: Polyn_t

represent the polynomial data type
operations:

Polyn_t Polyn_new ();
// an empty polyn
Polyn_t Polyn_add (Polyn_t p1, Polyn_t p2);
real Polyn_value (Polyn_t p, real x0); // p(x0)
Polyn_t Polyn_mult (Polyn_t p1, Polyn_t p2);
// add an item c*x^n, which does not appear in p
void Polyn_insert (Polyn_t p, real c, int n);
Polynomial ADT in C: Interface
// in file “polyn.h”
#ifndef POLYN_H
#define POLYN_H
typedef struct Polyn_t * Polyn_t;
Polyn_t Polyn_new ();
Polyn_t Polyn_add (Polyn_t p1, Polyn_t p2);
Polyn_t Polyn_value (Polyn_t p, real x0);
Polyn_t Polyn_mult (Polyn_t p1, Polyn_t p2);
void Polyn_insert (Polyn_t p, real c, int n);
#endif
Polynomial ADT in C:
Implementation
// in file “polyn.c”
#include “linkedList.h”
#include “polyn.h”
struct Polyn_t
{
List_t coefExps;
};
// where “coefExps” is a list of tuples: (c, n)
// one way to read “list coefExps” is:
//
list<tuple<double, nat>> coefExps
// However, C does not support this style of
// declaration… :-(
Operation: “newPolyn”
Polyn_t Polyn_new ()
{
Polyn_t p = malloc (sizeof (*p));
// use a linked list internally
p->coefExps = List_new ();
return p;
}
Operation: “insert”
void Polyn_insert (Polyn_t p, real c, nat n)
{
// could we use “double” and “int”, instead of
// “real” and “nat”?
Tuple_t t = Tuple_new (c, n);
List_insertAtTail (p->coefExps, t);
return;
}
// Leave other functions as exercises.
Change to the Head
#include
#include
#include
#include
<stdlib.h>
“linkedList.h”
“tuple.h”
“polyn.h”
struct Polyn_t
{
List_t coefExps;
};
Linear List Application#2:
Dictionary

Dictionay:



where ki are keys and vi are value
all ki are comparable and distinct
How can dict’ be represented in computers?


many ideas (we’d discuss some in future)
for now, we make use of a linear list
Dictionary ADT: Interface

Abstract data type: Dict_t


represent the dictionary data type
operations:
Dict_t Dict_new ();
void Dict_insert (Dict_t d, poly key, poly value);
poly Dict_lookup (Dict_t d, poly key);
poly Dict_delete (Dict_t d, poly key);
“dict” ADT in C: Interface
// in file “dict.h”
#ifndef DICT_H
#define DICT_H
typedef struct Dict_t *Dict_t;
Dict_t Dict_new ();
void Dict_insert (Dict_t d, poly key, poly value);
poly Dict_lookup (Dict_t d, poly key);
poly Dict_delete (Dict_t d, poly key);
#endif
“dict” ADT in C:
Implementation
// in file “dict.c”
#include “linkedList.h”
#include “dict.h”
struct Dict_t
{
List_t l;
};
Operations: “new”
Dict_t Dict_new ()
{
Dict_t d = malloc (sizeof (*d));
d->l = List_new ();
return d;
}
Operations: “insert”
void Dict_insert (Dict_t d, poly key, poly value)
{
Tuple_t t = Tuple_new (key, value);
List_insertAtHead (d->l, t);
return;
}
// Leave other functions as programming
// exercises.