顺序表操作详解

简介: 顺序表操作详解

一、顺序表结构定义

       数组可以存储数据,而对数组的数据进行操作,例如增删改查等操作被称为顺序表,顺序表需要大量用到C语言的结构体与指针,我们先来想想,如果想要对一个数组进行数据操作,比如插入元素操作,首先肯定是需要一个数组来存储数据的,那么对于要插入位置的索引是不是还需要一个角标,用来记录元素的个数,在进行元素索引的时候以便于快速找到。

typedef struct vector {//结构体定义
  int size, count;//size为数组大小,count为数组存储元素个数
  int* data;//data为数组
}vector;//重命名为vector

二、顺序表初始化

       在我们使用这个顺序表之前,需要先初始化顺序表一下,相信你也看到了,结构体内采用的存储方式为指针,那么就意味着在初始化的时候需要对指针进行malloc处理。

vector* InitNewVector(int n)//结构体初始化,n为开辟空间大小
{
  vector* v = (vector*)malloc(sizeof(vector));//开辟这个结构体
  v->size = n;//记录空间大小为n
  v->count = 0;//此时数组内部没有元素
  v->data = (int*)malloc(sizeof(int) * n);//动态数组开辟空间
  return v;//返回开辟好的结构体
}

       顺序表的初始化操作我们就完成了,这个时候你已经拥有了一个顺序表,只不过这个时候顺序表内还没有元素,那么接下来我们就需要实现数据结构的基本操作了,增删改查。

三、内存释放

       首先,既然创造了这个顺序表是由堆里面开辟的,那么就需要开发者自己手动的来回收这些内存,接下来来实现一下顺序表销毁操作:

void clearVector(vector* v)//销毁顺序表,传入顺序表指针
{
  if (v == NULL)
    return;
  free(v->data);//先free掉顺序表内部动态开辟的数组
  free(v);//再将顺序表给销毁
  return;
}

值得注意的是,在销毁顺序表需要由内而外销毁,如果直接销毁整个顺序表并不会自动帮你把内部数组销毁,反而这样会让你丢失对应的地址在想要释放内部数组就很困难了。

四、插入操作

       接下来进行顺序表的插入操作,在实现操作之前,你需要知道再插入之前的特别情况是什么, 如果传入函数的位置不对,或者顺序表内部数组元素(count)个数大于了数组大小(size)是属于错误情况的,还有一种重要的情况是当数组元素满了(size == count)的时候也是需要返回。

int insert(vector* v, int pos, int val)//插入操作,对pos位置插入val值
{
  if (v->size < pos || pos < 0) return 0;//排除错误情况
  if (v->size == pos)//当数组满了也需要进行判断
    return 0;//返回0代表插入失败
  for (int i = v->count - 1; i >= pos; i--)//数组进行插入前需要将要插入位置之后的数据都往后移动一个单位
    v->data[i + 1] = v->data[i];//向后移动
  v->data[pos] = val;//此时pos位置的值已经被空下来了直接插入就行
    v->count += 1;//插入完之后数组元素个数进行记录一下
  return 1;//返回1代表插入成功
}

五、删除操作

       顺序表插入操作已经完成了, 接下来实现元素的删除操作,同插入相似,删除的位置如果小于0或者大于size都返回0。

int delete(vector* v, int pos)//传入结构体指针与要删除的位置
{
  if (v == NULL) return 0;
  if (pos < 0 || v->size < pos)//排除边界情况
    return 0;
  for (int i = pos + 1; i < v->count; i++)//进行元素删除
    v->data[i - 1] = v->data[i];
  v->count -= 1;//删除完元素个数减一
  return 1;//删除成功返回1
}

删除,插入操作已经实现出来,查找操作非常简单可以自己尝试去编码一下,这里就不详细展示了。

六、实现随机插入删除

       接下来便是如何把数据进行体现出来,在这里我采用随机插入随机删除的方法进行代码演示,原理就是状态码进行分发,在接收任务时进行概率分配任务,详细如下:

