当前位置:   article > 正文

【数据结构】—— 顺序表

顺序表

目录

1、顺序表的概念和结构

1.1线性表

1.2顺序表的定义

1.3顺序表的分类

1.3.1 静态顺序表 

1.3.2 动态顺序表

2、顺序表的接口实现

2.1初始化

2.2销毁

2.3插入

准备知识:

2.3.1尾插

2.3.2头插

2.3.3指定位置插入

 2.4 删除      

2.4.1头删

2.4.2尾删

2.4.3指定位置删除

2.5查找

2.6打印

源代码 


1、顺序表的概念和结构

1.1线性表

 线性表(linearlist)是n个具有相同特性的数据元素的有限序列。线性表在逻辑结构是线性的,但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。

1.2顺序表的定义

         顺序表(Sequential List)是一种基本的数据结构,它通过一组连续的存储单元来存储具有相同类型的数据元素。

与数组的区别:

  1. 大小变化:数组的大小在创建时确定,之后不能改变;而顺序表的大小可以动态变化。
  2. 扩容机制:数组通常不支持直接扩容;顺序表在需要时可以通过重新分配内存空间来实现扩容。
  3. 使用场景:数组适用于元素数量固定不变或变化不大的场景;顺序表则更适用于元素数量需要动态变化的场景。

          (顺序表的底层结构是数组,对数组的封装,实现了常用的增删改查等接口)

bf88aaab14fa49db9639354b5864abb7.png

           顺序表逻辑结构是线性的,物理结构也是连续的。

特点:随机访问快、插入删除慢(需要移动元素)、存储空间固定或动态扩展 。

1.3顺序表的分类

顺序表是一个存储结构,因此什么数据类型都可以存储。将int类型的名字替换成SLDataTYpe,后续如果向存储别的类型,如char,short,结构体等类型时,只需要将int修改为char,short,结构体等类型,使得操作更加简化。

1.3.1 静态顺序表 

  1. typedef int SLdatetype;
  2. #define N 100
  3. typedef struct SeqList
  4. {
  5. SLdatetype arr[N];//定长数组:开辟空间大小为100的数组
  6. int size;//指有效数据的个数
  7. }SL;

概念:使用定长数组存储元素  

缺陷:  

  1. 固定大小限制

  2. 内存利用效率低

  3. 插入/删除困难

  4. 数据大小限制

  5. 难以管理

空间给少了溢出,给多了造成空间浪费。实际应用中出现较少

1.3.2 动态顺序表

  1. typedef struct Seqlist
  2. {
  3. SldateType* arr;//存储数据的底层结构
  4. int capacity;//空间容量
  5. int size;//有效数据的个数
  6. }SL;

概念:动态顺序表(Dynamic Array)是一种能够根据需要动态调整大小的数组结构,通常用于存储元素集合,并支持高效的随机访问和动态增删操作。

缺陷:

  1. 内存浪费

  2. 插入/删除效率低

  3. 容量调整耗时

  4. 不适合大对象存储

  5. 频繁扩容导致性能下降

a8e8df8c0f7e400e9205927f203aa984.png

2、顺序表的接口实现

准备工作:

创建三个代码文件:

1)SeqListc.h(头文件)—— 接口声明文件

2)  SeqList.c(源文件)——接口实现文件

3)  test.c(测试文件)——功能测试文件

aa3b30ee9c6f41a0b7339991b63fe6bc.png

2.1初始化

  1. void SlInit(SL *ps)
  2. {
  3. assert(ps != NULL);//判断ps是否为空
  4. ps->arr = NULL;
  5. ps->capacity = ps->size = 0;
  6. }

2.2销毁

  1. void Sldestory(SL* ps)
  2. {
  3. assert(ps != NULL);//判断ps是否为空
  4. ps->arr = NULL;
  5. ps->capacity = ps->size = 0;
  6. }

2.3插入

中间/头部的插入删除,时间复杂度为O(n)

准备知识:

顺序表的头插和尾插
1)空间足够,直接插入
2)空间不够,扩容
扩容的原则:

 1)一次扩容一个空间   
 2)一次扩容固定的大小
 3)成倍数地增加 (推荐:按倍数增加效率高,空间浪费相对较少)————动态内存管理
