前言

当前所有算法都使用测试用例运行过,但是不保证100%的测试用例,如果存在问题务必联系批评指正~

问题介绍

原问题
使用C语言实现一个简单的ArrayList,能够具备动态扩容的功能,为后面的C语言实现的算法奠定基础数据结构

解决方案

基本数据结构又两部分实现:
1、ArrayList.h 头文件用来定义结构和操作结构的所有方法签名
2、ArrayList.c 源文件用来实现操作结构的所有方法
详情看代码

代码编写

java语言版本

1、ArrayList.h


#ifndef MYCPROJECT_ARRAYLIST_H
#define MYCPROJECT_ARRAYLIST_H

#endif //MYCPROJECT_ARRAYLIST_H

/*****************************************************************************************************
 * 由于C++编译器支持重载,函数名称会做一些处理,而在c中仅是简单函数名,这里定义为了告诉c++编译器按照C语言的方式编译即可*
 *****************************************************************************************************/
#ifdef __cplusplus
extern "C"
{
#endif

/**
 * 类似于java ArrayList功能
 * 1、元素地址连续,支持随机访问
 * 2、支持增删改查
 * 3、支持动态扩容
 */



/**
 * 列表初始化配置
 */
// 列表的初始化大小
#define INIT_SIZE 15
// 扩容倍数
#define INCREASE_PERC 1.5


/**
 * 列表的结构名称定义
 */
// 定义
typedef struct Data Data;
typedef struct ArrayList ArrayList;

/**
 * 列表的结构定义(相当于对象,但是方法定义不在对象中)
 */
// 列表元素,内部统一封装了一个指向内存的指针,可以不确定类型
struct Data {
    void *data;
};
// 列表,维护一个指向任何地方的指针,并且维护整个列表的长度和所占内存的大小,此处的内存指的是指针占的内存
struct ArrayList {
    // 存储数据的开头,随机访问直接游标即可
    Data* datas;
    // 当前列表有效元素长度
    int len;
    // 列表所占内存大小
    int size;
};


/*****************************************************************************
 * 定义操作结构的方法,相当于对象中的方法,c语言面向过程编程,也就是面向函数编程,主体是函数*
 * ***************************************************************************/
/**
 * 初始化列表
 * 1、给list数组分配内存
 * 2、初始化各变量长度
 * @return 成功时返回ArrayList结构地址
 */
ArrayList* init_array_list();


/**
 * 插入一个元素
 * @param list
 * @param p_data
 * @param index 如果为0,则从头插入,如果为-1,则从尾部插入
 * @return
 */
int array_list_insert(ArrayList *list, void* p_data, long index);

/**
 * 获取一个元素
 * @param list
 * @param index
 * @return 直接返回数据指针
 */
void* array_list_get(ArrayList* list, long index);

/**
 * 删除元素
 * @param list
 * @param index
 */
void array_list_remove_at(ArrayList* list, long index);

/**
 * 清空数组元素,不释放内存
 * @param list
 */
void array_list_clear(ArrayList* list);

/**
 * 释放数组内存
 * @param list
 */
void array_list_free(ArrayList* list);

/**
 * 判空
 * @param list
 * @return
 */
int array_list_is_empty(ArrayList* list);


#ifdef __cplusplus
};
#endif

ArrayList.c

#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <limits.h>
#include <stdlib.h>

#include "arraylist.h"

/********************************************
 *          该文件为头文件的方法实现            *
 ********************************************/

/**
 * 初始化列表:申请一块内存并初始化完成后,将内存的地址返回
 * 1、Arraylist结构分配一波内存
 * 2、给ArrayList结构中的data指针分配一波内存用来存储data结构列表
 * @return 成功时返回ArrayList结构地址
 */
ArrayList* init_array_list(){
    int i = 0;
    // 先给列表分配内存
    ArrayList* list = calloc(1, sizeof(ArrayList));
    if (NULL == list) {
        return NULL;
    }
    // 在给列表中的data分配内存
    list->datas = calloc(INIT_SIZE, sizeof(Data));
    if (NULL == list->datas) {
        return NULL;
    }
    for (i = 0; i < INIT_SIZE; ++i)
    {
        // 地址给0相当于null
        // datas[i] 当指针通过数组访问返回的引用,不是地址,该引用直接表示了data,所以这里使用"."来访问data
        // 但是list是指针,表示的是地址,通过地址直接访问datas成员时,就需要使用"->"来访问
        // datas[i].data等价与 (&datas[i]) -> data,先取到引用的地址再访问data
        list->datas[i].data = 0;
    }
    // 元素长度
    list->len = 0;
    // data内存长度
    list->size = INIT_SIZE;
    return list;
}

/**
 * 插入一个元素
 * @param list
 * @param p_data
 * @param index 如果为0,则从头插入,如果为-1, 则从尾部插入
 * @return
 */