int main()
{
  srand((unsigned int)time(NULL));//产生伪随机
  vector* v = InitNewVector(2);//初始化生成顺序表
    #define MAX_OP 10
  for (int i = 0; i < MAX_OP; i++)//进行数据操作的此数
  {
    int pos, val, op = rand() % 5;//pos为位置,val为待插入值,op为概率分配任务,在0-4之间分配任务随机产生数字
    switch (op)//经过op分发任务删除概率为20%,插入概率为80%
    {
        case 0:
        {
          pos = rand() % (v->count + 1);//插入位置进行随机
          printf("Delete item %d at vector! = %d\n"
          , pos, delete(v, pos));
          break;
        }
        case 1:
        case 2:
        case 3:
        case 4:
        {
          pos = rand() % (v->count + 1);
          val = rand() % 100 + 1;
          printf("Insert %d to %d at vector! = %d\n"
          , val, pos, insert(v, pos, val));
          break;
        }
    }
    output(v);//打印函数,最后每次插入都会进行打印
  }
  clearVector(v);//销毁顺序表
  return 0;
}

伪随机产生随机数,在进行状态码分发方式进行对插入删除操作进行任务分配,最后输出顺序表中的内容。

七、输出方式

       为了使顺序表输出方式更加美观,我采用自己的一种常用输出方式:

void output(vector* v)//顺序表输出操作
{
  if (v == NULL)
    return;
  int len = 0;
  for (int i = 0; i < v->count; i++)
  len += printf("%3d", i);
  printf("\n");
  for (int i = 0; i < len; i++)
  printf("-");
  printf("\n");
  for (int i = 0; i < v->count; i++)
  printf("%3d", v->data[i]);
  printf("\n");
  printf("\n");
  printf("\n");
  return;
}

这样输出结果为:

这样是不是就美观一些,当然有艺术细胞的同志可以自行设计。

八、插入操作改变以及扩容操作

       现在有个新的问题,如果顺序表满了,那该怎么办?难道在写一份顺序表吗?答案是否定的,如果你C语言学了realloc这个函数,那么你扩容的问题就可以简单解决 。首先,要思考是在什么情况下才需要进行扩容,在哪步操做下需要扩容?没错,当插入元素满了的时候进行扩容操作,所以可以这样设计插入函数:

int insert(vector* v, int pos, int val)
{
  if (v->size < pos || pos < 0) return 0;
  if (v->size == pos  && !expand(v))//在这里进行扩容判断,如果扩容成功则继续插入操作否则return 0
    return 0;
  for (int i = v->count - 1; i >= pos; i--)
    v->data[i + 1] = v->data[i];
  v->count += 1;
  v->data[pos] = val;
  return 1;
}

那么该如何实现扩容函数呢?其实很简单,用一个整形指针变量接收realloc后的值,在进行判断是否扩容失败,如果成功则把这个变量的值赋给结构体的数组,这里realloc的值可以自行调整大小,我这里默认扩容两倍大小。

int expand(vector* v)
{
  if (v == NULL)
    return 0;
  int *tmp = (int *)realloc(v -> data, sizeof(v->size) * 2 * sizeof(int));//指针变量接收realloc后的值防止扩容失败造成崩溃
  if (tmp == NULL)//对realloc函数是否扩容成功进行检查
  {
    perror("realloc");
    return 0;
  }
  v->data = tmp;
  printf("Expand %d from %d at vector!\n", v->size, v->size * 2);//扩容成功进行提示
  v->size *= 2;//将容量进行相同倍数扩大
  return 1;//成功返回1
}

这样输出的结果为:

 

       可以看到确实发生了扩容操作,这样一个完整的顺序表就实现出来了!如果有帮到你的话还望三连多多支持作者~~

 

相关文章
|
19天前
|
存储 测试技术 C语言
顺序表详解(SeqList)
顺序表详解(SeqList)
41 0
|
8月前
|
存储
顺序表操作详解
顺序表操作详解
50 0
|
8月前
|
存储
【顺序表】
【顺序表】
34 0
|
19天前
|
存储
实现顺序表的增删查改
现在让我们探索数据结构这个美妙的世界吧!
16 0
|
19天前
浅谈顺序表基本操作
浅谈顺序表基本操作
|
19天前
|
存储
顺序表讲解
顺序表讲解
26 0
|
19天前
顺序表的实现
顺序表的实现
|
7月前
|
测试技术
顺序表(2)
顺序表(2)
530 0
|
7月前
06 顺序表操作
06 顺序表操作
15 0
|
7月前
|
存储 NoSQL
03 顺序表
03 顺序表
17 0