您的位置:首页 > 理论基础 > 数据结构算法

5.数据结构之通用动态数组

2015-10-22 11:42 459 查看

数据结构之通用动态数组

引言

1.关于数组我们之前做过简单的介绍,数组是一种物理和逻辑都连续的块存储空间,关于数组的定义主要有三个方式:

[code]//数组定义的三种方式:
int array1[] = {12, 23, 34, 45, 56, 67, 78};  //方法1
int array2[100] = {0};    //方法2

int *array3 = NULL;

array3 = (int *)malloc(sizeof(int) * 100);    //方法3
if(array3 != NULL){
     fprintf(stderr, "the memory is full!\n");
     exit(1);
}


array1和array2在主函数的栈帧上进行定义,字符指针array3是在主函数的栈帧上进行定义的,但是array3所指向的申请空间却是在内存的堆上,它们的内存分布是不同的。

常规数组存在的问题?

忽略数组元素插入和删除的效率低下,常规数组最重要的问题在于数组的规模无法实时地增大或减小,这会极大的影响程序的数据存储。基于上述的问题,我们急需一种能够动态进行扩充的数组(即动态数组

动态数组

创建动态数组的条件必须满足主存储区域在堆上,因为栈上的空间一旦申请是无法更改大小的,二如果我们把数组存放在堆上则可以借助系统函数malloc和realloc进行动态的扩充,上述两个函数的声明如下所示:

[code] //在堆上申请size大小的内存,如果申请成功把该块内存第一个字节的地址返回给调用者
 void *malloc(size_t size);

 //先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域(注意:原来指针是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。
 void *realloc(void *ptr, size_t size);


基于上述两个函数,我们可以在第一次使用malloc初始化数组,在以后的使用过程中,如果数组被占用完,就可以使用realloc进行内存的再申请。

如下图所示:



通用动态数组

通用动态数组不仅满足我们上述的要求可以进行动态的扩充和缩小,还能够实现对任意类型元素的存储。关于动态数组的目录结构如下图所示:



在这个结构中我们给出了对动态数组遍历的另外一种方式,没有采用数组的下标直接进行访问,而是采用了迭代器组件,这个组件在java还有C++等面向对象语言中大量的使用。在C语言中我们也可以采用结构体方式构造出迭代器。

关于迭代器的问题我们是接触过的,不管是数组的下标索引方式,还是指针的指向和加减操作,都是迭代器的表现形式。但是这里所提出的迭代器将会忽略掉数据结构的细节,而只是做一个对数据结构进行遍历的“指针”

整个迭代器的操作在iterator.h文件中进行实现:

[code]//iterator.h文件,通用迭代器的实现
#ifndef _ITERATOR_H
#define _ITERATOR_H

typedef struct ITERATOR
{
    void       *ptr;    //迭代器指针,与容器相关
    void       *data;    //用户数据指针
    int        data_len;    //用户数据长度(可以忽略)
    const char *key;    //若为哈希表的迭代器,则为哈希键值地址
    int        klen;    //若为binhash迭代器,则为健长度
    int        i   ;    //当前迭代器在容器中的位置索引
    int        size;    //当前容器中元素的总个数
}ITERATOR;

/*
 * 正向遍历容器中的元素
 * iter         迭代器
 * container    容器地址
 * */
#define FOREACH(iter, container) \
        for((container)->iter_head(&(iter), (container)); \
            (iter).ptr;    \
            (container)->iter_next(&(iter), (container)))

/*
 * 反向便利容器中的元素
 * iter         迭代器
 * container    容器地址
 * */
#define FOREACH_REVERSE(iter, container) \
        for((container)->iter_tail(&(iter), (container)); \
            (iter).ptr; \
            (container)->iter_prev(&(iter), (container)))

/*
 * 获得当前迭代器指针与某容器关联的成员结构类型对象
 * iter         迭代器
 * container    容器地址
 *
 * */
#define ITER_INFO(iter, container) \
        ((container)->iter_info(&(iter), (container)))

#define foreach         FOREACH
#define foreach_reverse FOREACH_REVERSE
#define iter_info       ITER_INFO  

#endif


foreach和foreach_reverse两个宏都是迭代其对于数据结构正向反向的两种方式,只需写入foreach或foreach_reverse就可以完成对数据结构的遍历,关于迭代其的操作数据结构需要满足以下条件:

1.相应的数据结构必须要提供迭代器操作的四个基本接口:iter_head、iter_tail、iter_next、iter_prev。这样才可以在数据结构中进行双向的遍历。

2.一旦所操作的数据结构发生变化(元素的删除或添加),则之前的迭代器失效。必须重新对迭代器的位置进行初始化。

使用迭代其的方式如下图所示:



关于动态数组的接口实现在arrray.h文件中,如下图所示:

[code]//动态数组的头文件(结构和接口声明)
#ifndef _ARRAY_H_
#define _ARRAY_H_

#include <stdio.h>
#include <stdlib.h>
#include "iterator.h"

#define TRUE      (1)
#define FALSE     (0)
#define ZERO      (0)
#define FIRST     (0)
#define STEP_SIZE (64)

typedef unsigned char Boolean;

struct Array;   //动态数组结构体
typedef struct Array Array;

struct Array{
    int capacity;    //动态数组的容量
    int count;    //数组元素个数
    void **data;   //动态数组数据

    void (*free)(void *ptr);    //元素的释放
    Boolean (*match)(void *value1, void *value2);   //元素的匹配
    void *(*dup)(void *src_value);   //元素的拷贝

    //动态数组的操作
    //尾部添加元素
    Boolean (*push_back)(Array *array, void *value);
    //头部添加元素
    Boolean (*push_front)(Array *array, void *value);
    //尾部删除
    Boolean (*pop_back)(Array *array);
    //头部删除
    Boolean (*pop_front)(Array *array);

    //迭代器操作
    //指向容器头部的位置
    void *(*iter_head)(Iterator *iter, Array *array);
    //指向容器尾部的位置
    void *(*iter_tail)(Iterator *iter, Array *array);
    //指向容器的下一个位置
    void *(*iter_next)(Iterator *iter, Array *array);
    //指向容器的上一个位置
    void *(*iter_prev)(Iterator *iter, Array *array);
};

//动态数组操作的接口
Array *init_array(int init_size)                  ;    //动态数组的初始化
void  destroy_array(Array **array)                ;    //动态数组的销毁
void  array_clean(Array *array)                   ;    //动态数组的清空
//插入到指定下标的前边
Boolean array_prev_insert(Array *array,
             int index, void *value)              ;
//插入到指定下标的后边
Boolean array_back_insert(Array *array,
             int index, void *value)              ;
int get_array_count(Array *array)                 ;   //得到元素的个数
void *get_array_index(Array *array, int index)    ;   //得到指定下标的元素
int find_value(Array *array, void *value)         ;    //找到元素下标
//删除指定下标元素(并将后续的元素前移)
Boolean delete_index_valu(Array *array, int index);
//删除指定范围的元素
Boolean delete_range_value(Array *array,
                           int begin, int end);

#endif


核心的数据存储在void **data变量中(可以结合main函数的参数char **argv进行理解),了解了动态数组的接口声明以及动态数组结构体定义,我们着重去介绍这些接口的具体实现:

[code]//动态数组接口的具体实现
#include "array.h"
#include "tools.h"

static int adjust_size(int size)
{
    size += (STEP_SIZE - 1);
    size /= STEP_SIZE;
    size *= STEP_SIZE;

    return size;
}

//动态数组的增长
static void array_grow(Array *array, int size)
{
    int adjust = 0;

    //增长的大小不大于capacity
    if(array->capacity >= size){
        return ;
    }

    //得到调整后的申请大小
    adjust = adjust_size(size);
    if(adjust <= ZERO){
        return ;
    }
    array->capacity = adjust;
    if(array->data){    //动态数组增长
        array->data = (void **)Realloc(array->data,
                      sizeof(void *) * adjust);
    }else{   //动态数组初始化
        array->data = (void **)Malloc(sizeof(void *) * adjust);
    }
}

//尾部添加元素
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 + STEP_SIZE);
    }

    array->data[array->count] = value;
    array->count++;
    return TRUE;
}

