当前位置:   article > 正文

【数据结构】顺序表_删除表尾元素

删除表尾元素

目录

线性表

 顺序表

一、顺序表的存储

1.顺序表的静态存储

2.顺序表的动态存储

二、顺序表的初始化

三、顺序表的检查(动态增容)

四、顺序表的销毁释放 

五、顺序表的头尾操作

1.表尾插入元素

2.表尾删除元素

3.表头插入元素 

4.表头删除元素

5.顺序表查找某个元素

六、顺序表打印

 七、顺序表插入指定位置元素

八、 顺序表删除指定位置元素


前言:

线性表

先说一下线性表

线性表是一种数据结构,它由一组元素组成,这些元素按照一定的顺序排列。线性表中的元素可以是任意类型的,比如数字、字符、字符串、结构体等,在线性表中,每个元素都有一个编号,这个编号称作元素的下标或索引,从0开始递增。线性表中的元素之间存在一种线性关系,即元素只能按照一定的顺序依次排列,每个元素最多只有两个相邻的元素。常见的线性表包括数组、链表、栈、队列等。线性表通常用于实现各种算法和数据结构,如查找、排序、图论等。

 首先这里涉及 的一些 知识点 :结构体  和  malloc 等动态内存函数

如果感到理解有些难度可以先去学习上述 知识点。

 顺序表

一、顺序表的存储

存储定义:线性表的顺序存储结构,指的是用一段地址连续的存储单元依次存储线性表的数据元素。

1.顺序表的静态存储

 即 使用固定长度的数组来存储元素

  1. #define N 25
  2. //顺序表的静态存储
  3. typedef int SLDDataType;
  4. typedef struct SeqList
  5. {
  6. SLDDataType arr[N]; //定长数组
  7. size_t size; //有效数据的个数
  8. }SeqList;

该种定义的方法适用于已知数据元素的个数的情况下,提供的大小是固定的,定义N大了,就会浪费空间,定义N小了,就不够用。所以根据情况比较推荐适用动态顺序表。

2.顺序表的动态存储

  1. typedef struct SeqList
  2. {
  3. SLDataType* a; //定义的动态数组
  4. int size; //当前数据元素的个数
  5. int capacity; //数组的容量
  6. }SeqList;

先定义一个指针,使用malloc函数进行开辟

 下面采用动态扩容的方式

二、顺序表的初始化

  1. //初始化顺序表
  2. void SeqListInit(SeqList* ps)
  3. {
  4. assert(ps); //断言一下避免出现空指针
  5. ps->a = NULL;
  6. ps->size = 0;
  7. ps->capacity = 0;
  8. }

接下我们会顺序表的元素进行增、删、改、查,这样就需要对顺序表先进行检查

三、顺序表的检查(动态增容)

  1. //顺序表的检查
  2. void SLCheckCapacity(SeqList* ps)
  3. {
  4. //顺序表满了,使用realloc增容
  5. if (ps->size == ps->capacity)//有效数据个数 等于 当前容量
  6. {
  7. //先判断一下顺序表是否第一次使用空间
  8. int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
  9. SLDataType* tmp = (SLDataType*)realloc(ps->a,sizeof(SLDataType)*newcapacity);
  10. if (tmp != NULL)
  11. {
  12. ps->a = tmp;
  13. ps->capacity = newcapacity;
  14. }
  15. else
  16. {
  17. perror("SLCheckCapacity -> realloc");
  18. return;
  19. }
  20. }
  21. }

四、顺序表的销毁释放 

 顺序表的销毁

  1. //顺序表的销毁
  2. void SeqListDestory(SeqList* ps)
  3. {
  4. assert(ps);
  5. if (ps->a != NULL)
  6. {
  7. free(ps->a);//释放动态数组
  8. ps->a = NULL;
  9. ps->capacity = 0;
  10. ps->size = 0;
  11. }
  12. }

五、顺序表的头尾操作

1.表尾插入元素

从顺序表的表尾插入元素

  1. // 顺序表的尾插元素
  2. void SeqListPushBack(SeqList* ps,SLDataType x)
  3. {
  4. assert(ps); //避免指针为空
  5. //先进行顺序表的检查,动态增容
  6. SLCheckCapacity(ps);
  7. ps->a[ps->size] = x;//将元素x放到表尾
  8. ps->size++; //表示有效个数据个数增加了1个
  9. }

 注意 表尾插入元素的时间复杂度是O(1)

