資料結構7.動態陣列
阿新 • • 發佈:2019-02-20
引言:
我們之前提到過 陣列(Array),是一種資料結構,是資料元素(elements)的集合。
陣列的特點:
//陣列的缺點:
//
// 1.一旦陣列定義,則大小固定,無法進行修改(陣列的大小)。
// 2.陣列插入和刪除的效率太低,時間複雜度O(n)。
//
// 陣列的優點:
//
// 1.下標訪問,速度快,時間複雜度是O(1)
陣列的定義:
//方法1
int array1[10] = {0};
//方法2
int array2[] = {12, 23, 34, 45, 56, 67, 78};
//方法3
int *array3 = NULL;
array3 = (int *)malloc(sizeof(int) * 100);
if(array3 != NULL){
fprintf(stderr, "the memory is full!\n");
exit(1);
}
當初,我們說array1和array2都在定義時直接分配了棧上的記憶體空間;
而array3是一個int型別的指標,指向我們在堆上用malloc分配的4bytes ×100 = 400 bytes大小的空間。
普通情況下這樣陣列雖然在O(1)的時間複雜度訪問下標進行資料存取查詢,可是一般陣列定義後,大小不能夠進行動態變化,且插入刪除效率過低,時間複雜度為O(n).
當初,為了彌補這些不足,我們又學習到了連結串列。而連結串列失去了很多陣列的優點。
那怎麼樣才可以兼顧陣列和連結串列的優點呢?今天我們將要學習動態陣列。動態陣列
一、動態陣列基
動態陣列是指在編譯時不能確定陣列長度,程式在執行時需要動態分配記憶體空間的陣列。
我們要怎麼樣為陣列分配到用來儲存資料的空間呢。
NAME
malloc, free, calloc, realloc - allocate and free dynamic memory
SYNOPSIS
#include <stdlib.h>
void *malloc(size_t size);
void free(void *ptr);
void *calloc (size_t nmemb, size_t size);
void *realloc(void *ptr, size_t size);
void *malloc(size_t size);
malloc 向系統申請分配指定size個位元組的記憶體空間。返回型別是 void* 型別。void* 表示未確定型別的指標。C,C++規定,void* 型別可以強制轉換為任何其它型別的指標。
void free(void *ptr);
釋放malloc(或calloc、realloc)函式給指標變數分配的記憶體空間的函式,釋放申請的動態記憶體。
使用後該指標變數一定要重新指向NULL,防止野指標出現,有效規避誤操作。(另:對於free(p)這句語句,如果p 是NULL 指標,那麼free 對p 無論操作多少次都不會出問題。如果p 不是NULL 指標,那麼free 對p連續操作兩次就會導致程式執行錯誤。)
void *calloc(size_t nmemb, size_t size);
在記憶體的動態儲存區中分配n個長度為size的連續空間,函式返回一個指向分配起始地址的指標;如果分配不成功,返回NULL。
一般使用後要使用 free(起始地址的指標) 對記憶體進行釋放,不然記憶體申請過多會影響計算機的效能,以至於得重啟電腦。如果使用過後不清零,還可以使用指標對該塊記憶體進行訪問。
void *realloc(void *ptr, size_t size);
realloc函式功能為先判斷當前的指標是否有足夠的連續空間,如果有,擴大mem_address指向的地址,並且將mem_address返回,如果空間不夠,先按照newsize指定的大小分配空間,將原有資料從頭到尾拷貝到新分配的記憶體區域,而後釋放原來mem_address所指記憶體區域,同時返回新分配的記憶體區域的首地址,即重新分配儲存器塊的地址。
- 我們看到用
malloc()
可以自己在堆上定義大小分配的空間,所以我們可以用malloc()
為我們提供分配記憶體的方法。 - 如果已分配的記憶體不夠我們可以使用
relloc()
函式,再次申請記憶體。
於是,照常我們將這些需要經常使用的函式,放入工具類定義:
tools.h
#ifndef _TOOLS_H_
#define _TOOLS_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//工具類介面
void *Malloc(size_t size);
void swap(void *a, void *b, int length);
void *Realloc(void *ptr, size_t size);
#endif
tools.c
#include "tools.h"
void *Malloc(size_t size)
{
void *result = malloc(size);
if(result == NULL){
fprintf(stderr, "the memory is full!\n");
exit(1);
}
return result;
}
void swap(void *a, void *b, int length)
{
void *temp = Malloc(length);
memcpy(temp, a, length);
memcpy(a, b, length);
memcpy(b, temp, length);
free(temp);
}
void *Realloc(void *ptr, size_t size)
{
void *result = realloc(ptr, size);
if(result == NULL)
{
fprintf(stderr, "the memory is full !\n");
exit(1);
}
return result;
}
照例可以使用迭代器:
iterator.h
#ifndef _ITERATOR_H_
#define _ITERATOR_H_
typedef struct Iterator
{
void *ptr;
int index;
int size;
}Iterator;
typedef Iterator iter;
/*
*正向迭代器
* container(list、array、stack)容器
*/
#define FOREACH(iter, container) \
for(container->iter_head(&(iter), container); \
(iter).ptr; \
container->iter_next(&(iter), container))
#define foreach FOREACH
#define FOREACH_REVERSE(iter, container) \
for(container->iter_tail(&(iter), container); \
(iter).ptr; \
container->iter_prev(&(iter), container))
#define foreach_reverse FOREACH_REVERSE
#endif
二、動態陣列定義
dynamic_array.h
#ifndef _ARRAY_H_
#define _ARRAY_H_
#include "iterator.h"
#define TRUE (1)
#define FALSE (0)
#define MODE_SIZE (32)
#define ZERO (0)
typedef unsigned char Boolean;
typedef struct Array Array;
struct Array
{
void **data; //1.儲存實體
int capacity; //2.動態陣列申請大小
int count; //3.當前元素個數
//4.拷貝函式指標
void *(*copy)(void *src_value);
//5.匹配函式指標
Boolean (*match)(void *value1, void *value2);
//6.釋放函式指標
void (*free)(void *ptr);
//7.頭部插入
Boolean (*push_front)(Array *array, void *value);
//8.尾部插入
Boolean (*push_back)(Array *array, void *value);
//9.頭部刪除
Boolean (*pop_front)(Array *array);
//10.尾部刪除
Boolean (*pop_back)(Array *array);
//
//迭代器操作
//11.指向陣列頭部的位置
void *(*iter_head)(Iterator *iter, Array *array);
//12.指向陣列尾部的位置
void *(*iter_tail)(Iterator *iter, Array *array);
//13.指向後一個元素的位置
void *(*iter_next)(Iterator *iter, Array *array);
//14.指向前一個元素的位置
void *(*iter_prev)(Iterator *iter, Array *array);
};
//動態陣列介面
//1.初始化
Array *init_array(int init_size);
//2.銷燬
void destroy_array(Array **array);
//3.清空
void clean_array(Array *array);
//4.插入到指定下標的前面
Boolean array_insert_prev(Array *array,
int index, void *value);
//5.插入到指定下標的後面
Boolean array_insert_next(Array *array,
int index, void *value);
//6.得到陣列個數
int get_array_count(Array *array);
//7.得到指定下標元素
void *get_index_value(Array *array, int index);
//8.刪除指定下標元素
Boolean delete_index_value(Array *array, int index);
//9.刪除指定下標範圍的元素
Boolean delete_range_value(Array *array, int begin, int end);
//10.查詢指定元素的下標
int find_array_value(Array *array, void *value);
#endif
三、動態陣列介面實現
dynamic_array.c
#include "dynamic_array.h"
#include "tools.h"
//前插、尾插、前刪、尾刪、
static Boolean array_push_front(Array *array, void *value);
static Boolean array_push_back(Array *array, void *value);
static Boolean array_pop_front(Array *array);
static Boolean array_pop_back(Array *array);
//迭代器 頭、尾、下一個、前一個
static void *array_iter_head(Iterator *iter, Array *array);
static void *array_iter_tail(Iterator *iter, Array *array);
static void *array_iter_next(Iterator *iter, Array *array);
static void *array_iter_prev(Iterator *iter, Array *array);
//封裝陣列增長函式
static void array_grow(Array *array, int size);
static int adjust_size(int size);
//1. 數字調整
static int adjust_size(int size)
{
//MODE_SIZE == 32
size += (MODE_SIZE -1); //100 -> 100 +31
size /= (MODE_SIZE); //131 -> 131 /32 == 4
size *= (MODE_SIZE); //4 -> 4 * 32 == 128
//將其size增長到離size最近的32的倍數處
return size;
}
//2.調整陣列大小
static void array_grow(Array *array, int size)
{
int adjust = 0;
if(array->capacity < size)
{
adjust = adjust_size(size); //增長
array->capacity = adjust;
if(array->data != NULL)
{
array->data = Realloc(array->data,
sizeof(void *)*adjust);
}
else
{
array->data = Malloc(sizeof(void *)*adjust);
}
}
}
//陣列的插入刪除操作
//1.前插
static Boolean array_push_front(Array *array, void *value)
{
return array_insert_prev(array, 0, value);
}
//2.尾插
static Boolean array_push_back(Array *array, void *value)
{
if(array ==NULL || value == NULL)
{
return FALSE;
}
//如果陣列容量不夠,增長
if(array->count >= array->capacity)
{
array_grow(array, array->count + MODE_SIZE);
}
array->data[array->count] = value ;
array->count ++;
return TRUE;
}
//3.前刪
static Boolean array_pop_front(Array *array)
{
int i =0 ;
void *delete = NULL;
if(array == NULL || array->count == ZERO)
{
return FALSE;
}
array->count -- ;
delete = array->data[0];
if(array->free != NULL)
{
array->free(delete);
}
while(i < array->count)
{
array->data[i] = array->data[i+1];
++i;
}
array->data[i] = NULL;
return TRUE;
}
//4.尾刪
static Boolean array_pop_back(Array *array)
{
void *delete = NULL;
if(array == NULL || array->count == ZERO)
{
return FALSE;
}
array->count -- ;
delete = array->data[array->count];
if(array->free != NULL)
{
array->free(delete);
}
array->data[array->count] = NULL;
return TRUE;
}
//迭代器操作介面
//1. 迭代器頭
static void *array_iter_head(Iterator *iter, Array *array)
{
if(iter == NULL || array == NULL)
{
return NULL;
}
iter->index = 0;
iter->size = array->count;
if(array->data == NULL || array->count == ZERO)
{
iter->ptr = NULL;
}
else
{
iter->ptr = array->data[0];
}
return iter->ptr;
}
//2. 迭代器指向尾
static void *array_iter_tail(Iterator *iter, Array *array)
{
if(iter == NULL || array == NULL)
{
return NULL;
}
iter->index = array->count -1;
iter->size = array->count;
if(array->data == NULL || array->count == ZERO)
{
iter->ptr = NULL;
}
else
{
iter->ptr = array->data[iter->index];
}
return iter->ptr;
}
//3. 迭代器 只想下一個陣列元素
static void *array_iter_next(Iterator *iter, Array *array)
{
if(iter == NULL || array == NULL)
{
return NULL;
}
iter->index ++;
iter->size = array->count;
if( iter->index >= iter->size)
{
iter->ptr = NULL;
}
else
{
iter->ptr = array->data[iter->index];
}
return iter->ptr;
}
//4. 迭代器 指向前一個數組元素
static void *array_iter_prev(Iterator *iter, Array *array)
{
if(iter == NULL || array == NULL)
{
return NULL;
}
iter->index --;
iter->size = array->count;
if( iter->index <= ZERO)
{
iter->ptr = NULL;
}
else
{
iter->ptr = array->data[iter->index];
}
return iter->ptr;
}
//動態陣列介面
//1.動態陣列初始化
Array *init_array(int init_size)
{
Array *array = (Array *)Malloc(sizeof(Array));
//對控制資訊成員進行初始化
array->count = 0;
//陣列元素拷貝、比較、釋放指標初始化為NULL
array->free = NULL;
array->match = NULL;
array->copy = NULL;
//頭插、尾插、頭刪、尾刪
array->push_front = array_push_front;
array->push_back = array_push_back;
array->pop_front = array_pop_front;
array->pop_back = array_pop_back;
//迭代器操作
array->iter_head = array_iter_head;
array->iter_tail = array_iter_tail;
array->iter_next = array_iter_next;
array->iter_prev = array_iter_prev;
array->data = NULL;
array->capacity = 0;
if(init_size > 0)
{
array_grow(array, init_size);
}
return array;
}
//2.動態陣列的銷燬
void destroy_array(Array **array)
{
//釋放陣列元素對應的空間
//釋放data
//釋放array
if(array == NULL)
{
return ;
}
delete_range_value(*array, 0, get_array_count(*array));
free(*array);
*array = NULL;
}
//3.動態陣列清空
void clean_array(Array *array)
{
if(array == NULL)
{
return ;
}
int i = 0 ;
while(i < array->count)
{
array->data[i] = NULL;
++i;
}
}
//4.插入到指定下標的前面
Boolean array_insert_prev(Array *array, int index, void *value)
{
int i = 0;
if(array == NULL || value == NULL
|| index > get_array_count(array))
{
return FALSE;
}
//如果陣列容量不夠,增長
if(array->count+1 >= array->capacity)
{
array_grow(array, array->count + MODE_SIZE);
}
i = array->count;
//index及以後的元素向後推移
while(i > index)
{
array->data[i] = array->data[i-1];
--i;
}
//插入元素
array->count ++;
array->data[i] = value;
return TRUE;
}
//5.插入到指定下標的後面
Boolean array_insert_next(Array *array,
int index, void *value)
{
int i = 0;
if(array == NULL || value == NULL
|| index > get_array_count(array))
{
return FALSE;
}
//如果陣列容量不夠,增長
if(array->count+1 >= array->capacity)
{
array_grow(array, array->count + MODE_SIZE);
}
i = array->count;
//index及以後的元素向後推移,從後向前防止被覆蓋
while(i > index)
{
array->data[i] = array->data[i-1];
--i;
}
//插入元素
array->count ++;
array->data[i] = value;
return TRUE;
}
//6.得到陣列個數
int get_array_count(Array *array)
{
if(array == NULL)
{
return -1;
}
return array->count;
}
//7.得到指定下標元素
void *get_index_value(Array *array, int index)
{
if(array == NULL || index > get_array_count(array))
{
return NULL;
}
return array->data[index];
}
//8.刪除指定下標元素
Boolean delete_index_value(Array *array, int index)
{
int i = 0;
if(array == NULL || index > get_array_count(array))
{
return FALSE;
}
i = index;
//陣列元素的移動,將其覆蓋
while(i < array->count)
{
array->data[i] = array->data[i+1];
++i;
}
//刪除最後一個
if(array->free != NULL)
{
free(array->data[array->count]);
}
array->data[array->count] = NULL;
array->count --;
return TRUE;
}
//9.刪除指定下標範圍的元素
Boolean delete_range_value(Array *array, int begin, int end)
{
#if 1
int i = begin;
int diff = end - begin + 1;
int count = get_array_count(array);
// 0 1 2 3 4 5 6 7 8 9
// x x x count:10 begin:2 end:4 diff:3
// 0 1 5 6 7 8 9 x x x
if(array == NULL || begin > count || begin < 0 || end < 0
|| end > count || diff < 0 )
{
return FALSE;
}
//後面diff個元素向前推移
while(i < count-diff)
{
array->data[i] = array->data[i+diff];
++i;
}
if(array->free != NULL)
{
i = array->count;
while(i-- > count - diff)
{
free(array->data[i]);
array->data[i] = NULL;
}
}
array->count -= diff;
return TRUE;
#endif
}
//10.查詢指定元素的下標
int find_array_value(Array *array, void *value)
{
int i = 0 ;
int count = get_array_count(array);
//printf("array_count:%d\n",count);
if(array == NULL || value == NULL)
{
return -1;
}
#if 1
//這裡通過元素是否相同做比較
while(i++ < count)
{
if(*((int *)(&array->data[i])) == *((int *)value))
{
return i;
}
}
//或者通過匹配指標函式match
#else
for(i = 0 ; i < count; ++i)
{
if(array->match!=NULL)
{
if(array->match(array->data[i], value))
{
return i;
}
}
else
{
if(array[data[i] == value)
{
return i;
}
}
}
return -1;
}
四、函式功能實現
檔案結構:
├── dynamic_array
├── dynamic_array.c
├── dynamic_array.h
├── iterator.h
├── main
├── main.c
├── tools.c
└── tools.h
main.c
#include <stdio.h>
#include "iterator.h"
#include "dynamic_array.h"
int main(int argc, char **argv)
{
int i = 0;
Array *array= init_array(30);
int a[] = {1, 2, 3, 4, 5};
int length = sizeof(a)/sizeof(a[0]);
for(i = 0; i < length; ++i)
{
array_insert_prev(array, i, &a[i]);
}
for(i = 0; i < length; ++i)
{
array_insert_next(array, i, &a[i]);
}
printf("array_count:");
printf("%d \n",get_array_count(array));
length = get_array_count(array);
printf("length:%d\n",length);
printf("Array :\n");
for(i = 0; i < length; ++i)
{
printf("%d ",*(int *)array->data[i]);
}
printf("\n");
printf("array_index_count_2:");
printf("%d \n",*(int *)get_index_value(array, 2));
printf("delete_index_value(array, 2):\n");
delete_index_value(array, 2);
length = get_array_count(array);
for(i = 0; i < length; ++i)
{
printf("%d ",*(int *)array->data[i]);
}
printf("\n");
delete_range_value(array, 1, 3);
length = get_array_count(array);
for(i = 0; i < length; ++i)
{
printf("%d ",*(int *)array->data[i]);
}
printf("\n");
/* foreach(iter, array)
{
printf("%d ",*(int *)array->data[i]);
}
*/
printf("find_array_value(array, ?)\n");
printf("array[5]: %d \n",find_array_value(array, &array->data[5]));
printf("array[3]:%d \n",find_array_value(array, &array->data[3]));
printf("\n");
printf("clean_array(array)\n");
clean_array(array);
length = get_array_count(array);
for(i = 0; i < length; ++i)
return 0;
}
執行結果:
root@aemonair:# cc.sh *.c
Compiling ...
-e CC dynamic_array.c main.c tools.c -g -lpthread
-e Completed .
-e Fri Jun 24 11:51:10 CST 2016
root@aemonair:~# ./dynamic_array
array_count:10
length:10
Array :
1 2 3 4 5 1 2 3 4 5
array_index_count_2:3
delete_index_value(array, 2):
1 2 4 5 1 2 3 4 5
1 1 2 3 4 5
find_array_value(array, ?)
array_count:6
array[5]: 5
array_count:6
array[3]:3
clean_array(array)
總結
至此,我們已經完成了動態陣列的實現。
我們可以從中看到,C語言可以用來實現各種各樣的資料結構,同時,作為動態陣列,我們多次使用malloc和free等函式,需要對記憶體的申請釋放特別注意。
我們在其中所使用的各種通用思想,以及迭代器的方法,需要再深入理解。通過我們的新鮮知識對舊的知識點進行進一步拓展和提升。