//头部添加元素
static Boolean array_push_front(Array *array, void *value)
{
    return array_prev_insert(array, 0, value);
}

//尾部删除
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;
}
//头部删除
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[FIRST];
    if(array->free != NULL){ 
        array->free(delete);
    }
    //把后续的元素整体向前搬移
    for(i = 0; i < array->count; ++i){
        array->data[i] = array->data[i + 1];
    }
    array->data[i] = NULL;
    return TRUE;
}

//迭代器操作
//指向容器头部的位置
static void  *array_iter_head(Iterator *iter, Array *array)
{
    if(iter == NULL || array == NULL){
        return NULL;
    }
    iter->index = FIRST;   //迭代器指向下标为0
    iter->size = array->count;
    if(array->data == NULL || array->count == ZERO){
        //动态数组的数据域为NULL或者没有有效元素
        iter->ptr = NULL;
    }else{
        iter->ptr = array->data[FIRST];
    }
    return iter->ptr;    
}

//指向容器尾部的位置
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;
}

//指向容器的下一个位置
static void *array_iter_next(Iterator *iter, Array *array)
{
    iter->index++;   //迭代器的下标加1
    if(iter->index >= iter->size){
        //迭代其的下标超过容器的范围
        iter->ptr = NULL;
    }else{
        iter->ptr = array->data[iter->index];
    }
    return iter->ptr;
}

