当前位置: 首页 > news >正文

股票跟单网站开发/合肥百度seo排名

股票跟单网站开发,合肥百度seo排名,龙岗网站设计代理商,洛阳住房和城乡建设委员会网站目录 前言 所以,从定义出发 在实现中窥探数组的特性 从内存分配策略仔细考察这个结构 创建一个空白的动态数组 bonus:pos_pointer,访问第i个元素的工具函数 尾插一个元素到动态数组上 插入 尾删 删除指定位置的元素 访问与删除 回…

目录

前言

所以,从定义出发

在实现中窥探数组的特性

从内存分配策略仔细考察这个结构

创建一个空白的动态数组

bonus:pos_pointer,访问第i个元素的工具函数

尾插一个元素到动态数组上

插入

尾删

删除指定位置的元素

访问与删除

回过头来思考我们的实现


本文章隶属于项目:从0到1的数据结构教程(含Leetcode刷题笔记)Charliechen114514/From-0-To1-Tutorials-for-ds-algorithm-lib-in-C: This is a toturials for createing a portable C Common Library by steps!https://github.com/Charliechen114514/From-0-To1-Tutorials-for-ds-algorithm-lib-in-C本篇文章的代码在:

From-0-To1-Tutorials-for-ds-algorithm-lib-in-C/Tutorial_1_DynamicArray/Codes at main · Charliechen114514/From-0-To1-Tutorials-for-ds-algorithm-lib-in-Chttps://github.com/Charliechen114514/From-0-To1-Tutorials-for-ds-algorithm-lib-in-C/tree/main/Tutorial_1_DynamicArray/Codes

前言

我们这一张的主要内容,就是仔细的学习动态数组这个数据结构。出于刷算法题的目的,笔者需要强调的是——动态数组这个事情,本质上是为了更好的理解数组操作的概念。看这篇博客的朋友,是需要通过这篇博客理解数组性质的,作为基本数据结构的根基,笔者也会强调一下常见的数据结构操作有哪些,这样我们理解了数据结构的操作,思考项目,或者功利点说,刷题更加的方便。

笔者这里也有一份最小简单实现的代码,放在文章的,介绍各种数据结构原理的章节部分,感兴趣的朋友酌情参考即可!

所以,从定义出发

什么是动态数组?简单的说——动态的数组,我们知道如下的基本事实:

  • 在C/C++中,假设给定数组为:int arr[10],即可以使用arr[i]访问第i + 1个元素,也可以使用arr + i * sizeof(elem)访问第i + 1个元素,这是因为我们可以使用指针访问数组,或者使用下表访问符访问数组

  • 传统的数组显然是静态的,换而言之,我们在编译期就确定了数组的大小,如果我们想要存储更多的内容,就必须重新另寻他处,这个问题的办法非常好解决,只需要在堆上提供动态的内存空间存放即可。我们需要的信息,也就是这个内存的起始地址和数组大小,以及存储的元素的元素大小,就可以访问当前空间中任何一个元素了。

我们的动态数组就是这样的一个可调节大小的数组结构,我们要实现针对这个结构的如何接口。毕竟,在真实的业务开发中,我们不应该非常关心底层的结构是如何实现元素存储,访问的。笔者简单的基于上述讨论,给出一些一个数据结构应当拥有的接口,这里我就以动态数组DynamicArray作为例子。

CCDynamicArray* create_blank_dynamic_array(size_t  elem_type);
void dynamic_array_push_back(CCDynamicArray* array, void* data, size_t elem_type);
void dynamic_array_insert(CCDynamicArray* array, void* data, size_t elem_type, size_t position);
void dynamic_array_pop_back(CCDynamicArray* array);
void dynamic_array_erase(CCDynamicArray* array, size_t where);
void dynamic_array_iterate(CCDynamicArray* array, IterationFunc func);
void erase_dynamic_array(CCDynamicArray* array);
函数名参数类型用途
CCDynamicArray* create_blank_dynamic_arraysize_t elem_type创建一个空的动态数组,elem_type 表示数组元素的类型大小(字节)。
void dynamic_array_push_backCCDynamicArray* array向动态数组末尾添加一个元素,data 是指向元素的指针,elem_type 确保类型匹配。
void* data
size_t elem_type
void dynamic_array_insertCCDynamicArray* array在指定位置插入一个元素,position 是插入的位置索引。
void* data
size_t elem_type
size_t position
void dynamic_array_pop_backCCDynamicArray* array删除动态数组的最后一个元素。
void dynamic_array_eraseCCDynamicArray* array删除指定位置的元素,where 是要删除的位置索引。
size_t where
void dynamic_array_iterateCCDynamicArray* array遍历动态数组并对每个元素执行 func 函数。
IterationFunc func
void erase_dynamic_arrayCCDynamicArray* array释放动态数组的内存并销毁数组。