2.表尾删除元素

  1. //表尾删除元素
  2. void SeqListPopBack(SeqList* ps)
  3. {
  4. assert(ps);
  5. assert(ps->size > 0);
  6. ps->size--;
  7. }

 测试效果图:

  

3.表头插入元素 

思想:先从表尾开始,从前往后移动元素,再把要插入的元素放到表头。当表为空的时候直接插入

  1. //表头插入元素
  2. void SeqListPushFront(SeqList* ps, SLDataType x)
  3. {
  4. assert(ps);
  5. //检查容量,动态扩容
  6. SLCheckCapacity(ps);
  7. int end = ps->size - 1; //指向表尾
  8. while (end >= 0)
  9. {
  10. ps->a[end + 1] = ps->a[end]; //依次后移
  11. end--;
  12. }
  13. ps->a[0] = x;
  14. ps->size++;
  15. }

 注意:这里的时间复杂度尾O(N^2)

4.表头删除元素

 主要思想:从表的头部开始前移元素

  1. //表头删除元素
  2. void SeqListPopFront(SeqList* ps)
  3. {
  4. assert(ps);
  5. assert(ps->size > 0); //首先的保证有元素
  6. int begin = 1;
  7. while (begin <ps->size)
  8. {
  9. ps->a[begin-1] = ps->a[begin]; //从头部开始前移
  10. begin++;
  11. }
  12. ps->size--;
  13. }

测试效果图: 

5.顺序表查找某个元素

  1. //顺序表的查找
  2. int SeqListFind(SeqList* ps, SLDataType x)
  3. {
  4. assert(ps);
  5. int i = 0;
  6. for (i = 0; i < ps->size;i++)
  7. {
  8. if (x == ps->a[i])
  9. {
  10. return i; //查找成功返回下标
  11. }
  12. }
  13. //查找失败
  14. return -1;
  15. }

测试效果图:

六、顺序表打印

  1. //顺序表的打印
  2. void SeqListPrint(SeqList* ps)
  3. {
  4. assert(ps);
  5. int i = 0;
  6. for (i = 0; i < ps->size;i++)
  7. {
  8. printf("%d ",ps->a[i]);
  9. }
  10. }

 七、顺序表插入指定位置元素

  1. void SeqListInsert(SeqList* ps, int pos, SLDataType x)
  2. {
  3. assert(ps);
  4. //判断给出的位置是否合法
  5. assert(pos >= 0 && pos <= ps->size );
  6. SLCheckCapacity(ps);
  7. int end = ps->size - 1;
  8. while (end >= pos)
  9. {
  10. ps->a[end + 1] = ps->a[end];
  11. end--;
  12. }
  13. ps->a[pos] = x;
  14. ps->size++;
  15. }

测试效果图:

八、 顺序表删除指定位置元素

  1. //顺序表删除pos位置的值
  2. void SeqListErase(SeqList* ps, int pos)
  3. {
  4. assert(ps);
  5. //判断位置是否合法,注意这里是 小于 ps->size.
  6. //也就是要在数组范围内
  7. assert(pos >= 0 && pos < ps->size);
  8. SLCheckCapacity(ps);
  9. int begin = pos; //记录指定位置
  10. while (begin < ps->size-1)
  11. {
  12. ps->a[begin] = ps->a[begin + 1];
  13. begin++;
  14. }
  15. ps->size--;
  16. }

测试效果图:

【总结】 

线性表的顺序存储方式,顺序表。主要介绍对顺序表的增、删、改、查。

另外

说到线性表 ,可能会想到 数组,因为数组的空间是连续的,数组下标从0 到 n ,显然是有序的。

这里多说一下,数组的下标为什么从0开始呢?原因是 数组的下标表示元素在内存中的偏移量,也就是它相对于数组首地址的距离。而第一个元素相对于首地址的偏移量是0,因此第一个元素的下标为0,还有就是 方便 指针 与 数组 之间转换的自洽,p[0] == *(p+0)。 

 在测试效果中,可能会遇到越界的问题(越界一定会报错吗?
越界不一定报错,系统对越界的检查是一种抽查

  1. 进行读操作时,越界是检查不出来的
  2. 进行写操作时,如果是修改到标志位才会检查出来。(比如数组,系统会在数组末尾后设有标志位,越界写时,修改到标志位时,就会被检查出来,进行报错)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/637995
推荐阅读
相关标签
  

闽ICP备14008679号