距离上一篇学习笔记隔离了好久哈,因为上学期没有开数据结构的课程,自学的。这学期捡起来。
我想在大家看这篇博客的时候,已经对数据结构的一些最最基础的东西都了解,比如说什么是算法啦,什么链表之类的,其实数据结构就是一种种的结构,用来处理各种问题。
下面我介绍一下顺序表。
一、什么是顺序表
大家知道数组吗?数组就是一种非常简单的顺序表,但是数组有局限性,数组需要指定大小,而且数据会超出。。真忧伤= =,但是我们可以通过一种方法让数组变成要多大就有多大的东东,高档吧?
顺序表就是这种东西,他和数组一样,申请的是一块连续的内存(链表不是连续的内存,是分开的),所以说我们可以通过下标来确定顺序表中的元素位置和信息,顺序表可以理解为强大的数组,因为他就是用数组实现的一种东东。
二、顺序表整体构建
我把我写的顺序表的整体法上来,然后再一段一段的分析,我想,长篇大论大家肯定看不进去,我紧握最大的努力让大家明白,大家一起拿下数据结构!!!
首先讲一下顺序表:
顺序表是有一个前驱,一个后继。
顺序表分别有四种基本操作以及很多其他的操作。因为四种基本操作掌握之后,拿下别的操作没有任何问题。
四种基本操作分别是:初始化,增加,插入,删除。
开始讲解:
在可爱的C语言中,顺序表是由一个结构体来作为基础的,其中包括三项:
typedef struct list {
Elemtype *elem; //设置顺序表主体
Elemtype length; //用来控制顺序表长度
Elemtype listsize; //用来控制顺序表容量
}
容量和大小是不一样的。比如说我初始化一个有10个空间的数组elem,向数组中放入4个数据,那么数组的容量就是10,而长度是4。
初始化操作:
因为顺序表是建立在结构体中的,那里只是一个定义而已哦,你得弄出一个真家伙来,那就是初始化操作,让顺序表真正的诞生。
在初始化之前,先在程序前面定义一个MORENUM和一个BASENUM 大小自己定义。一个是用来设置顺序表基础大小的,一个是用来扩增顺序表的,顺序表比数组的强大之处,就是没有大小的限制,那么每次不够了都要扩增一下,MORENUM就是用来扩增用的数。
void Initlist(LIST *L) {
L->elem = (int *)malloc(BASENUM * sizeof(int)); //首先给顺序表申请一段内存
if(!L->elem) { //错误排除 万一申请出错 报错
printf("error");
exit(0);
}
L->length = 0; //设置一个顺序表长度的参数,用来动态申请用,因为没有数据所以是0
L->listsize = BASENUM; //设置基础大小,用来动态申请用
}
增加和插入操作(我合成为了一个):
增加操作故名思议就是向顺序表中添加数据,因为这是智能的顺序表~~~所以不能直接向数组一样直接就赋值,需要考虑两个问题,第一插入数据的位置是否有数据,要是有的话那就必须将此位置的数据一次往后挪一个,然后空出来的位置放置新数据,这样就完成了插入操作。第二,还需要考虑一个问题就是我现在顺序表有10个空间,我想插入第11个数据,那么就必须扩增顺序表阿,因为咱们很智能,让他自己扩充。第三,插入操作不能乱插入,我有10个空间,你非要向20位置插入,可以插入,但是中间有空洞,这是不允许的,所以还得加个小判断,来判断位置是否合法。
下面是代码:
void cha(LIST *L,int i,int e) { //顺序表插入操作
int *p,*q; //定义两根指针,用来挪数据用
if (i < 1 || i > L->length + 1) { //判断插入位置是否合法
printf("error2");
exit(0);
}
if(L->length >= L->listsize) { //扩增表判断,如果长度大于容量了
int *newbase; //先弄一个新的指针
newbase = (int *)realloc(L->elem,(L->listsize + MORENUM) * sizeof(int));
//上面这句,把我们原来的表用realloc函数扩增,并且把结果给newbase指针
if (!newbase) { //仍然是错误判断
printf("error3");
exit(0);
}
L->elem = newbase; //然后把我们的基础设置为新的newbase,这样不就扩大了吗
L->listsize += MORENUM; //既然扩大了,那么容量就变成新的了
}
i--; //因为要插入的是第几个位置,数组是从0开始的哦,所以要减一
p = &L->elem[i]; //让p指针指向要插入的位置
for (q = &L->elem[L->length - 1]; q >= p; --q) {//这段放到后方解释,用来挪数据
*(q + 1) = *q;
}
*p = e; //挪完了数据是不是就有个空,数据放进去吧
++L->length; //然后让长度加一
}
下面解释一下刚才for循环:
我们放进去数据是不是要弄一个空出来,那么这个循环就是这个作用,挪动数据我们这里从来往前挪,将最后一个数据向后挪一位,然后到数第二个挪到最后一个一次类推,
q = &L->elem[L->length - 1]
用来指向最后一个位置
q >= p
判断是否挪完,我们用p指针指向了空的位置,挪到这里就不能在挪了。
--q
顺序表中的数据地址是连续的,这是顺序表的特点之一,所以我们这里的操作是在地址上进行的,那么这句就是用来标志位置的,一个一个往后挪位置,因为从后往前,所以--q
删除操作:
数列的删除操作同插入差不多,但是不用考虑表空间的扩增,用同样的for循环来
void elemDelete(LIST *L,int i,int e) { //顺序表插入操作
int *p,*q;
if (i < 1 || i > L->length + 1) {
printf("error2");
exit(0);
}
i--;
p = &L->elem[i];
for (q = &L->elem[L->length - 1]; q > p; p++) {
*p = *(p + 1);
}
--L->length;
}
首先通过一个例子来让大家整体理解顺序表
①输入一个单调递增有序数列,并且输入要输入的个数,然后输入一个数,插入到顺序表中,并且使插入后的顺序表仍然有序
上代码:
#include <stdio.h>
#include <stdlib.h> //定义头文件,需要用到realloc函数
#define BASENUM 3 //定义顺序表基础大小,大楼不也得有个地基吗
#define MORENUM 10 //因为是不限长度的,每次不够了咱就申请,设置一个申请大小(设置成1不就是要多少给多少了吗)
typedef struct list {
int *elem;
int length;
int listsize;
}LIST; //必然是用结构体构成顺序表的
void shu(LIST *L) { //输出函数(就像输出数组一样)
int i;
for (i = 0; i < L->length; i++) {
printf("%d ",L->elem[i]);
}
printf("剩余量为%d\n",L->listsize - L->length);
}
void Initlist(LIST *L) { //顺序表初始化
L->elem = (int *)malloc(BASENUM * sizeof(int)); //稍后介绍realloc和malloc函数,非常重要滴!这里就是申请一段大小的内存,然后给顺序表作为基础大小
if(!L->elem) { //错误排除 万一申请出错 报错
printf("error");
exit(0);
}
L->length = 0; //设置一个顺序表长度的参数,用来动态申请用
L->listsize = BASENUM; //设置基础大小,用来动态申请用
}
void cha(LIST *L,int i,int e) { //顺序表插入操作
int *p,*q;
if (i < 1 || i > L->length + 1) {
printf("error2");
exit(0);
}
if(L->length >= L->listsize) {
int *newbase;
newbase = (int *)realloc(L->elem,(L->listsize + MORENUM) * sizeof(int));
if (!newbase) {
printf("error3");
exit(0);
}
L->elem = newbase;
L->listsize += MORENUM;
}
i--;
p = &L->elem[i];
for (q = &L->elem[L->length - 1]; q >= p; --q) {
*(q + 1) = *q;
}
*p = e;
++L->length;
}
void fre(LIST *L) { //C语言用完了东西要知道施放,防止内存泄漏啊
free(L->elem);
L->elem = NULL;
L->length = 0;
L->listsize = 0;
}
int fang(LIST *L,int e) { //这是放进顺序表一个数使插入后的表仍然有序的操作
int i;
if (L->elem[0] > e) {
cha(L,1,e);
return 1;
}
if (L->elem[L->length - 1] < e) {
cha(L,L->length + 1,e);
return 1;
}
for (i = 0; i < L->length; i++) {
if (L->elem[i] <= e && L->elem[i + 1] > e) {
cha(L,i + 2,e);
return 1;
}
}
}
int main (void) { //主函数
int i,j;
int p,e;
LIST L;
Initlist(&L);
printf("请输入要输入的个数:");
scanf("%d",&j);
printf("请输入有序的数列:");
for (i = 1; i <= j; i++) {
scanf("%d",&e);
cha(&L,i,e);
}
shu(&L);
printf("请输入要插入的数字:");
scanf("%d",&p);
fang(&L,p);
shu(&L);
fre(&L);
}
推荐大家看一遍代码注释了解一下大体思路在来看具体解法,思路清晰才能做好程序。