int array_list_insert(ArrayList *list, void* p_data, long index){
    // 如果要扩容,则扩容后的内存量
    int relloc_size = 0;
    // 参数校验.list不为null,index不可以越界
    if (NULL == list || index < -1 || index >= list->len) {
        return -1;
    }
    // 判断扩容,未插入前如果长度还剩一个位置,则开始扩容
    if (list->len == list -> size - 1) {
        // 判断1.5倍是否越界
        int newCapacity = list->size * INCREASE_PERC;
        if (newCapacity - INT_MAX > 0) {
            // 如果新的数组越界了则为负数,则线性增加
            newCapacity = list->size+1;
        }
        if (newCapacity - INT_MAX > 0) {
            // 还是大于最大值,则返回-1
            return -1;
        }
        // 重新分配内存
        list->datas = realloc(list->datas, sizeof(Data) * newCapacity);
        if (NULL == list->datas) {
            // 分配内存失败
            return -1;
        }
        // 新分配的内存需要初始化
        for (int i = list->len; i < list->size; ++i) {
            list->datas[i].data = 0;
        }
        list->size = newCapacity;
    }
    // 放入新的data
    if (-1 == index) {
        // 插入尾部
        list->datas[list->len].data = p_data;
    }else {
        // 插入指定位置
        void* tem = p_data;
        void* tem2 = NULL;
        for (int i = index; i <= list->len; ++i) {
            tem2 = list->datas[i].data;
            list->datas[i].data = tem;
            tem = tem2;
        }
    }
    list->len++;
    return 1;
}



/**
 * 获取一个元素
 * @param list
 * @param index
 * @return 直接返回数据指针
 */
void* array_list_get(ArrayList* list, long index) {
    // 参数校验
    if (NULL == list || index < -1 || index >= list->len) {
        return NULL;
    }
    // index = -1 返回最后一个元素
    return index == -1 ? list->datas[list->len-1].data : list->datas[index].data;
}

/**
 * 删除元素
 * @param list
 * @param index
 */
void array_list_remove_at(ArrayList* list, long index) {
    // 参数校验
    if (NULL == list || index < -1 || index >= list->len) {
        return;
    }
    if (index == -1) {
        // 删除最后一个元素
        list->datas[list->len-1].data = NULL;
        list->len--;
        return;
    }
    // 删除元素,将元素覆盖过来即可
    int next = index+1;
    for (; next < list->len; ++next) {
        list->datas[next-1].data = list->datas[next].data;
    }
    // 将最后一个元素变成null
    list->datas[next].data = NULL;
    list->len--;
    return;
}

/**
 * 清空数组元素,不释放内存
 * @param list
 */
void array_list_clear(ArrayList* list) {
    if (NULL == list) {
        return;
    }
    // 将每一个data都指向NULL
    int i = 0;
    for (i = 0; i < list->len; ++i) {
        list->datas[i].data = NULL;
    }
    // 长度改变
    list->len = 0;
    return;
}

/**
 * 释放数组内存
 * @param list
 */
void array_list_free(ArrayList* list) {
    if (NULL == list) {
        return;
    }
    // 先释放list中的datas
    if (list->datas != NULL) {
        free(list->datas);
    }
    free(list);
    return;
}

/**
 * 判空
 * @param list
 * @return
 */
int array_list_is_empty(ArrayList* list) {
    if (NULL == list) {
        return 0;
    }
    if (list->len == 0) {
        return 1;
    }else {
        return 0;
    }
}




/**
 * 测试用例
 * @return
 */
int main(void) {
    setbuf(stdout, NULL);
    ArrayList* list = init_array_list();
    int ints[] = {5,4,6,3,4,5,6};
    int len = 7;
    for (int i = 0; i < len; ++i) {
        array_list_insert(list, &ints[i], -1);
    }
//    array_list_insert(list, &i,-1);
    if (array_list_is_empty(list) == -1) {
        printf("the list is empty \n");
    }
    for (int j = 0; j < len; ++j) {
        printf("the %d num is %d \n", j,*(int*)array_list_get(list, j));
    }
//    array_list_clear(list);
//    i=200;
//    array_list_insert(list, &i,-1);
//    printf("the first num is %d \n", *(int*)array_list_get(list, 0));
}

c语言版本

正在学习中

c++语言版本

正在学习中

思考感悟

Arraylist 是我们最经常使用的数据结构,c语言和java语言的设计思想存在很大的差异,对于面向过程的开发语言,我深刻的能够感觉到c语言任何一个函数的定义都是一个过程,执行的过程中需要将操作的结构指针传入,这里对于面向对象开发的我来说其实很不适应。
第一次发布c语言哈,拙劣写法看看就好,哈哈。

写在最后

07-07 13:38