当前位置:   article > 正文

【数据结构】——顺序表

【数据结构】——顺序表

目录

前言:        

顺序表

动态顺序表的实现

代码总览:


前言:
        

数据结构是由“数据”和“结构”两词组合而来。

        什么是数据?

常见的数值1、2、3、4.....、教务系统⾥保存的⽤⼾信息(姓名、性别、年龄、学历等

等)、网页里肉眼可以看到的信息(文字、图片、视频等等),这些都是数据

        什么是结构?

当我们想要使⽤大量使⽤同⼀类型的数据时,通过⼿动定义⼤量的独立的变量对于程序来说,可读性非常差,我们可以借助数组这样的数据结构将⼤量的数据组织在⼀起,结构也可以理解为组织数据的方式

简而言之

  1. 能够存储数据(如顺序表、链表等结构)
  2. 存储的数据能够方便查找

那么为什么需要数据结构呢?

假定数组有10个空间,已经使用了5个,向数组中插入数据步骤:

求数组的长度,求数组的有效数据个数,向下标为数据有效个数的位置插入数据(注意:这里是

否要判断数组是否满了,满了还能继续插⼊吗).....

假设数据量非常庞大,频繁的获取数组有效数据个数会影响程序执行效率。

结论:

        最基础的数据结构能够提供的操作已经不能完全满⾜复杂算法实现。

顺序表

线性表

        线性表是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串等

        线性表在了逻辑上是线性结构,也就是说是一条直线。但是在物理结构上并不一定是连续的,线性表在物理结构上存储时,通常以数组和链式结构的形式存储。

知识补充:

逻辑结构和物理结构

1.逻辑结构:

所谓逻辑结构就是数据与数据之间的关联关系,准确的说是数据元素之间的关联关系。

注:所有的数据都是由数据元素构成,数据元素是数据的基本构成单位。而数据元素由多个数据项构成。

逻辑结构有四种基本类型:集合结构、线性结构、树状结构和网络结构。也可以统一的分为线性结构和非线性结构。

2.物理结构:

数据的物理结构就是数据存储在磁盘中的方式。官方语言为:数据结构在计算机中的表示(又称映像)称为数据的物理结构,或称存储结构。它所研究的是数据结构在计算机中的实现方法,包括数据结构中元素的表示及元素间关系的表示。

而物理结构一般有四种:顺序存储,链式存储,散列,索引

顺序表

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

顺序表可以分为静态顺序表和动态顺序表

        静态顺序表

静态顺序表是使用定长的数组来存储元素

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

使用动态顺序表缺陷:空间给小了不够用,空间给多了造成空间浪费

        动态顺序表

动态顺序表的实现

静态顺序表是定长数组,而动态顺序表是可增容的,不会浪费空间也不会出现空间不够的场景,这里来实现动态顺序表存储整形数据

首先就是顺序表的一些功能

这里将其写入SeqList.h 的头文件中

SeqList.h头文件

  1. #pragma once
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<assert.h>
  5. typedef int Type;
  6. typedef struct SeqList//动态顺序表
  7. {
  8. Type* arr;
  9. int size;
  10. int num;
  11. }SL;
  12. //动态顺序表
  13. // 初始化
  14. void SLInit(SL* p);
  15. //销毁
  16. void SLDesTroy(SL* p);
  17. //输出
  18. void SLPrint(SL* p);
  19. //顺序表扩容
  20. void SLExps(SL* p);
  21. //从头部插入数据
  22. void SLAddHand(SL* p, Type x);
  23. //从头部删除数据
  24. void SLDelHand(SL* p);
  25. //从尾部插入数据
  26. void SLAddEnd(SL* p, Type x);
  27. //从尾部删除数据
  28. void SLDelEnd(SL* p);
  29. //从任意位置插入数据
  30. void SLAddeve(SL* p, Type x, int t);
  31. //从任意位置删除数据
  32. void SLDeleve(SL* p, int t);
  33. //查找数据
  34. int SLFind(SL* p, Type f);