//指向容器的上一个位置
static void *array_iter_prev(Iterator *iter, Array *array)
{
    iter->index--;
    if(iter->index < FIRST){
        iter->ptr = NULL;
    }else{
        iter->ptr = array->data[iter->index];
    }
    return iter->ptr;
}

//动态数组操作的接口
Array *init_array(int init_size)    //动态数组的初始化
{
    Array *array = NULL;

    array = (Array *)Malloc(sizeof(Array));

    array->count = 0;   //初始化时没有元素
    array->push_back = array_push_back;
    array->push_front = array_push_front;
    array->pop_back = array_pop_back;
    array->pop_front = array_pop_front;
    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->free = NULL;
    array->match = NULL;
    array->dup = NULL;

    array->data = NULL;      
    if(init_size > 0){
         //对动态数组的数据域进行初始化
        array_grow(array, init_size);
    }else{
        array->capacity = 0; 
    } 
    return array;
}

void  destroy_array(Array **array)    //动态数组的销毁
{
    if(array == NULL || *array == NULL){
        return ;
    }

    //先清除data,再释放array
    array_clean(*array);
    free((*array)->data);
    free(*array); 
    *array = NULL;
}

void  array_clean(Array *array)    //动态数组的清空
{
    int i = 0;

    if(array == NULL){
        return ;
    }

    //先清除data,再修改count
    for(i = 0; i < array->count; ++i){
        if(array->free != NULL){
            array->free(array->data[i]);
        }
        array->data[i] = NULL;
    }
    array->count = 0;
}

//插入到指定下标的前边
Boolean array_prev_insert(Array *array,
             int index, void *value)
{
    int i = 0;

    if(array == NULL || value == NULL
    || index < FIRST || index >= array->count){
        return FALSE;
    }

    //容量不够,需要扩充
    if(array->count >= array->capacity){
         array_grow(array, array->capacity + STEP_SIZE);
    }

    //index及其以后的元素整体向后搬移(从后向前)
    for(i = array->count; i > index; --i){
         array->data[i] = array->data[i - 1];
    }
    //把指定的值插入到index位置
    array->data[index] = value;
    array->count++;
    return TRUE;
}

//插入到指定下标的后边
Boolean array_back_insert(Array *array,
             int index, void *value)
{
    int i = 0;

    if(array == NULL || value == NULL
    || index < 0 || index >= array->count){
        return FALSE;
    }

    //容量不够进行扩充
    if(array->count >= array->capacity){ 
        array_grow(array, array->capacity + STEP_SIZE);
    }

    for(i = array->count; i > index + 1; --i){ 
        array->data[i] = array->data[i - 1];
    }
    array->data[index + 1] = value;
    array->count++;
    return TRUE;
}

