• 数据结构·顺序表


    目录

    线性表

    概念及结构

    顺序表

    静态顺序表

    动态顺序表

    接口实现 

    SeqList.h

    SeqList.c

     Test.c

    结束语


    线性表

    线性表( linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
    线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。

    概念及结构

    顺序表是用一段 物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。

    连续存储顺序,是不能跳跃的

    顺序表

    顺序表是用一段 物理地址连续 的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。

    静态顺序表

    使用定长数组存储元素

            静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N 定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实现动态顺序表。

    静态顺序表价值较低 -- 不考虑 

    动态顺序表

     扩容方式 -- 使用 realloc 库函数 -- 有两种扩容方式 原地扩容 与 异地扩容 后者代价大

    扩容选择 -- 两倍

    扩容两倍的原因

    一次扩容多了,存在空间的浪费。一次扩容少了,频繁扩容,效率损失

    接口实现 

    SeqList.h

    1. #pragma once //方式二次引用
    2. #define _CRT_SECURE_NO_DEPRECATE //VS里面的检查 -- 其他编辑器不需要
    3. #include
    4. #include
    5. #include
    6. //或者
    7. //#ifndef __SEQLIST_H__
    8. //#define __SEQLIST_H__
    9. //……
    10. //#endif
    11. //静态的顺序表 -- 缺陷:当不知道要存储多少数据的时候就体现出来了
    12. //#define N 10
    13. //typedef int SLDataType;
    14. //struct Seqlist
    15. //{
    16. // SLDataType a[N];
    17. // int size; //存储数据的个数
    18. //};
    19. //动态顺序表 -- 有两种扩容方式 原地扩容与异地扩容 后者代价大
    20. //重命名类型 -- 方便后续改变存储类型
    21. typedef int SLDataType;
    22. typedef struct Seqlist
    23. {
    24. SLDataType* a;
    25. int size; //存储数据的个数
    26. int capacity;//存储空间的大小
    27. }SL;
    28. //初始化 -- 这里要接收地址,用指针接收,利用地址来与外部产生联系
    29. void SLInit(SL* psl);
    30. //销毁 --
    31. void SLDestory(SL* psl);
    32. //顺序表的打印 -- 方便检查
    33. void SLPrint(SL* psl);
    34. //头插头删 尾插尾删
    35. //尾插
    36. void SLPushBack(SL* psl, SLDataType x);
    37. //头插
    38. void SLPushFront(SL* psl, SLDataType x);
    39. //尾删
    40. void SLPoPBack(SL* psl);
    41. //头删
    42. void SLPoPhFront(SL* psl);
    43. // 顺序表查找 -- 没找到返回-1
    44. int SLFind(SL* psl, SLDataType x);
    45. //顺序表在pos位置插入x
    46. void SLInsert(SL* psl, size_t pos, SLDataType x);
    47. // 顺序表删除pos位置的值
    48. void SLErase(SL* psl, size_t pos);
    49. //修改
    50. void SLModify(SL* psl, size_t pos, SLDataType x);

    SeqList.c

    1. #include "SeqList.h"
    2. //初始化
    3. void SLInit(SL* psl)
    4. {
    5. assert(psl); //自身断言不可以为空 不可以传NULL过来
    6. psl->a = NULL;
    7. psl->size = psl->capacity = 0;
    8. }
    9. //销毁
    10. void SLDestory(SL* psl)
    11. {
    12. assert(psl);
    13. //if (psl->a) //free自己会检查是否为NULL -- free NULL 没意义
    14. //{
    15. free(psl->a);
    16. psl->a = NULL;
    17. psl->size = psl->capacity = 0;
    18. //}
    19. }
    20. //检查并扩容
    21. void SLCheckCapacity(SL* psl)
    22. {
    23. //检查容量
    24. if (psl->size == psl->capacity)
    25. {
    26. int newCapacity = psl->capacity == 0 ? 4 : psl->capacity * 2; //扩容两倍 -- 当为0的时候赋值4字节
    27. SLDataType* tmp = (SLDataType*)realloc(psl->a, newCapacity * sizeof(SLDataType)); //三种情况,原地、异地与失败 -- 为防止失败应使用临时指针
    28. if (tmp == NULL) // 这里开辟空间大小容易出错,必须要记得后面的* sizeof(SLDataType) 不然开辟的空间不够
    29. {
    30. perror("realloc fail");
    31. return;
    32. //exit(-1); //或者这样直接返回,更直接
    33. }
    34. psl->a = tmp;
    35. psl->capacity = newCapacity;
    36. }
    37. }
    38. //打印
    39. void SLPrint(SL* psl)
    40. {
    41. assert(psl);
    42. for (int i = 0; i < psl->size; i++)
    43. {
    44. printf("%d ", psl->a[i]);
    45. }
    46. printf("\n");
    47. }
    48. //尾插
    49. void SLPushBack(SL* psl, SLDataType x)
    50. {
    51. //assert(psl);
    52. //检查并扩容
    53. //SLCheckCapacity(psl);
    54. //psl->a[psl->size] = x;
    55. //psl->size++;
    56. //当SLInsert写好后就不需要这些了,直接调用
    57. SLInsert(psl, psl->size, x);
    58. }
    59. //头插
    60. void SLPushFront(SL* psl, SLDataType x)
    61. {
    62. //assert(psl);
    63. //SLCheckCapacity(psl);
    64. //挪动数据
    65. //int end = psl->size - 1;
    66. //while (end >= 0)
    67. //{
    68. // psl->a[end+1] = psl->a[end];
    69. // --end;
    70. //}
    71. //psl->a[0] = x;
    72. //psl->size++;
    73. //这里也不需要了,直接调用
    74. SLInsert(psl, 0, x); // -- 这就相当与头插了
    75. }
    76. //尾删
    77. void SLPoPBack(SL* psl)
    78. {
    79. //assert(psl);
    80. //比较温柔的检查
    81. //if (psl->size == 0)
    82. //{
    83. // return;
    84. //}
    85. //暴力的检查 -- 会直接报错 在C++中更常用
    86. //assert(psl->size > 0);
    87. //psl->size--; //realloc是不可以释放一部分的空间的所以不要free
    88. // //也不需要清除掉数据,但是没必要,可以直接覆盖
    89. // //这种的情况是由size为基准,由它访问
    90. // //但是有bug:删掉的多了就会出现数据的越界
    91. //写完之后就可以替换掉之前的了
    92. SLErase(psl, psl->size-1);
    93. }
    94. //头删
    95. void SLPoPhFront(SL* psl)
    96. {
    97. //assert(psl);
    98. //assert(psl->size > 0);
    99. //一
    100. //int begin = 0;
    101. //while (begin < psl->size - 1)
    102. //{
    103. // psl->a[begin] = psl->a[begin + 1];
    104. // ++begin;
    105. //}
    106. //--psl->size;
    107. //写完之后就可以替换掉之前的了
    108. SLErase(psl, 0);
    109. }
    110. //缩容:以时间换空间 -- 不常用
    111. //不缩容:以空间换时间 -- 现在的设备比较空间都比较强
    112. //realloc 缩容可能会异地缩容
    113. //顺序表查找 -- 没找到返回-1
    114. //这里直接遍历数组就可以了,在数据少的情况下这样反而更快 O(N) 级别
    115. int SLFind(SL* psl, SLDataType x)
    116. {
    117. assert(psl);
    118. for (int i = 0; i < psl->size; ++i)
    119. {
    120. if (psl->a[i] == x)
    121. {
    122. return i;
    123. }
    124. }
    125. return -1;
    126. }
    127. //顺序表在pos位置插入x -- 在前面插入
    128. void SLInsert(SL* psl, size_t pos, SLDataType x) //size_t 更符合库函数的设计
    129. {
    130. assert(psl);
    131. assert(pos <= psl->size);
    132. SLCheckCapacity(psl);
    133. //挪动数据 法一: 用这种方法要注意算术转化,容易死循环
    134. //int end = psl->size - 1;
    135. //while (end>=(int)pos)
    136. //{
    137. // psl->a[end] = psl->a[end - 1];
    138. // --end;
    139. //}
    140. //挪动数据 法二:这种方法更好,不需要强制转化
    141. size_t end = psl->size;
    142. while (end > pos)
    143. {
    144. psl->a[end] = psl->a[end - 1];
    145. end--;
    146. }
    147. psl->a[pos] = x;
    148. ++psl->size;
    149. }
    150. // 顺序表删除pos位置的值
    151. void SLErase(SL* psl, size_t pos)
    152. {
    153. assert(psl);
    154. assert(pos < psl->size);
    155. size_t begin = pos;
    156. while (begin < psl->size - 1)
    157. {
    158. psl->a[begin] = psl->a[begin + 1];
    159. ++begin;
    160. }
    161. --psl->size;
    162. }
    163. //修改
    164. void SLModify(SL* psl, size_t pos, SLDataType x)
    165. {
    166. assert(psl);
    167. assert(pos < psl->size);
    168. psl->a[pos] = x;
    169. }

     Test.c

    1. #include "SeqList.h"
    2. //尾插、头插、销毁测试
    3. void TestSeqList1()
    4. {
    5. SL s;
    6. //初始化
    7. SLInit(&s);
    8. //尾插
    9. SLPushBack(&s, 1);
    10. SLPushBack(&s, 2);
    11. SLPushBack(&s, 3);
    12. SLPushBack(&s, 4);
    13. SLPushBack(&s, 5);
    14. SLPushBack(&s, 6);
    15. //打印观察
    16. SLPrint(&s);
    17. //头插
    18. SLPushFront(&s, 10);
    19. SLPushFront(&s, 20);
    20. SLPushFront(&s, 30);
    21. //打印观察
    22. SLPrint(&s);
    23. //销毁
    24. SLDestory(&s);
    25. }
    26. //尾插、尾删、删多了测试
    27. TestSeqList2()
    28. {
    29. //SLInit(NULL); //测试传空指针 -- 到assert会直接报错
    30. SL s;
    31. SLInit(&s);
    32. SLPushBack(&s, 1);
    33. SLPushBack(&s, 2);
    34. SLPushBack(&s, 3);
    35. SLPushBack(&s, 4);
    36. SLPrint(&s);
    37. //尾删
    38. SLPoPBack(&s);
    39. SLPoPBack(&s);
    40. SLPrint(&s);
    41. //再尾删
    42. SLPoPBack(&s);
    43. SLPoPBack(&s);
    44. SLPrint(&s);
    45. //头删
    46. SLPushBack(&s,10);
    47. SLPushBack(&s,20);
    48. SLPushBack(&s,30);
    49. SLPushBack(&s,40);
    50. SLPrint(&s);
    51. SLDestory(&s);
    52. }
    53. //插入测试
    54. TestSeqList3()
    55. {
    56. SL s;
    57. SLInit(&s);
    58. SLInsert(&s, 0, 30);
    59. SLPushBack(&s, 40);
    60. SLPrint(&s);
    61. }
    62. //尾插、删除测试
    63. TestSeqList4()
    64. {
    65. SL s;
    66. SLInit(&s);
    67. SLPushBack(&s, 1);
    68. SLPushBack(&s, 2);
    69. SLPushBack(&s, 3);
    70. SLPushBack(&s, 4);
    71. SLPushBack(&s, 5);
    72. SLPrint(&s);
    73. SLErase(&s, 0);
    74. SLPrint(&s);
    75. int x = 0;
    76. scanf("%d", &x);
    77. int pos = SLFind(&s, x);
    78. if (pos != -1)
    79. {
    80. SLErase(&s, pos);
    81. }
    82. SLPrint(&s);
    83. }
    84. void memu()
    85. {
    86. printf("****************************\n");
    87. printf("1、尾插数据 2、头插数据\n");
    88. printf("3、位置插入 7、打印数据 \n");
    89. printf(" -1、退出 \n");
    90. printf("****************************\n");
    91. }
    92. int main()
    93. {
    94. //TestSeqList1(); //测试的时候尽量单独用一个函数测试
    95. //TestSeqList3();
    96. //TestSeqList4();
    97. SL s;
    98. SLInit(&s);
    99. int option = 0;
    100. int x = 0;
    101. int pos = 0;
    102. do
    103. {
    104. memu();
    105. printf("请输入你的操作:>");
    106. scanf("%d", &option);
    107. switch (option)
    108. {
    109. case 1:
    110. printf("请连续输入你想要插入的数据,以-1结束\n");
    111. scanf("%d", &x);
    112. while (x != -1)
    113. {
    114. SLPushBack(&s,x);
    115. scanf("%d", &x);
    116. }
    117. break;
    118. case 2:
    119. printf("请连续输入你想要插入的数据,以-1结束\n");
    120. scanf("%d", &x);
    121. while (x != -1)
    122. {
    123. SLPushFront(&s, x);
    124. scanf("%d", &x);
    125. }
    126. break;
    127. case 3:
    128. printf("请输入你想要插入的数据\n");
    129. scanf("%d", &x);
    130. printf("请输入你想要插入的位置\n");
    131. scanf("%d", &pos);
    132. SLInsert(&s, pos, x);
    133. break;
    134. case 7:
    135. SLPrint(&s);
    136. break;
    137. default:
    138. break;
    139. }
    140. } while (option != -1);
    141. SLDestory(&s);
    142. return 0;
    143. }

    菜单不完善了,很简单

    我写这篇的时候发现这里不允许使用 来二层注释 

    结束语

    别有幽愁暗恨生,此时无声胜有声。
                                            唐·白居易 《琵琶行》

  • 相关阅读:
    重生奇迹MU套装大全中的极品属性
    【word论文排版-公式】如何实现两栏格式公式居中,编号右对齐
    基于单片机设计的家用自来水水质监测装置
    kafka 使用thrift序列化对象
    项目中的奇葩需求你都怎么应对?
    nginx服务器
    130 行代码搞定核酸统计,程序员在抗疫期间的大能量
    【数据结构基础_字符串】Leetcode 409.最长回文串
    数据结构 之 树
    Packet Tracer的使用介绍
  • 原文地址:https://blog.csdn.net/weixin_67595436/article/details/126032028