要存储一些数据,顺序表具备以上功能(对于整型)

顺序表初始化

        顺序表初始化,其实就是将动态顺序表中指针置为NULL,有效数据和空间容量置为0;

代码如下:

  1. // 初始化
  2. void SLInit(SL* p)
  3. {
  4. p->arr = NULL;
  5. p->size = 0;
  6. p->num = 0;
  7. }

在初始化完成后s中的数据。

顺序表销毁

        在使用完顺序表后,就要销毁顺序表,因为动态顺序表内存是动态开辟的,所以需要对动态内存进行释放,并将有效数据和空间容量个数置为0;

代码如下:

  1. //销毁
  2. void SLDesTroy(SL* p)
  3. {
  4. if (p->arr != NULL)
  5. {
  6. free(p->arr);
  7. p->arr = NULL;
  8. }
  9. p->size = 0;
  10. p->num = 0;
  11. }

        顺序表销毁之后,指针置为NULL,有效数据和空间容量的为0;

顺序表输出

        现在如果顺序表中有数据,我们需要查看数据,就要用到顺序表的输出

代码如下:

  1. //输出
  2. void SLPrint(SL* p)
  3. {
  4. for (int i = 0; i < p->size; i++)
  5. {
  6. printf("%d ", p->arr[i]);
  7. }
  8. printf("\n%d %d\n", p->size, p->num);
  9. }

测试看一下输出

        这里将有效数字和空间容量也输出出来以便查看,下面插入数据以后就不在进行输出了

顺序表扩容

        我们要像顺序表中插入数据,这必然会涉及到扩容的问题

代码如下:

  1. //顺序表扩容
  2. void SLExps(SL* p)
  3. {
  4. int num = (p->num == 0) ? 4 : 2 * p->num;
  5. Type* tmp = (Type*)realloc(p->arr, num * sizeof(Type));
  6. assert(tmp);
  7. p->arr = tmp;
  8. p->num = num;
  9. }

如果首次扩容,即p->num等于0,这样习惯给它赋值成4,以后每次扩容以倍数增加(这里使用2倍,也可以使用3倍)。

扩容以后再测试看一下输出结果(查看有效数据和空间容量)

这里首次扩容,空间容量为4。

        注:扩容主要使用在插入数据判断空间大小不够时

顺序表头插

现在需要从顺序表头部(起始位置)插入数据,这里就需要将有效数据向后移动一位,再进行插入数据以防数据丢失。

        当然,再插入数据之前需要判断空间是否足够

代码如下:

  1. //从头部插入数据
  2. void SLAddHand(SL* p, Type x)
  3. {
  4. if (p->size >= p->num)
  5. {
  6. SLExps(p);
  7. }
  8. for (int i = p->size; i > 0; i--)
  9. {
  10. p->arr[i] = p->arr[i - 1];
  11. }
  12. p->arr[0] = x;
  13. p->size++;
  14. }

测试以下代码是否正确

顺序表头删

从起始位置删除数据,就需要把有效数据向前移动一位,并且有效数据个数-1;

代码如下:
 

  1. //从头部删除数据
  2. void SLDelHand(SL* p)
  3. {
  4. for (int i = 0; i < p->size - 1; i++)
  5. {
  6. p->arr[i] = p->arr[i + 1];
  7. }
  8. p->size--;
  9. }

测试:

顺序表尾插

现在从尾部插入数据,很简单直接在数据末尾插入数据,然后有效数据+1;

        当然,也需要进行判断空间是否足够

代码如下:

  1. //从尾部插入数据
  2. void SLAddEnd(SL* p, Type x)
  3. {
  4. if (p->size >= p->num)
  5. {
  6. SLExps(p);
  7. }
  8. p->arr[p->size] = x;
  9. p->size++;
  10. }

测试:

顺序表尾删

从尾部删除数据很简单马,可以直接让有效数据个数-1;