int get_array_count(Array *array)   //得到元素的个数
{
    if(array == NULL){
        return -1;
    }
    return array->count;
}

void *get_array_index(Array *array, int index)   //得到指定下标的元素
{
    if(array == NULL || array->count == ZERO
    || index < FIRST || index >= array->count){
        return NULL;
    }
    return array->data[index];
}

int find_value(Array *array, void *value)    //找到元素下标
{
    int i = 0;

    if(array == NULL || array->count == ZERO
     || value == NULL){
        return -1;
    }

    for(i = 0; i < array->count; ++i){
        if(array->match != NULL){
             if(array->match(array->data[i], value) == TRUE){
                 return i;
             }
        }else{ 
             if(array->data[i] == value){ 
                 return i;
             }
        }  
    }
    return -1;
}

//删除指定下标元素(并将后续的元素前移)
Boolean delete_index_value(Array *array, int index)
{
    int i = 0;

    if(array == NULL || array->count == ZERO
    || index < FIRST || index >= array->count){
        return FALSE;
    }

    //释放被删除元素指向的结构    
    if(array->free != NULL){
        array->free(array->data[index]);
    }

    //把后续的数组元素整体前移
    for(i = index; i < array->count - 1; ++i){
        array->data[i] = array->data[i + 1];
    }
    array->data[i] = NULL;
    array->count--;
    return TRUE;
}

//删除指定范围的元素
Boolean delete_range_value(Array *array,
                           int begin, int end)
{
    int i = 0;
    int j = 0;

    if(array == NULL || array->count == ZERO
    || begin < FIRST || end >= array->count
    || end < begin){
        return FALSE;
    } 

    //先删除begin到end的元素
    for(i = begin; i <= end; ++i){
        if(array->free != NULL){
            array->free(array->data[i]);
        }
        array->data[i] = NULL;
    }

    //再把end后到末尾的元素搬移到begin
    for(i = end + 1, j = begin; i < array->count; ){
        array->data[j++] = array->data[i++];
    }

    array->count -= (end - begin) + 1;
    return TRUE;
}


在这个数据结构中我们依然用到了工具函数,他们的声明和定义分别存在于tools.h和tools.c文件中,下面分别进行说明:

[code]//tools.h文件,包含malloc和realloc的包裹函数
#ifndef _TOOLS_H_
#define _TOOLS_H_

// git svn
#include <stdio.h>
#include <stdlib.h>

void *Malloc(size_t size)            ;    //申请指定大小堆空间
void *Realloc(void *ptr, size_t size);    //重新申请指定大小堆空间

#endif


[code]//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 *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;
}


最后我们对动态数组的是实现进行检测,在main.c函数中:

[code]//动态数组检测
#include "array.h"
#include "tools.h"
#include "iterator.h"

int main(int argc, char **argv)
{
    Array *array = NULL;
    Iterator iter = {0};
    int arr[] =  {12, 2, 3, 324, 34, 34};
    int arr_len = sizeof(arr) / sizeof(arr[0]);
    int i = 0;

    array = init_array(20);    //初始化动态数组

    for(i = 0; i < arr_len; ++i){
        array->push_back(array, &arr[i]);
    }

    //打印动态数组元素
    foreach(iter, array){
        print_int(iter.ptr);
    }
    printf("\n");

    //头部删除元素
    for(i = 0; i < 3; ++i){
        array->pop_front(array);
    }

    //打印动态数组元素
    foreach(iter, array){
        print_int(iter.ptr);
    }
    printf("\n");

    destroy_array(&array);    //销毁动态数组
    return 0;
}


小结:

最终我们实现了动态数组的相关操作,这里最有收获的是使我们对传统数据结构的认知发生了改变,原来我们可以对数据进行修改,从而增强其功能和操作效率、以及操作的隐蔽性。

在接下来的几个章节中我们将会着重去讲解栈和队列,同为线性数据结构,我们可以通过对通用双端链表和通用动态数组的封装从而实现栈和队列。

敬请期待!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: