变长动态数组

在C/C++中,原生数组的空间大小是静态的。例如,声明int a[10]之后,数组a的大小就已经固定为10,因此在使用时,常常以数据量的上限作为数组的长度,比如最多有可能要保存100,000个元素时,可以创建一个长度为106的数组。这样做的显著缺点就是,当实际数据量较小时,会造成大量的空间浪费。更合理的方法是因地制宜地对数组空间进行动态分配。这里给出一种最简单的动态数组管理策略:

  1. 使用用户分配的一段连续的内存空间表示数组;
  2. 初始时,数组的容量为16;
  3. (扩展) 当插入元素时,如果空间已满,将数组容量增加至原有的2倍,也就是将原有的空间释放,分配一个大小为原有大小2倍的内存空间,并将原来的元素复制到新的空间中,例如原来的空间大小为16,现在增加到32;
  4. (压缩) 删除数组中元素后,如果剩余元素的数量小于容量的1/4(向下取整)且数组的容量大于16时,删除掉原有的空间,分配一个为原有容量一半的新的内存空间,并将原来的元素复制到新的空间中,例如原来的容量为64,但是删除元素后,数组中只有15个元素,那么将容量压缩到32。

根据以上策略,实现下面的接口。这些接口以C++中容器std::vector为蓝本,接口的具体功能请看函数前的注释。通过实现这些接口,你或许能够受到一些关于面向对象编程(OOP)的启发。

动态数组类型定义:

typedef int Elem;

typedef enum bool
{
    false,
    true
} bool;

typedef struct DynamicArray
{
    size_t capacity;   // 数组的容量,初始值和最小值均为16u
    size_t size;       // 数组中元素的个数
    Elem* data;     // 用于保存数组中元素的内存空间
} DynamicArray;

函数接口定义:

// 创建数组,初始容量为16。
DynamicArray* DynamicArray_new();

// 本题中,在位置pos前插入元素*v,也就是*v插入后,它是数组中的第pos个元素,下标从0开始。
// 注意,插入后的数组可能发生容量的倍增,假设内存分配总是合法的,不需要针对内存不足抛出异常。
// 例如:
//      1. pos = 10  ,表示在第10个元素前插入元素*v,插入后它前面有10个元素,原来的第10个元素在它后面
//      2. pos = 0   ,表示在数组的起始位置插入元素*v
//      3. pos = size,表示在数组的末尾插入元素*v
void DynamicArray_insert(DynamicArray* this, size_t pos, const Elem* v);

// 删除位置pos的元素,可以假定pos总是合法的,即pos=0,1,...,size - 1,删除后的数组不应该有空档。
// 注意,删除数组元素可能会导致容量变更。
// 例如:
//     size = 6, 数组中的元素为0,1,2,3,4,5。删除了pos=3的元素3之后,数组的元素变为0,1,2,4,5,size=5,数组第3个元素为4
Elem DynamicArray_erase(DynamicArray* this, size_t pos);

// 获取位置为pos的元素,假设pos总是合法的。
Elem DynamicArray_get(const DynamicArray* this, size_t pos);

// 将位置为pos的元素设置为*v,假设pos总是合法的。
void DynamicArray_set(DynamicArray* this, size_t pos, const Elem* v);

// 返回数组的容量。
size_t DynamicArray_capacity(const DynamicArray* this);

// 返回数组的元素个数。
size_t DynamicArray_size(const DynamicArray* this);

// 返回数组元素是否为空,为空则返回true,否则返回false。
bool DynamicArray_empty(const DynamicArray* this);

// 重设数组元素个数为new_size,注意,容量可能因此发生变更。
// 新的数组元素个数如果比原来的数组更多,将原有数组的元素复制到新数组对应位置即可。
// 新的数组元素个数如果比原来的数组更少,超出的部分截断即可。
// 例如:
//    1. 原数组为0,1,2,3,4,5,新数组大小为4,那么新数组变为0,1,2,3,size=4
//    2. 原数组为0,1,2,3,新数组大小为6,那么数组变为0,1,2,3,?,?,size=6,其中?表示任意值,即这些多出的元素
//       不需要初始化
void DynamicArray_resize(DynamicArray* this, size_t new_size);

// 删除数组。不要忘记释放原有数组的空间,否则会造成内存泄漏。
void DynamicArray_delete(DynamicArray* this);

裁判测试程序样例:

int main()
{
    char line[100];
    DynamicArray* arr = NULL;
    size_t size;
    size_t pos;
    Elem v;

    while (gets(line))
    {
        switch (line[0])
        {
        case 'C': // 创建数组
            arr = DynamicArray_new();
            break;
        case 'D': // 删除数组
            DynamicArray_delete(arr);
            arr = NULL;
            break;
        case 'R': // resize
            sscanf(line + 2, "%lu", &size);
            DynamicArray_resize(arr, size);
            break;
        case 'c': // 输出capacity
            printf("%lu\n", DynamicArray_capacity(arr));
            break;
        case 's': // 输出size
            printf("%lu\n", DynamicArray_size(arr));
            break;
        case 'i': // 插入元素
            sscanf(line + 2, "%lu %d", &pos, &v);
            DynamicArray_insert(arr, pos, &v);
            break;
        case 'd': // 删除元素并输出
            sscanf(line + 2, "%lu", &pos);
            printf("%d\n", DynamicArray_erase(arr, pos));
            break;
        case 'G': // get元素并输出
            sscanf(line + 2, "%lu", &pos);
            printf("%d\n", DynamicArray_get(arr, pos));
            break;
        case 'S': // set元素
            sscanf(line + 2, "%lu %d", &pos, &v);
            DynamicArray_set(arr, pos, &v);
            break;
        case 'e': // 输出数组是否为empty
            printf("%s\n", DynamicArray_empty(arr) == true ? "true" : "false");
            break;
        default:
            break;
        }
    }

    return 0;
}

/* 请在这里填写答案 */

代码:

DynamicArray* DynamicArray_new()
{
    DynamicArray* start=(DynamicArray*)malloc(sizeof(DynamicArray));
    start->capacity=16u;
    start->size=0;
    start->data=(Elem*)malloc((start->capacity)*sizeof(Elem));
    return start;
}
void DynamicArray_insert(DynamicArray* this,size_t pos,const Elem* v)
{
    if(this->size>=this->capacity)
    {
        Elem* tmp=(Elem*)realloc(this->data,(this->capacity)*2*sizeof(Elem));
        this->data=tmp;
        this->capacity=this->capacity*2;
    }
    //为什么i从size-1开始不行
    for(int i=this->size;i>pos;i--)
    {
        this->data[i]=this->data[i-1];
    }

    this->data[pos]=*v;
    this->size++;
}
Elem DynamicArray_erase(DynamicArray* this,size_t pos)
{
    Elem t=this->data[pos];
    for(int i=pos;i<this->size-1;i++)
    {
        this->data[i]=this->data[i+1];
    }
    this->size--;
    while(this->size*4<(this->capacity))
    {
        this->capacity/=2;
    }
    if(this->capacity<16)this->capacity=16;
    Elem* tmp=(Elem*)realloc(this->data,this->capacity*sizeof(Elem));
    this->data=tmp;
    return t;
}
Elem DynamicArray_get(const DynamicArray* this,size_t pos)
{
    Elem t=this->data[pos];
    return t;
}
void DynamicArray_set(DynamicArray* this,size_t pos,const Elem* v)
{
    this->data[pos]=*v;
}
size_t DynamicArray_capacity(const DynamicArray* this)
{
    size_t c=this->capacity;
    return c;
}
size_t DynamicArray_size(const DynamicArray* this)
{
    size_t s=this->size;
    return s;
}
bool DynamicArray_empty(const DynamicArray* this)
{
    if(this->size>0)return false;
    else return true;
}
void DynamicArray_resize(DynamicArray* this,size_t new_size)
{
    size_t newcap=this->capacity;
    while(newcap<new_size)newcap*=2;
    while(new_size*4<newcap)
    {
        newcap/=2;
    }
    if(newcap<16)newcap=16;
    Elem* newdata=(Elem*)realloc(this->data,newcap*sizeof(Elem));
    this->data=newdata;
    this->size=new_size;
    this->capacity=newcap;
}
void DynamicArray_delete(DynamicArray* this)
{
    free(this->data);
    this->data=NULL;
    this->capacity=0;
    this->size=0;
    free(this);
}
注意:

insert函数中i不可以从size-1开始遍历,因为size又可能为0,即对空数组进行插入时会发生段错误

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值