(三者都是分配内存,都是stdlib.h库里的函数(多次分配会造成程序效率低下)
malloc :用于动态分配指定字节数的内存空间。
calloc : 函数用于动态分配指定数目和大小的内存空间,并将内存初始化为零
realloc:函数用于重新分配之前分配的内存块大小。

perror的使用

assert的使用

扩容的完整代码:

一般采用1.5倍或2倍进行扩容(1.5倍或2倍扩容策略在内存管理中是一种折中方案,能够在保证性能的同时,有效地管理内存和提高程序运行效率。)

  1. void SlCheckCapacity(SL *ps)
  2. {
  3. if (ps->size == ps->capacity) {
  4. int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//三目运算符(如果是0则赋值为4,否则capacity*2)
  5. int* tmp = (SldateType*)realloc(ps->arr, newcapacity * sizeof(SldateType));//空间申请(注意开辟的是字节数)
  6. //判断申请空间成功
  7. if (tmp == NULL)
  8. {
  9. perror("realloc fail");
  10. exit(1);
  11. }
  12. //free(ps->arr);//为什么注释掉:因为realloc已释放arr原来的空间,无需再次释放
  13. ps->arr = tmp;
  14. ps->capacity = newcapacity;
  15. }
  16. }
2.3.1尾插
  1. void pushBack(SL* ps, SldateType x)//将x插入顺序表的尾部
  2. {
  3. //断言
  4. assert(ps != NULL);//如果为NULL直接报错
  5. SlCheckCapacity(ps);//判断是否需要扩容
  6. ps->arr[ps->size] = x;//尾插
  7. ps->size++;//有效数据个数 + 1
  8. }
2.3.2头插
  1. void pushFront(SL* ps, SldateType x)//将x插入顺序表的头部
  2. {
  3. assert(ps != NULL);//如果为NULL直接报错
  4. SlCheckCapacity(ps);//判断是否需要扩容
  5. //旧数据向后挪一位
  6. for (int i = ps->size; i > 0; i--)
  7. {
  8. ps->arr[i] = ps->arr[i - 1];
  9. }
  10. ps->arr[0] = x;//头插
  11. ps->size++;//有效数据个数 + 1
  12. }
2.3.3指定位置插入

pos为插入的位置。用assert限定插入的位置,防止出现负数或者过大的数。

  1. void Insert(SL* ps,int pos,SldateType x)
  2. {
  3. assert(ps != NULL);
  4. assert(pos >= 0 && pos <= ps->size);//检查pos下标的合法性
  5. SlCheckCapacity(ps);//判断是否需要扩容
  6. for (int i = ps->size; i > pos; i--)
  7. {
  8. ps->arr[i] = ps->arr[i - 1];
  9. }
  10. ps->arr[pos] = x;//指定位置插入元素x
  11. ps->size++;//有效数据个数 + 1
  12. }
  • 功能测试

  

 2.4 删除      

2.4.1头删
  1. void PopFront(SL* ps)
  2. {
  3. assert(ps != NULL);
  4. assert(ps->size);
  5. //不为空执行挪动
  6. for (int i = 0; i <ps->size-1; i++)
  7. {
  8. ps->arr[i] = ps->arr[i + 1];
  9. }
  10. ps->size--;//有效数据个数 - 1
  11. }
  • 功能测试

2.4.2尾删
  1. void PopBack(SL* ps)
  2. {
  3. assert(ps != NULL);
  4. assert(ps->size);
  5. ps->size--; //有效数据个数 - 1
  6. }
  • 功能测试

 2.4.3指定位置删除

pos为删除的位置。用assert限定插入的位置,防止出现负数或者过大的数。

  1. void Erase(SL* ps, int pos)
  2. {
  3. assert(ps != NULL);//如果为NULL直接报错
  4. assert(ps->size);//顺序表不能为空
  5. assert(pos >= 0 && pos < ps->size);//检查pos下标的合法性
  6. for (int i = pos; i < ps->size; i++)
  7. {
  8. ps->arr[i] = ps->arr[i + 1];
  9. }
  10. ps->size--;//有效数据个数 - 1
  11. }
  • 功能测试

2.5查找

  1. int Find(SL* ps,SldateType x)//查找指定元素在顺序表的位置
  2. {
  3. for (int i = 0; i < ps->size; i++)
  4. {
  5. if (ps->arr[i] == x) return i;//找到了返回pos
  6. }
  7. return -1;//没找到返回-1
  8. }
  • 功能测试

2.6打印

  1. void SlPrint(SL* ps)
  2. {
  3. for (int i = 0; i < ps->size; i++)//打印顺序表
  4. printf("%d ", ps->arr[i]);
  5. printf("\n");
  6. }
  • 功能测试

源代码 

.h头文件

  1. #pragma once//防止头文件被二次引用
  2. #include<stdio.h>
  3. #include<stdlib.h> /*realloc*/
  4. #include<assert.h> /*assert*/
  5. typedef int SldateType;
  6. //动态顺序表
  7. typedef struct Seqlist
  8. {
  9. SldateType* arr;//存储数据的底层结构
  10. int capacity;//空间容量
  11. int size;//有效数据的个数
  12. }SL;
  13. //初始化
  14. void SlInit(SL *ps);
  15. //销毁
  16. void Sldestory(SL* ps);
  17. //打印顺序表
  18. void SlPrint(SL* ps);//保持接口一致性
  19. //顺序表的头插和尾插
  20. void pushFront(SL* ps, SldateType x);//将x插入顺序表的头部
  21. //将x插入顺序表的尾部
  22. void pushBack(SL* ps, SldateType x);
  23. //删除顺序表的最后一个数
  24. void PopBack(SL* ps);
  25. //删除顺序表的第一个数
  26. void PopFront(SL* ps);
  27. //从指定位置pos之前插入元素x
  28. void Insert(SL* ps, int pos, SldateType x);
  29. //从指定位置pos删除元素
  30. void Erase(SL* ps, int pos);
  31. //查找元素x在顺序表的下标
  32. int Find(SL* ps, SldateType x);

.c文件

  1. #include"SeqList.h"
  2. //实现接口
  3. //初始化
  4. void SlInit(SL *ps)
  5. {
  6. assert(ps != NULL);//判断ps是否为空
  7. ps->arr = NULL;
  8. ps->capacity = ps->size = 0;
  9. }
  10. //销毁
  11. void Sldestory(SL* ps)
  12. {
  13. assert(ps != NULL);//判断ps是否为空
  14. ps->arr = NULL;
  15. ps->capacity = ps->size = 0;
  16. }
  17. void SlCheckCapacity(SL *ps)
  18. {
  19. if (ps->size == ps->capacity) {
  20. int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//三目运算符(如果是0则赋值为4,否则capacity*2)
  21. int* tmp = (SldateType*)realloc(ps->arr, newcapacity * sizeof(SldateType));//空间申请(注意开辟的是字节数)
  22. //判断申请空间成功
  23. if (tmp == NULL)
  24. {
  25. perror("realloc fail");
  26. exit(1);
  27. }
  28. //free(ps->arr);//为什么注释掉:因为realloc已释放arr原来的空间,无需再次释放
  29. ps->arr = tmp;
  30. ps->capacity = newcapacity;
  31. }
  32. }
  33. void pushBack(SL* ps, SldateType x)//将x插入顺序表的尾部
  34. {
  35. //断言
  36. assert(ps != NULL);//如果为NULL直接报错
  37. SlCheckCapacity(ps);//判断是否需要扩容
  38. ps->arr[ps->size] = x;
  39. ps->size++;
  40. }
  41. void pushFront(SL* ps, SldateType x)//将x插入顺序表的头部
  42. {
  43. assert(ps != NULL);//如果为NULL直接报错
  44. SlCheckCapacity(ps);//判断是否需要扩容
  45. //旧数据向后挪一位
  46. for (int i = ps->size; i > 0; i--)
  47. {
  48. ps->arr[i] = ps->arr[i - 1];
  49. }
  50. ps->arr[0] = x;//头插数据
  51. ps->size++;//有效数据个数 + 1
  52. }
  53. void SlPrint(SL* ps)
  54. {
  55. for (int i = 0; i < ps->size; i++)//打印顺序表
  56. printf("%d ", ps->arr[i]);
  57. printf("\n");
  58. }
  59. void PopBack(SL* ps)
  60. {
  61. assert(ps != NULL);
  62. assert(ps->size);
  63. ps->size--; //有效数据个数 - 1
  64. }
  65. void PopFront(SL* ps)
  66. {
  67. assert(ps != NULL);
  68. assert(ps->size);
  69. //不为空执行挪动
  70. for (int i = 0; i <ps->size-1; i++)
  71. {
  72. ps->arr[i] = ps->arr[i + 1];
  73. }
  74. ps->size--;//有效数据个数 - 1
  75. }
  76. void Insert(SL* ps,int pos,SldateType x)
  77. {
  78. assert(ps != NULL);
  79. assert(pos >= 0 && pos <= ps->size);
  80. SlCheckCapacity(ps);//判断是否需要扩容
  81. for (int i = ps->size; i > pos; i--)
  82. {
  83. ps->arr[i] = ps->arr[i - 1];
  84. }
  85. ps->arr[pos] = x;
  86. ps->size++;
  87. }
  88. void Erase(SL* ps, int pos)
  89. {
  90. assert(ps != NULL);
  91. assert(ps->size);
  92. assert(pos >= 0 && pos < ps->size);
  93. for (int i = pos; i < ps->size; i++)
  94. {
  95. ps->arr[i] = ps->arr[i + 1];
  96. }
  97. ps->size--;//有效数据个数 - 1
  98. }
  99. int Find(SL* ps,SldateType x)//查找指定元素在顺序表的位置
  100. {
  101. for (int i = 0; i < ps->size; i++)
  102. {
  103. if (ps->arr[i] == x) return i;//找到了返回pos
  104. }
  105. return -1;//没找到返回-1
  106. }
声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号