高級語言里的列表是最常用的數據結構,在C里造個輪子玩玩,C沒有泛型,先用int練習。
Collection的ADT一般有hasnext,next,add, remove操作,List一般還加了removeat, insert等,然后Stack有push和pop,Queue有enqueue和dequeue。列表有種實現, ArrayList和LinkedList,總體來說ArrayList更常用一些,就先用數組實現個列表。
ArrayList在末尾的添加和刪除還是挺快的(O(1)),所以當棧來用挺好,Push和Pop都在末尾。 當隊列的話,Enqueue在數組頭部放入元素,所有右邊的元素都要向右移動(O(N)),比較耗 時,不如LinkedList。另外獲取指定索引數據或設置指定索引的數據是O(1)復雜度, 比LinkedList快,如果要想查看是否有指定數據,或刪除指定數據,要掃描全表,復雜度 是O(N)。哦,刪除指定位置的數據復雜度也是O(N), 因為刪除后右邊的數據要全部向 左移動。
可以開始了,首先定義一個結構來保存狀態
struct arr_list { int * arr; // 內部數組 int index; // 實際數據大小 int size; // 預分配空間大小 };
創建一個array list
struct arr_list* create_arr_list(n) { if (n < 1) { n = 10; } struct arr_list *arr = (struct arr_list*)malloc(sizeof(struct arr_list)); arr->arr = (int*)malloc(sizeof(int) * n); arr->size = n; arr->index = 0; return arr; }
空間不足時自動擴容,默認策略是空間不夠時申請雙倍大小空間, 然后把原有數據拷貝到 新空間,並把原有空間釋放掉, 該函數一般是新增元素前調用,所以判斷條件是當實際 所用空間已經等於或大於(應該不可能)預分配空間時擴容。
static void expand_space(struct arr_list *arr) { int *tmp, i, *p, *q; if (arr->index >= arr->size) { tmp = (int *)malloc(sizeof(int) * arr->size * 2); p = arr->arr; q = tmp; for (i = 0; i < arr->index; i++) { *q++ = *p++; } free(arr->arr); arr->arr = tmp; arr->size = arr->size * 2; } }
在指定位置插入新元素,現有元素向右移,O(N)
int list_insert(struct arr_list *arr, int index, int obj) { int i; if (index < 0 || index > arr->index) { return -1; } expand_space(arr); for (i = arr->index; i > index ; i--) { arr->arr[i] = arr->arr[i - 1]; } arr->arr[index] = obj; arr->index++; return 0; }
在array list 末尾插入數據, O(1)
int list_push(struct arr_list *arr, int obj) { return list_insert(arr, arr->index, obj); }
刪除指定位置的數據,O(N), 刪除數據后,所有數據向左移動
int list_removeat(struct arr_list *arr, int index) { int i; if (index < 0 || index >= arr->index) { return -1; } for (i = index; i < arr->index - 1; i++) { arr->arr[index] = arr->arr[index + 1]; } arr->index--; return 0; }
移除並返回末尾的數據, O(1)
int list_pop(struct arr_list *arr) { return list_removeat(arr, arr->index - 1); }
判斷 array list里是否包含某個數據, O(N)
int list_index(const struct arr_list *arr, int obj) { int i; for (i = 0; i < arr->index; i++) { if (arr->arr[i] == obj) { return i; } } return -1; }
刪除某個數據項,O(N), 只刪第一次出現的位置, 刪除后所有數據向左移動
int list_remove(struct arr_list *arr, int obj) { int i, index; index = list_index(arr, obj); if (index != -1) { for (i = index; i < arr->index - 1; i++) { arr->arr[i] = arr->arr[i + 1]; } arr->index--; } return index; }
釋放一個array list的內存
int free_arr_list(struct arr_list *arr){ free(arr->arr); free(arr); return 0; }
從頭打印一個arr list
void print_arr_list(const struct arr_list *arr) { int i, t; printf("size=%d,index=%d\n", arr->size, arr->index); for (i = 0; i < arr->index; i++) { list_get(arr, i, &t); printf("list[%d]=%d\n", i, t); } }
最后整體測試一下
int main(void) { struct arr_list *arr; int r; arr = create_arr_list(3); printf("list push: 5, 6, 7\n"); list_push(arr, 5); list_push(arr, 6); list_push(arr, 7); print_arr_list(arr); printf("list push: 8, will auto expand\n"); list_push(arr, 8); print_arr_list(arr); printf("list remove at 2\n"); list_removeat(arr, 2); print_arr_list(arr); printf("list pop \n"); list_pop(arr); print_arr_list(arr); printf("list insert 0\n"); list_insert(arr, 0, 3); print_arr_list(arr); r = list_index(arr, 3); printf("list index 3:%d\n", r); r = list_index(arr, 7); printf("list index 7:%d\n", r); printf("list remove 3\n"); list_remove(arr, 3); print_arr_list(arr); printf("list set index 0 = 3\n"); list_set(arr, 0, 3); print_arr_list(arr); free_arr_list(arr); return 0; }
小結
用C實現一下高級語言里常用的數據結構,可以對它們有更深的理解。