Extensible Array C and Data Structures Baojian Hua
Data Structures Data structure studies the organization of data in computers, consisting of the (abstract) data types (definition and repr ’ ) relationship between elements of this type operations on data types Algorithms: operations on data structures subtle interplay with data structure design Slogan: program=data structures + algorithm
What will this part cover? Linear structures: extensible array, linked list, stack, queue, string, etc. Tree & forest: binary tree, binary search tree Graph Hash Sorting and Searching?
Linear Data Structures A linear list (list) consists of: a collection of data elements e1, e2, …, en (n≥0) elements are ordered: e1 ≤ e2 ≤ … ≤ en e i is called an predecessor of e i+1 e i+1 is called a successor of e i every element has at most one successor and one predecessor
Typical operations // create an empty list new (); // 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 List_t *List_t; List_t List_new (); int List_length (List_t l); poly List_nth (List_t l, int n); // “poly”? 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
Implementations Two well-known 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 and the array may not be full, so we must keep a “ tail ” tag to record its tail (the position of its last elements) 0 n-1
Implementation Using Array The straightforward method to implement this interface is to use an array and the array may not be full, so we must keep a “ tail ” tag to record its tail (the position of its last elements) 0 n-1 tail
1st Try #define N 100 int main () { int a[N]; int tail = 0; a[tail++] = 3; a[tail++] = 5; … // what if we continue to insert value into “a”? } 0 n-1 tail
Array-based Implementation // Combine these above observations, we have: // in file “arrayList.c” #include #include “list.h” #define INIT_LENGTH 32 #define EXT_FACTOR 2 struct List_t { poly *array; int max; int tail; }; 0 n-1 array max tail l
Create an Empty List List_t List_new () { List_t l = malloc (sizeof (*l)); l->array = malloc (INIT_LENTH * sizeof(poly)); l->max = INIT_LENTH; l->tail = 0; return l; }
Create an Empty List List_t List_new () { List_t l = malloc (sizeof (*l)); l->array = malloc (INIT_LENTH * sizeof(poly)); l->max = INIT_LENTH; l->tail = 0; return l; } $#%& l
Create an Empty List List_t List_new () { List_t l = malloc (sizeof (*l)); l->array = malloc (INIT_LENTH * sizeof(poly)); l->max = INIT_LENTH; l->tail = 0; return l; } 0 n-1 array l
Create an Empty List List_t List_new () { List_t l = malloc (sizeof (*l)); l->array = malloc (INIT_LENTH * sizeof(poly)); l->max = INIT_LENTH ; l->tail = 0; return l; } 0 n-1 array l
Create an Empty List List_t List_new () { List_t l = malloc (sizeof (*l)); l->array = malloc (INIT_LENTH * sizeof(poly)); l->max = INIT_LENTH ; l->tail = 0; return l; } 0 n-1 array max tail l
Operation: “ length ” int List_length (List_t 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); return l->tail; } 0 n-1 array max tail l
Operation: “ nth ” poly List_nth (List_t l, int i) { if (i =l->tail) error (“invalid index”); return (l->array)[i]; } 0 n-1 array max tail l
Operation: “ nth ” poly List_nth (List_t l, int i) { if (i =l->tail) error (“invalid index”); return (l->array)[i]; } 0 n-1 array max tail l i temp
Operation: “ insert ” void List_insert (List_t l, poly x, int i) { if (i l->tail) error (“invalid index”); //move the data …; } 0 n-1 array max tail l i
Operation: “ insert ” void List_insert (List_t l, poly x, int i) { if (i l->tail) error (“invalid index”); for (int j=l->tail; j>i; j--) (l->array)[j] = (l->array)[j-1]; …; } 0 n-1 array max tail l i j
Operation: “ insert ” void List_insert (List_t l, poly x, int i) { if (i l->tail) error (“invalid index”); for (int j=l->tail; j>i; j--) (l->array)[j] = (l->array)[j-1]; …; } 0 n-1 array max tail l i j
Operation: “ insert ” void List_insert (List_t l, poly x, int i) { if (i l->tail) error (“invalid index”); for (int j=l->tail; j>i; j--) (l->array)[j] = (l->array)[j-1]; …; } 0 n-1 array max tail l i j
Operation: “ insert ” void List_insert (List_t l, void *x, int i) { if (i l->tail) error (“invalid index”); for (int j=l->tail; j>i; j--) (l->array)[j] = (l->array)[j-1]; (l->array)[i] = x; } x 0 n-1 array max tail l i j
Operation: “ insert ” void List_insert (list l, void *x, int i) { if (i l->tail) error (“invalid index”); for (int j=l->tail; j>i; j--) (l->array)[j] = (l->array)[j-1]; (l->array)[i] = x; (l->tail)++; } x 0 n-1 array max tail l i j
Perfect? What if the initial array look like the right one? direct data movement will incur an out-of- bound error! 0 n-1 array max tail l i
Extensible Array void List_insert (List_t l, poly x, int i) { if (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 0 n-1 array max tail l i 02n-1 i l->array = realloc (l->array, EXT_FACTOR*(l->max)*sizeof(poly)); n-1
Extensible Array 0 n-1 array max tail l i 02n-1 i n-1 l->array = realloc (l->array, EXT_FACTOR*(l->max)*sizeof(poly));
Extensible Array 0 n-1 array max tail l i 02n-1 i l->max *= EXT_FACTOR; l->array = realloc (l->array, EXT_FACTOR*(l->max)*sizeof(poly));
Extensible Array array max tail l 02n-1 i n-1
Operation: “ delete ” The “ delete ” operation is reverse operation of the “ insert ” also involves data movement should we shrink the extensible array, when there are few elements in it (say ½ data item left)? More tricky than first looks
Operation: “ foreach ” void List_foreach (List_t l, void (*f)(poly)) { for (int i=0; i tail; i++) f ((l->array)[i]); return; } 0 n-1 array max tail l
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