320 likes | 414 Views
Extensible Array. C and Data Structures Baojian Hua bjhua@ustc.edu.cn. Linear Data Structures. A linear list (list) consists of: a collection of data elements e1, e2, … , en elements are ordered: e1 ≤ e2 ≤ … ≤ en ei is called an predecessor of e_{i+1}
E N D
Extensible Array C and Data Structures Baojian Hua bjhua@ustc.edu.cn
Linear Data Structures • A linear list (list) consists of: • a collection of data elements • e1, e2, …, en • elements are ordered: • 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 • 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) tail 0 n-1
Array-based Implementation // Combine these above observations, we have: // in file “arrayList.c” #include <stdlib.h> #include “list.h” #define INIT_LENGTH 32 #define EXT_FACTOR 2 struct listStruct { poly *array; int max; int tail; }; l 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; }
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 array %$&^ @#%$ 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 array 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->tail = 0; return l; } l array 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); return l->tail; } l array max tail 0 n-1
Operation: “nth” poly nth (list l, int i) { if (i<0 || i>=l->tail) error (“invalid index”); poly temp; temp = *((l->array)+i); return temp; } l array max tail 0 n-1
temp Operation: “nth” poly nth (list l, int i) { if (i<0 || i>=l->tail) error (“invalid index”); poly temp; temp = *((l->array)+i); return temp; } 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”); //move the data …; } l array max tail i 0 n-1
j Operation: “insert” void insert (list l, poly x, int i) { if (i<0 || i>l->tail) error (“invalid index”); for (int j=l->tail; j>i; j--) (l->array)[j] = (l->array)[j-1]; …; } l array max tail i 0 n-1
j Operation: “insert” void insert (list l, poly x, int i) { if (i<0 || i>l->tail) error (“invalid index”); for (int j=l->tail; j>i; j--) (l->array)[j] = (l->array)[j-1]; …; } l array max tail i 0 n-1
j Operation: “insert” void insert (list l, poly x, int i) { if (i<0 || i>l->tail) error (“invalid index”); for (int j=l->tail; j>i; j--) (l->array)[j] = (l->array)[j-1]; …; } l array max tail i 0 n-1
j Operation: “insert” void insert (list l, void *x, int i) { if (i<0 || 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 array max tail i x 0 n-1
j Operation: “insert” void insert (list l, void *x, int i) { if (i<0 || 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)++; } l array max tail i x 0 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 n-1 i n-1 0 2n-1
Extensible Array l->array = realloc (l->array, EXT_FACTOR*(l->max)*sizeof(poly)); l array max tail i 0 n-1 i n-1 0 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 n-1 i 0 2n-1
Extensible Array l array max tail i n-1 0 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