笔者认为这就是一个最小的接口了。声明完接口,我们才知道我们的抓手在什么地方。理解这个事情,我们就可以深入的探讨动态数组这个数据结构的基本特点了。早在最开始(嗯,比较久以前的第0章,笔者介绍了数据结构的时间复杂度计算方法),我们下面也会结合这些技术指标,仔细分析动态数组这个数据结构。

在实现中窥探数组的特性

如何实现动态数组的抽象?显然,一个熟悉Malloc接口的朋友会给出这样的抽象:

typedef struct {void*       holding_array;size_t      capicity;size_t      current_size;size_t      element_size;
}CCDynamicArray;

可以简单的衡量一下成员的含义:

  • holding_array就是我们的内存的指针变量,拿到他,我们拿到了整个数组的开头,只需要一个游标变量,就能知道访问内存中我们感兴趣的特定的元素

  • capicity说明了我们当前内存申请最大可以存放的元素个数,实际上,capicity * elem_size就是我们动态数组内存的总大小

  • current_size说明了我们现在这个池子里维护了多少个元素

  • elem_size说明了存储的元素大小,这个是我们后面会用到的。

笔者上面绘制的图,说明了我们动态数组的抽象的物理绘图。我想我说的很明白了。

从内存分配策略仔细考察这个结构

显然我说过,动态数组的存储内存是需要堆上开辟的,这个没有任何的疑问。对于C语言,这个接口就是malloc函数

static void __reallocate_memory_with_given_size(CCDynamicArray* array, size_t new_size)
{void* new_space = realloc(array->holding_array, new_size * array->element_size);assert(new_space);array->holding_array = new_space;   // 注意,holding_array指针变量不会发生变动,我们需要存储realloc返回的值array->capicity = new_size;
}

realloc接口是对malloc和free的小幅度封装,我们申请的这个接口将会对给定的起始地址的内存块进行大小的重新调整。基于此,我们就实现了存储内存的动态变化。__reallocate_memory_with_given_size作为一个私有函数,就是实现了这样的作用。我们给定准备调整内存大小的array结构体对象和新的目标的容量,进行一定的调整。

创建一个空白的动态数组

“Create Until Use”这个想法笔者非常喜欢。这个也是一种优化策略——直到你真的要用了,你再去创建它。否则不要为不存在的需求分配任何资源。

CCDynamicArray* create_blank_dynamic_array(size_t  elem_type)
{CCDynamicArray* array = malloc(sizeof(CCDynamicArray));array->holding_array = NULL;array->capicity = 0;array->element_size = elem_type;array->current_size = 0;return array;
}

笔者的这个实现当中创造了一个最空白的动态数组,在下面用户真正调用push_back或者insert的时候,才会分配资源。

bonus:pos_pointer,访问第i个元素的工具函数

C语言是没有泛型的,或者说,唯一的泛型void*不好用,我们必须自己偏移指针拿到对应元素的地址。举个例子,在一个存储了整形的数组中,外部程序只给定了数组地址和元素单个大小,如何访问第i个元素,我们需要使用这个工具函数

static inline char* pos_pointer(CCDynamicArray* array, size_t pos)
{return (char*)(array->holding_array + pos * array->element_size);
}

这个函数就等价于访问了arr[pos]了,为什么?,请看下面的图:

尾插一个元素到动态数组上

#define BASE_SZ (5)
void dynamic_array_push_back(CCDynamicArray* array, void* data, size_t elem_type)
{assert(array);assert(data);assert(elem_type == array->element_size);if(!array->holding_array){__reallocate_memory_with_given_size(array, BASE_SZ);}
​if(array->current_size >= array->capicity){__reallocate_memory_with_given_size(array, array->capicity * 2);}char* pos_p = pos_pointer(array, array->current_size);memcpy(pos_p, data, elem_type);array->current_size++;
}

插入的时候,我们总是需要考虑两种重要的情况:

  • 一开始,我们的holding_array为空,所以我们需要提前分配内存,保证内存访问合法

  • 如果我们的存储马上就要溢出了,就需要做扩容策略。笔者这里的策略是:扩大一倍。

处理完了内存,现在我们的访问是合法了,我们直接找到尾部,使用memcpy尾插数据,更新我们现在维护的数组大小

插入

这个是我们的重点,尾插实际上耗费的时间是O(N),为什么呢?举个例子,我们来说明一下,假设在一个5个元素大小的数组上,想插入一个元素到第三个位置上,我们需要怎么做呢?画一个图。

将剩下部分的元素,往后诺一个位置:

空出来的位置,留给我们的插入元素

当然,内存扩容的议题,我们就不再深究了,下面我们重点思考一个问题,一个问题是你看完这些内容中必然有想法的——那就是,如何界定剩下的元素往后挪这个说法?举个例子,我们向第三个位置,也就是上层应用程序提供了psoition=2的参数,我们需要挪动的是整个5个元素的后三个元素,也就是current_size - position个元素大小,想清楚整个事情,问题就很简单了。

