Extensible Array

Extensible Array
C and Data Structures
Baojian Hua
[email protected]
Linear Data Structures

A linear list (list) consists of:

a collection of data elements


elements are ordered:




e1, e2, …, en
e1≤e2 ≤ … ≤ en
ei is called an predecessor of e_{i+1}
e_{i+1} is called a successor of ei
every element has at most one successor
and one predecessor
Linear Data Structures

Typical operations on linear list :
// create an empty list
newList ();
// the length of a list l
length (list l);
// insert element x at position i in l, 0<=i<n
insert (list l, x, i);
// return the i-th element
nth (list l, i);
// delete the element at position i in l, 0<=i<n
delete (list l, i);
// apply function f to each element in l
foreach (list l, f);
Polymorphic Abstract Data
Types in C
// in “list.h”
#ifndef LIST_H
#define LIST_H
typedef struct listStruct *list;
list newList ();
int length (list l);
poly nth (list l, int n); // “poly”?
void insert (list l, poly x, int i);
poly delete (list l, int i);
void foreach (list l, void (*f)(poly));
#endif
Implementations

Two typical implementation techniques:



array-based
linked structure-based
We next consider the first, and leave
the second to the next slide
Implementation Using Array

The straightforward method to implement
this interface (ADT) is to use an array
0

n-1
and the array may not be full, so we must keep a
“tail” tag to record its tail (the position of its last
elements)
Implementation Using Array

The straightforward method to implement
this interface is to use an array
tail
0

n-1
and the array may not be full, so we must keep a
“tail” tag to record its tail (the position of its last
elements)
Array-based Implementation
// Combine these above observations, we have:
// in file “arrayList.c”
#include <stdlib.h>
#include “list.h”
#define INIT_LENGTH 32 l
#define EXT_FACTOR 2
struct listStruct
{
poly *array;
int max;
int tail;
};
array
max
tail
0
n-1
Operation: “newList”
list newList ()
{
list l = (list)malloc (sizeof (*l));
l->array = malloc (INIT_LENTH * sizeof(poly));
l->max = INIT_LENTH;
l->tail = 0;
return l;
}
Operation: “newList”
list newList ()
{
list l = (list)malloc (sizeof (*l));
l->array = malloc (INIT_LENTH * sizeof(poly));
l->max = INIT_LENTH;
l->tail = 0;
return l;
}
l
$#%&
%$&^
@#%$
Operation: “newList”
list newList ()
{
list l = (list)malloc (sizeof (*l));
l->array = malloc (INIT_LENTH * sizeof(poly));
l->max = INIT_LENTH;
l
l->tail = 0;
array
%$&^
return l;
@#%$
}
0
n-1
Operation: “newList”
list newList ()
{
list l = (list)malloc (sizeof (*l));
l->array = malloc (INIT_LENTH * sizeof(poly));
l->max = INIT_LENTH ;
l
l->tail = 0;
array
return l;
}
max
@#%$
0
n-1
Operation: “newList”
list newList ()
{
list l = (list)malloc (sizeof (*l));
l->array = malloc (INIT_LENTH * sizeof(poly));
l->max = INIT_LENTH ;
l
l->tail = 0;
array
return l;
}
max
tail
0
n-1
Operation: “length”
int length (list l)
{
// note that we omit such checks in the next
// for clarity. However, You should always do
// such kind of checks in your code.
assert(l);
l
array
return l->tail;
max
}
tail
0
n-1
Operation: “nth”
poly nth (list l, int i)
{
if (i<0 || i>=l->tail)
error (“invalid index”);
l
poly temp;
temp = *((l->array)+i);
return temp;
array
max
tail
}
0
n-1
Operation: “nth”
poly nth (list l, int i)
{
if (i<0 || i>=l->tail)
error (“invalid index”);
l
poly temp;
temp = *((l->array)+i);
return temp;
array
max
tail
}
i
0
temp
n-1
Operation: “insert”
void insert (list l, poly x, int i)
{
if (i<0 || i>l->tail)
error (“invalid index”);
//move the data
…;
}
l
array
max
tail
i
0
n-1
Operation: “insert”
void insert (list l, poly x, int i)
{
if (i<0 || i>l->tail)
error (“invalid index”);
l
for (int j=l->tail;
j>i; j--)
(l->array)[j] =
(l->array)[j-1];
…;
array
max
tail
i
}
0
j
n-1
Operation: “insert”
void insert (list l, poly x, int i)
{
if (i<0 || i>l->tail)
error (“invalid index”);
l
for (int j=l->tail;
j>i; j--)
(l->array)[j] =
(l->array)[j-1];
…;
array
max
tail
i
}
0
j
n-1
Operation: “insert”
void insert (list l, poly x, int i)
{
if (i<0 || i>l->tail)
error (“invalid index”);
l
for (int j=l->tail;
j>i; j--)
(l->array)[j] =
(l->array)[j-1];
…;
array
max
tail
i
}
0
j
n-1
Operation: “insert”
void insert (list l, void *x, int i)
{
if (i<0 || i>l->tail)
error (“invalid index”);
l
for (int j=l->tail;
j>i; j--)
(l->array)[j] =
(l->array)[j-1];
(l->array)[i] = x;
array
max
tail
i
x
}
0
j
n-1
Operation: “insert”
void insert (list l, void *x, int i)
{
if (i<0 || i>l->tail)
error (“invalid index”);
l
for (int j=l->tail;
j>i; j--)
(l->array)[j] =
(l->array)[j-1];
(l->array)[i] = x;
(l->tail)++;
array
max
tail
i
x
}
0
j
n-1
Perfect?

What if the initial input arguments look
like this one?
direct data
movement will
incur an
out-of-bound error!

l
array
max
tail
i
0
n-1
Extensible Array
void insert (list l, poly x, int i)
{
if (i<0 || i>l->tail)
error (“invalid index”);
// if l is full, extend l->array by a factor…
if (l->tail==l->max) {
l->array = realloc (l->array,
EXT_FACTOR*(l->max)*sizeof(poly));
l->max *= EXT_FACTOR;
}
// data movement as discussed above…;
}
Extensible Array
l->array = realloc (l->array,
EXT_FACTOR*(l->max)*sizeof(poly));
l
array
max
tail
i
0
0
i
n-1
n-1
2n-1
Extensible Array
l->array = realloc (l->array,
EXT_FACTOR*(l->max)*sizeof(poly));
l
array
max
tail
i
0
0
n-1
i
n-1
2n-1
Extensible Array
l->array = realloc (l->array,
EXT_FACTOR*(l->max)*sizeof(poly));
l
array
l->max *= EXT_FACTOR;
max
tail
i
0
0
i
n-1
2n-1
Extensible Array
l
array
max
tail
i
0
n-1
2n-1
Operation: “delete”

The “delete” operation is reverse
operation of the “insert” operation



also involves data movement
should we shrink the extensible array,
when there are few elements in it (say ½
data item left)?
See the programming assignment
Operation: “foreach”
void foreach (list l, void (*f)(poly))
{
for (int i=0; i<l->tail; i++)
f (*(l->array + i));
return;
l
array
}
max
tail
0
n-1
Summary

Linear list ADT:



a collection of ordered data element
each item has no more than one successor or
predecessor
Extensible array-based implementation



maintain internally a dynamically extensible array
bad performance with insert or delete
space waste