这是一位即将大二的大学生(
卷狗)在暑假预习数据结构时的一些学习笔记,供大家参考学习。水平有限,如有错误,还望多多指正。
本文主要介绍了如何手动实现一个变长数组,以及实现其部分功能(如删除、查找、添加、排序等)
变长数组又可以叫柔性数组,与一般数组不同,它是一个动态的数组,具体表现为可以根据数组里面元素个数的多少而自动的进行扩容,以便达到变长(柔性)的特点。
为了实现自动边长扩容这一特点,在本文的代码中,主要使用了C语言中malloc这一函数,来进行动态内存分配,以下是关于malloc函数使用的简要说明
头文件引入:
#include
内存分配:
int * pArr = (int *)malloc(sizeof(int) * len);
其中,len为要申请的长度
内存释放:
free(pArr);
我们用结构体来定义一个数组,并且把数组的部分属性封装到这个结构体里
struct MyArr
{
int * pBase; //利用指针 储存数据
int len; //数组长度 --> 数组所能容纳最大元素个数
int cnt; // 数组有效长度
int increment; // 自动增长因子
}
由于数组每次扩充时,其长度是成倍的增加,而这个倍数就是自动增长因子,即 increment
void init(struct Arr * parr, int length); //length为初始长度
声明一个数组后,我们首先要对其进行初始化。
其实现原理也很简单,就是给结构体内部的指针变量(数组)动态开辟一段空间,并且初始化其最大长度和有效长度,并且给出自动增长因子的大小
具体函数实现如下:
void init(struct Arr * parr, int length) //注意要把地址传过来,才能真正改变内部数据
{
parr -> pBase = (int*)malloc(sizeof(int) * length); //初始内存大小
if(NULL == parr -> pBase) //内存分配失败
{
printf("内存分配失败");
exit(0); //程序终止
}
else
{
parr -> len = length; //初始长度
parr -> cnt = 0; //初始元素个数
parr -> increment = 2; // 默认增长因子为2
}
}
bool is_empty(struct Arr * pArr);
本函数用于判断数组是否为空,所以只需要判断结构体内 cnt 参数是否为 0 即可
数组为空的话返回 true ,否则返回 false
具体函数实现如下:
bool is_empty(struct Arr * pArr)
{
if(pArr -> cnt == 0) return true;
else return false;
}
bool is_full(struct Arr * parr);
用于判断数组是否已满,所以只需要判断结构体内 cnt 参数是否与len参数相等即可
数组满的话返回 true ,否则返回 false
具体函数实现如下:
bool is_full(struct Arr * pArr)
{
if(pArr -> cnt == pArr -> len) return true;
else return false;
}
void show(struct Arr * pArr)
实现原理也很简单,即把整个数组遍历一遍
void show(struct Arr * pArr)
{
if(is_empty(pArr)) //parr已经就是地址,所以这里写parr就可以 不用再取地址
printf("数组为空");
else
{
for(int i = 0; i < pArr -> cnt; i ++)
printf("%d ", pArr -> pBase[i]); //!!!
printf("\n");
}
}
遍历前可以先判断一下数组是否为空
以及注意一下数组内元素的书写
void enlarge(struct Arr * pArr);
这是笔者认为整个程序中的灵魂,即动态扩容,就是当结构体内有效长度 cnt 与最大长度 len 相等时,通过自动增长因子 increm 对原有数组进行成倍的变长扩容.
实现原理主要分三步走:
- 动态开辟。即动态开辟一个临时指针变量 temp ,长度为原有数组 pBase 长度的 increment 倍
- 数据转移。将原有数组 pBase 内的数据转移过来,然后再对原有的数组 pBase 进行内存释放(减少内存的浪费)
- 重新指向。最后再把原有数组 pBase 重新指向 temp ,即可实现动态扩容
函数具体实现:
void enlarge(struct Arr * parr)
{
int * temp = (int *)malloc(sizeof(int) * parr -> len * parr ->increment); //创建临时数组
for(int i = 0; i < parr -> len; i ++) //数据转移
temp[i] = parr -> pBase[i];
free(parr -> pBase);
parr -> pBase = temp; //重新指向
if(NULL == parr -> pBase) //异常处理
{
printf("ERROR");
exit(0);
}
}
void append(struct Arr * parr, int val); //val为要追加的数据
所谓追加,就是在已有的数组的末尾再添加一个数
函数具体实现:
void append(struct Arr * parr, int val)
{
if(is_full(parr)) //如果数组已满,要先扩容
{
enlarge(parr);
}
parr -> pBase[parr -> cnt ++] = val;
}
注意由于追加了一个数,所以要有 ++ 操作符
void insert(struct Arr * parr, int pos, int val); //pos是插入的位置,val是插入的值
参数注意说明: pos值从 1 开始,是指插入到第 pos 个数上
实现原理:首先要找到第 pos 个元素,确定下标,然后把该元素以及后面的元素与后移一位,最后插入即可。
函数具体实现:
void insert(struct Arr * parr, int pos, int val)
{
if(is_full(parr)) //长度不够
{
enlarge((parr));
}
if(pos < 1 || pos > parr -> cnt) //pos值不正确
{
printf("位置不正确\n");
return ;
}
else
{
for(int i = parr -> cnt - 1; i >= pos - 1; i --)
parr -> pBase[i + 1] = parr -> pBase[i]; //元素后移
parr -> pBase[pos - 1] = val;
parr -> cnt ++;
}
}
该函数的实现稍有复杂,主要是在对元素后移时要搞清楚下标,什么时候 +1 什么时候 -1 ,这是操作实现的关键
void deleat(struct Arr * parr, int pos); //pos值为删除的元素位置
实现原理:找到对应 pos 的元素,将该元素后面的元素前移一位,实现覆盖即可,与插入操作大同小异。
函数具体实现:
bool delet(struct Arr * parr, int pos)
{
if(is_empty(parr)) return false;
else if(pos < 1 || pos > parr -> cnt) return false;
else
{
for(int i = pos - 1; i < parr -> cnt - 1; i ++)
parr -> pBase[i] = parr -> pBase[i + 1];
parr -> cnt --;
}
}
注意最后不要忘记 cnt –
void inversion(struct Arr * parr);
该函数主要功能就是实现数组内元素的倒置
本文利用的是双指针前后夹击的思想对数组元素进行倒置操作,其他方法亦可
函数具体实现:
void inversion(struct Arr * parr)
{
int i = 0; //头指针
int j = parr -> cnt - 1; //尾指针
while(i < j) //前后夹击
{
int t = parr -> pBase[i];
parr -> pBase[i] = parr -> pBase[j];
parr -> pBase[j] = t;
i ++, j --;
}
}
void sort(struct Arr * parr);
该函数实现的是对数组进行排序(从小到大
直接在函数体里套个排序算法即可,我这里是因为好久没写快排了,所以写了个快排再熟悉一下找找手感。其实个人感觉冒泡就够用了
ps:u1s1 y总的快排板子真好用,吹爆y总hhh
具体函数实现:
void sort2(struct Arr * parr)
{
quick_sort(parr -> pBase, 0, parr -> cnt - 1);
}
void quick_sort(int a[],int l,int r) //快排
{
if(l >= r) return;
int x = a[l + r >> 1],i = l - 1,j = r + 1;
while(i < j){
do i ++; while(a[i] < x);
do j --; while(a[j] > x);
if(i < j)
{
int t = a[i];
a[i] = a[j];
a[j] = t;
}
}
quick_sort(a, l, j);
quick_sort(a, j + 1, r);
}
总的来说,变长数组在使用过程中还是很便利的,在大部分(所有)oop语言中都存在变长数组,如C++里的vector,Java里的ArrayList(C语言在C99中好像也引入了这一概念)
我也是初学者,才疏学浅,很多更实用的功能还没有实现。如果有错误,还望大家多多指正。
最后感谢大家的阅读,祝列位阖家欢乐,福寿康宁!!!