void dynamic_array_insert(CCDynamicArray* array, void* data, size_t elem_type, size_t position)
{assert(array);assert(data);assert(elem_type == array->element_size);assert(position <= array->current_size);if(array->current_size == position){return dynamic_array_push_back(array, data, position);}if(!array->holding_array){__reallocate_memory_with_given_size(array, BASE_SZ);}
​if(array->current_size >= array->capicity){__reallocate_memory_with_given_size(array, array->capicity * 2);}
​memmove(pos_pointer(array, position + 1),pos_pointer(array, position),(array->current_size - position) * array->element_size);memcpy(pos_pointer(array, position), data, elem_type);array->current_size++;
}

memmove是一个这样的API:他将内存的数据搬运到我们的指定的位置。当然需要提供开始的地方和大小。我们向后挪一个位置,然后插入空出来的内存我们的数据!

尾删

整个很简单,我们直接给current_size减掉1,这样就好了,牢记一个事情,current_size维护了我们打算维护的数据结构的大小,换而言之,整个动态数组存在多少有效元素的事情

void dynamic_array_pop_back(CCDynamicArray* array)
{if(!array) return;if(array->current_size == 0){return;}array->current_size--;if(array->current_size < array->capicity / 2){__reallocate_memory_with_given_size(array, array->capicity / 2);}
}

值得一说的是,这里我们使用了缩一半的策略,在每一次pop的时候,我们发现一半的空间都被浪费了,我们才会去回收。

删除指定位置的元素

下一步,我们删除指定位置的元素。

删除指定地址的元素,只需要让给定位置的元素依次前挪即可。

void dynamic_array_erase(CCDynamicArray* array, size_t where)
{if(!array) return;if(array->current_size == 0){return;}assert(where < array->current_size);if(array->current_size == 1){return dynamic_array_pop_back(array);}memmove(pos_pointer(array, where),pos_pointer(array, where+1), (array->current_size - where) * array->element_size);   // 整个大小给出跟插入一个原理array->current_size--;
}

访问与删除

整个部分的代码就没啥好说的了,这里给出我们的代码

void dynamic_array_iterate(CCDynamicArray* array, IterationFunc func)
{for(size_t i = 0; i < array->current_size; i++){func(pos_pointer(array, i));}
}
​
void erase_dynamic_array(CCDynamicArray* array)
{free(array->holding_array);free(array);
}

回过头来思考我们的实现

我们说数组的通用插入和删除的时间复杂度是O(N),我们没有直接表现出来,但是memcpy和memmove的复杂度就是O(N)的,毕竟我们需要一个一个搬运我们的字节,因此,实际上还是O(N)的实现,但是我们访问内容,可以直接对着数据访问。

void* data_access(CCDynamicArray* array, size_t pos){return (void*)( (char*)array->holding_array + pos * array->element_size );
}

下表的访问最后会被编译成一条相对寻址的指令,所以,这就是连续内存的好处,从数组访问内容从来只需要知道第几个就可以O(1)常数时间完成,而且值非常的小!

http://www.whsansanxincailiao.cn/news/30335862.html

相关文章:

  • 网站建设 海豚弯/网络营销客服主要做什么
  • 北京网站建设方案案例/建网站的详细步骤
  • 服饰网站建设 e-idea/大数据精准获客软件
  • 在网站上签失业保险怎样做/seo优化公司哪家好
  • 做ppt如何从网站插入视频/seo网站关键词优化方式
  • 广州企业网站建设报价/网络营销课程有哪些
  • 买车平台十大排名/企业网站seo托管怎么做
  • tomcat 打开wordpress/优化设计答案五年级下册
  • 开发建设网站需要什么人才/seo排名优化公司价格
  • 建设一个网站的过程/长沙seo代理商
  • 软件工程师多少钱一个月/郴州seo快速排名
  • 出口网站制作/360应用商店
  • 游戏的网站策划应该怎么做/免费二级域名分发网站
  • 建设网站技术公司电话/百度免费资源网站
  • 怎样做企业营销网站/桂林网页
  • 网站后台制作教程/公司网站建设北京
  • 做网站原型图软件/自学seo能找到工作吗
  • 长沙移动网站建设哪家好/网络营销属于哪个专业
  • 网站开发团队投入/公司百度推广一年多少钱
  • 设计需要了解的网站/怎样在百度上做广告
  • 在线真正免费定位的网站/网站访问量排行榜
  • wordpress主题安装后图片找不到/武汉seo优化代理
  • 专业找人公司是真的吗/seo外包公司多吗
  • 电子商务网站建设阶段/广州seo搜索
  • 网站备案vpn注销/sem培训
  • 做网站博彩代理怎么找客源/制作一个网页的步骤
  • 网站建设万网/百度广告管家
  • 网站设计网/网上哪里可以免费打广告
  • 织梦网站建设/湖南长沙seo教育
  • 企业网站建设计什么科目/网站如何才能被百度收录