快速学习C语言四: 造轮子,ArrayList

高级语言里的列表是最常用的数据结构,在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实现一下高级语言里常用的数据结构,可以对它们有更深的理解。

快速学习C语言四: 造轮子,ArrayList

时间: 2024-08-01 22:19:08

快速学习C语言四: 造轮子,ArrayList的相关文章

快速学习C语言三: 开发环境, VIM配置, TCP基础,Linux开发基础,Socket开发基础

上次学了一些C开发相关的工具,这次再配置一下VIM,让开发过程更爽一些. 另外再学一些linux下网络开发的基础,好多人学C也是为了做网络开发. 开发环境 首先得有个Linux环境,有时候家里机器是Windows,装虚拟机也麻烦,所以还不如30块钱 买个腾讯云,用putty远程练上去写代码呢. 我一直都是putty+VIM在Linux下开发代码,好几年了,只要把putty和VIM配置好,其实 开发效率挺高的. 买好腾讯云后,装个Centos,会分配个外网IP,然后买个域名,在DNSPod解析过去

快速学习C语言一: Hello World

估计不会写C语言的同学也都听过C语言,从头开始快速学一下吧,以后肯定能用的上. 如果使用过其它类C的语言,如JAVA,C#等,学C的语法应该挺快的. 先快速学习并练习一些基本的语言要素,基本类型,表达式,函数,循环结构, 基本字符串操作, 基本指针操作,动态分配内存,使用结构表示复杂数据, 使用函数指针实现灵活逻辑. 虽然C是一个规模很小的语言,但也得自己多设计一些练习练手才能学会. 基本类型 我就记得char, int, 别的都不常用吧应该,用的时候再搜索. 表达式 和JAVA, C#差不多吧

快速学习C语言二: 编译自动化, 静态分析, 单元测试,coredump调试,性能剖析

上次的Hello world算是入门了,现在学习一些相关工具的使用 编译自动化 写好程序,首先要编译,就用gcc就好了,基本用法如下 gcc helloworld.c -o helloworld.o helloworld.c是源码,helloworld.o是编译后的可执行文件,运行的话就用 ./helloworld.o就可以了. 但是如果代码写的多了,每次改动完都手动用gcc编译太麻烦了,所以要用Makefile来 自动化这项工作,在当前目录下创建Makefile文件,大概如下 hellowor

零基础快速学习新语言的方法总结

"Do you want to spend the rest of your life selling sugared water or do you want a chance to change the world?"                                                                                                               -  Steve P.(aul) Jobs 学

快速学习C语言途径,让你少走弯路

1.标准C语言能干什么? 坦白讲,在今天软件已经发展了半个多世纪,单纯的C语言什么都干不了.标准C语言库只提供了一些通用的逻辑运算方法以及字符串处理,当然字符串在C语言看来也是一种操作内存的方法,所以单纯的C什么都做不了,不论是游戏客户端,服务器以及其他插件标准C语言要配合相应的系统C调用和其他语言共同完成一个完整的软件.所以C只是一个入门,要想吃软件这碗饭还有很长的路要走 2.从学会C语言到能写软件还要做什么? I.个人认为第一步是学习数据结构和算法.推荐一本书:<数据结构(C语言版) >

造轮子ArrayList

这篇博客实现一个简单的ArrayList集合.博客里的代码首先根据自己的想法实现,在走不动的情况下会去参考JDK源代码.所以阅读本文,不要抱着跟JDK源码对比的心态.于我个人而言,国庆期间,纯属娱乐.写完打游戏去. 首先写搭建一个架子 public class MyArrayList<E> { /* * 注意ArrayList的大小是可变的,此处设定的数组大小为默认大小,和每次扩建的大小 * */ //默认的数组大小 private int DEFAULT_CAPACITY = 1; //存储

【ruby项目,语言提交检查(一)】如何快速学习ruby ?

如何快速学习ruby ? 学习语言最快的思路. 变量,常量,变量类型,操作符, 逻辑语句如 if, else, switch, for, foreach, do while, break, 等等.要学的语言与这些命令相似的命令是什么?了解使用方法即可. 之后,如果是面向对象,就要了解一下关于对象的操作了. 有没有函数库,一般语言都有的.输出命令函数,操作数组,操作字符串,对象属性 操作文本文件 还有一个就是创建对象,类,数组这样的. 好了,成了会了上面的,你还没有学完此语言,但你是可以用此语言做

程序员为什么热衷造轮子

搜索一下"造轮子"或者"程序员为什么喜欢造轮子",会看到很多相关的讨论,这是个老生常谈的话题,很多人谈过了,谈了很多年.不过还是有再谈的必要. "造轮子"的含义: 明知道你做的不可能比前辈做得更好,却仍然坚持要做. 就软件开发而言,"造轮子"是指,"业界已经有公认的软件或者库了,却还坚持要自己做". 在软件开发过程中,有时你想造轮子老板却极力反对,有时你不想造轮子老板却坚持要造一个出来,为什么会有这种两极状

程序员为什么热衷造轮子?

搜索一下“造轮子”或者“程序员为什么喜欢造轮子”,会看到很多相关的讨论,这是个老生常谈的话题,很多人谈过了,谈了很多年.不过还是有再谈的必要. “造轮子”的含义: 明知道你做的不可能比前辈做得更好,却仍然坚持要做. 就软件开发而言,“造轮子”是指,“业界已经有公认的软件或者库了,却还坚持要自己做”. 在软件开发过程中,有时你想造轮子老板却极力反对,有时你不想造轮子老板却坚持要造一个出来,为什么会有这种两极状况? 这篇文章就来讨论“造轮子”这件事,包括下列主题: 程序员为什么会重复造轮子 为什么有