代码如下:

  1. //从尾部删除数据
  2. void SLDelEnd(SL* p)
  3. {
  4. p->size--;
  5. }

测试:

顺序表任意位置插入

        从任意位置插入数据,需要将指定位置数据以后的有效数据向后移动一位,再进行插入

代码:

  1. //从任意位置插入数据
  2. void SLAddeve(SL* p, Type x, int t)
  3. {
  4. if (p->size >= p->num)
  5. {
  6. SLExps(p);
  7. }
  8. for (int i = p->size; i > t; i--)
  9. {
  10. p->arr[i] = p->arr[i - 1];
  11. }
  12. p->arr[t] = x;
  13. p->size++;
  14. }

这里就不输出有效数字个数和空间容量了

测试:

顺序表任意数据删除

从任意位置删除数据,将该位置后的数据向前移动一位

代码如下:

  1. //从任意位置删除数据
  2. void SLDeleve(SL* p, int t)
  3. {
  4. for (int i = t; i < p->size - 1; i++)
  5. {
  6. p->arr[i] = p->arr[i + 1];
  7. }
  8. p->size--;
  9. }

测试:

顺序表查找

现在我们需要在顺序表中查找数据

这里也可以写函数返回数据的下标

代码:

  1. //查找数据
  2. void SLFind(SL* p, Type f)
  3. {
  4. for (int i = 0; i < p->size; i++)
  5. {
  6. if (p->arr[i] == f)
  7. {
  8. printf("查找的数据下标为:%d\n", i);
  9. return;
  10. }
  11. }
  12. printf("所查找的数据不存在\n");
  13. }

测试:

到这里,顺序表的知识就完成了,学完这些,我们也要写顺序表的实践,就是通讯录——在下一篇进行实现。

代码总览:

SeqList.h

  1. #pragma once
  2. #define _CRT_SECURE_NO_WARNINGS
  3. #include<stdio.h>
  4. #include<stdlib.h>
  5. #include<assert.h>
  6. typedef int Type;
  7. typedef struct SeqList//动态顺序表
  8. {
  9. Type* arr;
  10. int size;
  11. int num;
  12. }SL;
  13. //动态顺序表
  14. // 初始化
  15. void SLInit(SL* p);
  16. //销毁
  17. void SLDesTroy(SL* p);
  18. //输出
  19. void SLPrint(SL* p);
  20. //顺序表扩容
  21. void SLExps(SL* p);
  22. //从头部插入数据
  23. void SLAddHand(SL* p, Type x);
  24. //从头部删除数据
  25. void SLDelHand(SL* p);
  26. //从尾部插入数据
  27. void SLAddEnd(SL* p, Type x);
  28. //从尾部删除数据
  29. void SLDelEnd(SL* p);
  30. //从任意位置插入数据
  31. void SLAddeve(SL* p, Type x, int t);
  32. //从任意位置删除数据
  33. void SLDeleve(SL* p, int t);
  34. //查找数据
  35. void SLFind(SL* p, Type f);

