先说一下什么是顺序表吧:顺序表的底层结构是数组,对数组的封装,实现了常⽤的增删改查等接⼝ 。顺序表也有静态顺序表,就是利用数组进行实现,这样的内存空间上相对不便控制,让我们来实现一下动态顺序表吧。
1.我们先建立一下动态顺序表的动态数组结构
typedef int innt;//因为我们用的是int的数据进行存储,但是又别的情况,所以我们给int 重命名一下,会方便以后别的类型的使用更改。
typedef struct list
{
innt* arr;
int capcity;//这是顺序表的容量
int sz;//这是顺序表实际的有效数据
}list;
2.让我们给顺序表初始化一下吧
void chushihua(list* ps)
{
ps->arr = NULL;//将一开始的动态数组设为空指针
ps->capcity = ps->sz = 0;//一开始没有空间和数据都是零
}
3.初始化之后,我们来说说要为动态数组开辟空间。也就是扩容,怎么扩。
扩容有三种扩容的方式<1>往进写一个元素,就扩容一个元素的大小。想想是不是有点麻烦
<2>.第二种扩容方式是固定一个值的扩容,假如每次扩容一百,那么我存了101,那是不是就浪费了99的空间,这样做是有一点浪费的
<3>.第三种就是我们要用的一种扩容原则,成倍扩容,我们以二倍为例,也就是2,4,8,16,32。。这样的扩容方式是最常见的扩容方式,相比起来,最能节省空间。让我们实现一下。
void checkcap(list* ps)
{
if (ps->capcity == ps->sz)//如果有效数据堆满了空间,我们就得扩容
{
int c=ps->capcity == 0 ? 2 : 2 * sizeof(ps->capcity);
innt* ret = (innt*)realloc(ps->arr, 2 * sizeof(innt));
if (ret == NULL)
{
perror("realloc false");
exit(1);
}
ps->arr = ret;
ps->capcity = c;
}
}
我们用三目操作符判断一下容量是不是为零,不为零就正常扩二倍。接着我们给动态数组扩容,并且把地址传给了它。
<1>return 1;和exit(1)的区别
return 只会结束当前的程序,其他的子程序可能会继续走,而exit(1),会立即结束整个程序。
<2>每次的断言ps是防止是空指针
4.扩容之后我们就要,进行第一个操作尾插。
void pushback(list* ps,innt x)
{
assert(ps);
checkcap(ps);
ps->arr[ps->sz] = x;
ps->sz++;
}
有了扩容函数,操作很方便。我们只需要让有效数据的个数作为数组的下标,我们就可以进行存储这个数字。让我们调试一下,看看成没成功。看起来还是不错的
void print(list* ps)
{
for (int i = 0; i < ps->sz; i++)
{
printf("%d->", ps->arr[i]);
}
}
,让我们打印出来看一下。
5,写完了尾插,我们写一下头插。头插就是arr[0]=我们输入的值,让原来的数据只要往后挪一位就可以了。
void pushfront(list* ps, innt x)
{
assert(ps);
checkcap(ps);
for (int i = ps->sz; i > 0; i--)
{
ps->arr[i ] = ps->arr[i-1];
}
ps->arr[0] = x;
ps->sz++;
}
6 前面的插入和后面的插入我们已经完成了,那么我们完成一下头删和尾删。
尾删相对简单,我们只需要把有效数据的个数减一个就可以了,下次存储都不会影响。
头删相对于来说,和头插也差不多,一个往后挪,一个往前挪。
void popback(list* ps)
{
assert(ps);
assert(ps->sz);
ps->sz--;
}
void popfront(list* ps)
{
assert(ps);
assert(ps->arr);
for (int i = 0; i < ps->sz-1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->sz--;
}
宝贝是不是越写越轻松呢。
7.任意位置的插入和删除
插入:我们可以想象一下排列好的数组,我们想要在哪里插入,就传入位置和数据就可以了。把该位置的数据之后的数据往后挪一位,把该数据插入进去,是不是也很简单呢,简直和头插一样,只不过一个头不固定,一个头固定。
void insert(list* ps, int pos, innt x)//pos是要更改的位置
{
assert(pos > 0 && pos <= ps->sz);
checkcap(ps);
for (int i = ps->sz - 1; i >= pos-1; i--)
{
ps->arr[i + 1] = ps->arr[i];
}
ps->arr[pos - 1] = x;
ps->sz++;
}
<1>请大家注意每次的插入一定要把ps->size进行加一
<2>对pos的范围进行断言很重要
void shanchu(list* ps, int pos)
{
assert(ps);
assert(pos >= 0 && pos < ps->sz);
for (int i = pos - 1; i < ps->sz; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->sz--;
}
8再坚持坚持,我们来到最后一步,销毁,要把我们申请的空间还给系统
void destory(list* ps)
{
assert(ps);
list* ret = ps;
free(ps->arr);
ps ->arr= NULL;
ret->capcity = ret->sz = 0;
}
9 让我总结一下,对顺序表要掌握指针,结构体,熟练掌握才可以运用的很彻底。
第二个就是运用assert的断言,我们目的就是为了防止空间的乱用,避免传过来的是空指针从而导致数据的存储失败。
10代码呈上
//list.h
#pragma once
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>
typedef int innt;
typedef struct list
{
innt* arr;
int capcity;
int sz;
}list;
void chushihua(list* ps);
void pushback(list* ps,innt x);
void print(list* ps);
void pushfront(list* ps, innt x);
void popback(list* ps);
void popfront(list* ps);
void insert(list* ps, int pos, innt x);
void shanchu(list* ps, int pos);
void destory(list* ps);
//list.c
#include"list.h"
void chushihua(list* ps)
{
ps->arr = NULL;//将一开始的动态数组设为空指针
ps->capcity = ps->sz = 0;//一开始没有空间和数据都是零
}
void checkcap(list* ps)
{
if (ps->capcity == ps->sz)//如果有效数据堆满了空间,我们就得扩容
{
int c=ps->capcity == 0 ? 2 : 2 * sizeof(ps->capcity);
list* ret = (list*)realloc(ps->arr, 2 * sizeof(innt));
if (ret == NULL)
{
perror("realloc false");
exit(1);
}
ps->arr = ret;
ps->capcity = c;
}
}
void pushback(list* ps,innt x)
{
assert(ps);
checkcap(ps);
ps->arr[ps->sz] = x;
ps->sz++;
}
void print(list* ps)
{
for (int i = 0; i < ps->sz; i++)
{
printf("%d->", ps->arr[i]);
}
printf("\n");
}
void pushfront(list* ps, innt x)
{
assert(ps);
checkcap(ps);
for (int i = ps->sz; i > 0; i--)
{
ps->arr[i ] = ps->arr[i-1];
}
ps->arr[0] = x;
ps->sz++;
}
void popback(list* ps)
{
assert(ps);
assert(ps->sz);
ps->sz--;
}
void popfront(list* ps)
{
assert(ps);
assert(ps->arr);
for (int i = 0; i < ps->sz-1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->sz--;
}
void insert(list* ps, int pos, innt x)
{
assert(pos > 0 && pos <= ps->sz);
checkcap(ps);
for (int i = ps->sz - 1; i >= pos-1; i--)
{
ps->arr[i + 1] = ps->arr[i];
}
ps->arr[pos - 1] = x;
ps->sz++;
}
void shanchu(list* ps, int pos)
{
assert(ps);
assert(pos >= 0 && pos < ps->sz);
for (int i = pos - 1; i < ps->sz; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->sz--;
}
void destory(list* ps)
{
assert(ps);
if (ps->arr == NULL)
{
return;
}
list* ret = ps;
free(ps->arr);
ps ->arr= NULL;
ret->capcity = ret->sz = 0;
}
//test.c
#include"list.h"
void test()
{
list ps ;
chushihua(&ps);
pushback(&ps, 1);
pushback(&ps, 2);
pushback(&ps, 3);
pushback(&ps, 4);
print(&ps);
insert(&ps, 2, 40);
print(&ps);
shanchu(&ps, 2);
print(&ps);
}
int main()
{
test();
return 0;
}