SeqList.c

  1. #include"SeqList.h"
  2. // 初始化
  3. void SLInit(SL* p)
  4. {
  5. p->arr = NULL;
  6. p->size = 0;
  7. p->num = 0;
  8. }
  9. //销毁
  10. void SLDesTroy(SL* p)
  11. {
  12. if (p->arr != NULL)
  13. {
  14. free(p->arr);
  15. p->arr = NULL;
  16. }
  17. p->size = 0;
  18. p->num = 0;
  19. }
  20. //输出
  21. void SLPrint(SL* p)
  22. {
  23. for (int i = 0; i < p->size; i++)
  24. {
  25. printf("%d ", p->arr[i]);
  26. }
  27. printf("\n");
  28. //printf("%d %d\n", p->size, p->num);
  29. }
  30. //顺序表扩容
  31. void SLExps(SL* p)
  32. {
  33. int num = (p->num == 0) ? 4 : 2 * p->num;
  34. Type* tmp = (Type*)realloc(p->arr, num * sizeof(Type));
  35. assert(tmp);
  36. p->arr = tmp;
  37. p->num = num;
  38. }
  39. //从头部插入数据
  40. void SLAddHand(SL* p, Type x)
  41. {
  42. if (p->size >= p->num)
  43. {
  44. SLExps(p);
  45. }
  46. for (int i = p->size; i > 0; i--)
  47. {
  48. p->arr[i] = p->arr[i - 1];
  49. }
  50. p->arr[0] = x;
  51. p->size++;
  52. }
  53. //从头部删除数据
  54. void SLDelHand(SL* p)
  55. {
  56. for (int i = 0; i < p->size - 1; i++)
  57. {
  58. p->arr[i] = p->arr[i + 1];
  59. }
  60. p->size--;
  61. }
  62. //从尾部插入数据
  63. void SLAddEnd(SL* p, Type x)
  64. {
  65. if (p->size >= p->num)
  66. {
  67. SLExps(p);
  68. }
  69. p->arr[p->size] = x;
  70. p->size++;
  71. }
  72. //从尾部删除数据
  73. void SLDelEnd(SL* p)
  74. {
  75. p->size--;
  76. }
  77. //从任意位置插入数据
  78. void SLAddeve(SL* p, Type x, int t)
  79. {
  80. if (p->size >= p->num)
  81. {
  82. SLExps(p);
  83. }
  84. for (int i = p->size; i > t; i--)
  85. {
  86. p->arr[i] = p->arr[i - 1];
  87. }
  88. p->arr[t] = x;
  89. p->size++;
  90. }
  91. //从任意位置删除数据
  92. void SLDeleve(SL* p, int t)
  93. {
  94. for (int i = t; i < p->size - 1; i++)
  95. {
  96. p->arr[i] = p->arr[i + 1];
  97. }
  98. p->size--;
  99. }
  100. //查找数据
  101. void SLFind(SL* p, Type f)
  102. {
  103. for (int i = 0; i < p->size; i++)
  104. {
  105. if (p->arr[i] == f)
  106. {
  107. printf("查找的数据下标为:%d\n", i);
  108. return;
  109. }
  110. }
  111. printf("所查找的数据不存在\n");
  112. }

测试代码(test.c)

  1. #include"SeqList.h"
  2. void Test()
  3. {
  4. SL s;
  5. //初始化
  6. SLInit(&s);
  7. //SLPrint(&s);//打印
  8. //扩容
  9. //SLExps(&s);
  10. //SLPrint(&s);//打印
  11. 头插
  12. //SLAddHand(&s, 520);
  13. //SLAddHand(&s, 1314);
  14. //SLPrint(&s);//打印
  15. 头删
  16. //SLDelHand(&s);
  17. //SLPrint(&s);
  18. 尾插
  19. //SLAddEnd(&s, 1314);
  20. //SLAddEnd(&s, 520);
  21. //SLPrint(&s);
  22. 尾删
  23. //SLDelEnd(&s);
  24. //SLPrint(&s);
  25. //头插
  26. SLAddHand(&s, 1);
  27. SLAddHand(&s, 2);
  28. SLAddHand(&s, 3);
  29. SLAddHand(&s, 4);
  30. //4 3 2 1
  31. //任意位置插入
  32. SLAddeve(&s, 9, 2);
  33. //4 3 9 2 1
  34. SLPrint(&s);
  35. //任意位置删除
  36. SLDeleve(&s, 2);
  37. SLPrint(&s);
  38. //4 3 2 1
  39. SLFind(&s, 9);
  40. SLFind(&s, 2);
  41. //销毁
  42. SLDesTroy(&s);
  43. }
  44. int main()
  45. {
  46. Test();
  47. return 0;
  48. }

制作不易,感到有帮助的可以一键三连支持一下,如果有错误的地方,也请指出!!!

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

闽ICP备14008679号