当前位置:   article > 正文

《数据结构与算法》顺序表专题

《数据结构与算法》顺序表专题

必备基础知识:结构体、指针(一级指针、二级指针、指针传参)、结构体指针、动态内存管理

数据结构的相关概念

1 什么是数据结构

概念:数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在⼀种或多种特定关系
的数据元素的集合。数据结构反映数据的内部构成,即数据由那部分构成,以什么⽅式构成,以及数据元素之间呈现的结构。

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

eg:有两个牧场,A牧场是放养的羊,B牧场是每只羊都有各自的号码的羊,如果我们想找到叫“肖恩”的羊,在A牧场就无法快速的找到,而B牧场只需要找到“肖恩”的号码即可快速找到它。

顺序表

1 顺序表的概念及结构

1.1线性表

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

1.2 顺序表的分类

  ·  顺序表和数组的区别

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

       举个例子

9afeb0d73f4e4eb89fab1b64eb5e38d9.png

   ·  顺序表分类

    ·  静态顺序表

  1. #define N 100
  2. typedef int SLDataType//等效替换int,可以实现一键替换想要的数据类型
  3. struct SeqList
  4. {
  5. SLDataType a[N];//数组的长度即空间的大小
  6. int size;//有效数据个数
  7. };

       静态顺序表的缺陷:给定的数组长度,若不够就会导致后续数据的保存失败(导致数据丢失,引发严重技术事故),但如果给多了就会导致空间的大量浪费。

    ·  动态顺序表(可增容)

  1. typedef int SLDataType;
  2. typedef struct SeqList
  3. {
  4. SLDataType* arr;//存储数据的底层结构
  5. int capacity;//记录顺序表的空间大小
  6. int size;//记录顺序表当前有效的数据个数
  7. }SL;
  8. //typedef struct SeqList SL; //与以上代码相同的去定义SeqList结构体变量SL

 

2 动态顺序表的实现

SeqList.h  定义顺序表的结构,顺序表要实现的接口/方法

SeqList.c  具体实现顺序表里定义的接口/方法

test.c  测试动作:测试顺序表

2.1 初始化

错误示范

SeqList.h

void SLInit(SL s1);

 SeqList.c

  1. #include "SeqList.h"
  2. void SLInit(SL* ps)
  3. {
  4. s.arr = NULL;
  5. s.size = s.capacity = 0;
  6. }

test.c

  1. #include "SeqList.h"
  2. void slTest01()
  3. {
  4. SL s1;
  5. SLInit(s1);
  6. }
  7. int main()
  8. {
  9. s1Test01();
  10. return 0;
  11. }

 错误:这里传的不是地址而是传的sl的值过去给s,形参只是实参的值的零食拷贝,而并不是sl本身,这样程序在运行时就会报错“使用了未初始化的局部变量sl”。

正确示范

SeqList.h

  1. //初始化
  2. void SLInit(SL* ps);

SeqList.c

  1. #include "SeqList.h"
  2. void SLInit(SL* ps)
  3. {
  4. ps->arr = NULL;
  5. ps->size = ps->capacity = 0;
  6. }

test.c

  1. #include "SeqList.h"
  2. void slTest01()
  3. {
  4. SL s1;
  5. SLInit(&s1);
  6. }
  7. int main()
  8. {
  9. s1Test01();
  10. return 0;
  11. }

2.2 扩容

SeqList.h

void SLCheckCapacity(SL* ps);

SeqList.c

  1. void SLCheckCapacity(SL* ps)
  2. {
  3. if(ps=>size == ps->capacity)
  4. {
  5. int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
  6. //判断capacity是否为零,因为初始化时capacity为零,如果不为零则扩容两倍
  7. SLDataType* tmp = (SLDataType*)realloc(ps=>arr, newCapacity * sizeof(SLDataType));
  8. //注意newCapacity的单位,要乘上数据类型的大小
  9. //设一个零时变量tmp是为了防止realloc开辟空间失败导致地址丢失
  10. if(tmp == NULL)
  11. {
  12. perror("realloc fail!");
  13. exit(1);
  14. }
  15. //扩容成功
  16. ps->arr = tmp;
  17. ps->capacity = newCapacity;
  18. }
  19. }

2.3 打印

SeqList.h

void SLPrint(SL* ps);  //这里可以传值也可以传地址,这里传地址是为了保持接口一致性

SeqList.c

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

2.4 尾插

插入数据时很可能出现两种情况

  • 空间不够 -- 扩容再插入
  • 空间足够 -- 直接插入

SeqList.h

void SLPushBack(SL* ps, SLDataType x);

SeqList.c

  1. void SLPushBack(SL* ps, SLDataType x)
  2. {
  3. //为了防止传的地址为NULL
  4. //assert--粗暴的解决方式,如果等于NULL或者判断为假就会报错,程序无法继续运行下去
  5. assert(ps);
  6. //assert(ps != NULL);//等同于上一行
  7. //if判断--温柔的解决方式,如果等于NULL就就直接跳过而不插入
  8. //if(ps == NULL)
  9. //{
  10. // return;
  11. //}
  12. //空间不够,扩容
  13. SLCheckCapacity(ps);
  14. //空间足够,直接插入
  15. ps->arr[ps->size++] = x;
  16. }

test.c

  1. //测试尾插
  2. SLPushBack(&sl, 1);
  3. SLPushBack(&sl, 2);
  4. SLPushBack(&sl, 3);
  5. SLPushBack(&sl, 4);
  6. SLPrint(&sl);
  7. SLPushBack(&sl, 5);
  8. SLPrint(&sl);

输出结果为

  1. 1 2 3 4
  2. 1 2 3 4 5

2.5 头插

SeqList.h

void SLPushFront(SL* ps, SLDataType x);

SeqList.c

  1. void SLPushFront(SL* ps, SLDataType x)
  2. {
  3. assert(ps);
  4. //判断是否扩容
  5. SLCheckCapacity(ps);
  6. //旧的数据往后挪一位
  7. for(int i = ps->size; i > 0; i--) //i最后值为1
  8. {
  9. ps->arr[i] = ps->arr[i-1]; //最后一个循环为 ps->arr[1] = ps->arr[0]
  10. }
  11. ps->arr[0] = x;
  12. ps->size++;
  13. }

test.c

  1. //测试头插
  2. SLPushBack(&sl, 1);
  3. SLPushBack(&sl, 2);
  4. SLPushBack(&sl, 3);
  5. SLPushBack(&sl, 4);
  6. SLPrint(&sl);
  7. SLPuchFront(&sl, 5);
  8. SLPuchFront(&sl, 6);
  9. SLPuchFront(&sl, 7);
  10. SLPrint(&sl);

输出结果为

  1. 1 2 3 4
  2. 7 6 5 1 2 3 4

2.6 尾删

SeqList.h

void SLPopBack(SL* ps);

SeqList.c

  1. void SLPopBack(SL* ps)
  2. {
  3. assert(ps);
  4. assert(ps->size); //判断顺序表是否为空,为空不能进行删除操作
  5. //顺序表不为空
  6. ps->size--; //这里属于是看不见就等于没有,这并不影响数据的查找,修改,插入
  7. }

test.c

  1. //测试尾删
  2. SLPushBack(&sl, 1);
  3. SLPushBack(&sl, 2);
  4. SLPushBack(&sl, 3);
  5. SLPushBack(&sl, 4);
  6. SLPrint(&sl);
  7. SLPopBack(&sl);
  8. SLPopBack(&sl);
  9. SLPrint(&sl);

输出结过为

  1. 1 2 3 4
  2. 1 2

2.7 头删

SeqList.h

void SLPopFront(SL* ps);

SeqList.c

  1. void SLPopFront(SL* ps)
  2. {
  3. assert(ps);
  4. assert(ps->size);
  5. //不为空执行挪动操作
  6. for(int i = 0; i < size-1; i++) //i最后的值为size-2
  7. {
  8. ps->arr[i] = ps->arr[i+1]; //最后一次循环为 ps->arr[size-2] = ps->arr[size-1]
  9. }
  10. ps->size--;
  11. }

 test.c

  1. //测试头删
  2. SLPushBack(&sl, 1);
  3. SLPushBack(&sl, 2);
  4. SLPushBack(&sl, 3);
  5. SLPushBack(&sl, 4);
  6. SLPrint(&sl);
  7. SLPopFront(&sl);
  8. SLPopFront(&sl);
  9. SLPrint(&sl);

 输出结果为

  1. 1 2 3 4
  2. 3 4

2.8 指定位置插入数据

SeqList.h

void SLInsert(SL* ps, int pos, SLDataType x);

SeqList.c

  1. void SLInsert(SL* ps, int pos, SLDataType x)
  2. {
  3. assert(ps);
  4. assert(pos >= 0 && pos <= ps->size); //限制pos的大小,否则会出现插入失败的情况
  5. SLCheckCapacity(ps);
  6. //pos及之后的数据往后挪动一位,把pos空出来
  7. for(int i = ps->size; i > pos; i--)
  8. {
  9. ps->arr[i] = ps->arr[i-1]; //最后一步ps->arr[pos+1] = ps->arr[pos]
  10. }
  11. ps->arr[pos] = x;
  12. ps->size++;
  13. }

test.c

  1. //测试指定位置插入
  2. SLPushBack(&sl, 1);
  3. SLPushBack(&sl, 2);
  4. SLPushBack(&sl, 3);
  5. SLPushBack(&sl, 4);
  6. SLPrint(&sl);
  7. SLInsert(&sl, 0, 100);
  8. SLPrint(&sl);
  9. SLInsert(&sl, 0, 100);
  10. SLPrint(&sl);
  11. //SLInsert(&sl, 0, 100); //如果代码中没有assert来检查pos的值,这里就会插入失败,插入的数据异常
  12. //SLPrint(&sl);

输出结果为

  1. 1 2 3 4
  2. 100 1 2 3 4
  3. 100 1 2 3 4 200

2.9 删除指定位置数据

SeqList.h

void SLErase(SL* ps, int pos);

SeqList.c

  1. void SLErase(SL* ps, int pos)
  2. {
  3. assert(ps);
  4. assert(pos >= 0 && ps < ps->size);
  5. //pos以后的数据往前挪一位
  6. for(int i = pos; i < ps->size-1; i++)
  7. {
  8. ps->arr[i] = ps->arr[i+1]; //最后一步ps->arr[i-2] = ps->arr[i-1]
  9. }
  10. ps->size--;
  11. }

test.c

  1. //测试指定位置删除
  2. SLPushBack(&sl, 1);
  3. SLPushBack(&sl, 2);
  4. SLPushBack(&sl, 3);
  5. SLPushBack(&sl, 4);
  6. SLPrint(&sl);
  7. SLErase(&sl, 0);
  8. SLPrint(&sl);
  9. SLErase(&sl, sl.size - 1);
  10. SLPrint(&sl);

输出结果为

  1. 1 2 3 4
  2. 2 3 4
  3. 2 3

 2.10 查找数据

SeqList.h

int SLFind(SL* ps, SLDataType x);

 SeqList.c

  1. int SLFind(SL* ps, SLDataType x)
  2. {
  3. assert(ps); //加上断言对代码的健壮性更好
  4. for(int i = 0; i < ps->size; i++)
  5. {
  6. if(ps->arr[i] == x)
  7. {
  8. return i;
  9. }
  10. }
  11. return -1;
  12. }

test.c

 

  1. //测试查找
  2. SLPushBack(&sl, 1);
  3. SLPushBack(&sl, 2);
  4. SLPushBack(&sl, 3);
  5. SLPushBack(&sl, 4);
  6. SLPrint(&sl);
  7. int ret = SLFind(&Sl, 3);
  8. if(ret < 0)
  9. {
  10. printf("数据不存在,查找失败!!");
  11. }
  12. else
  13. {
  14. printf("数据找到了,在下标为%d的位置\n", ret);
  15. }
  16. //int ret = SLFind(&Sl, 6);
  17. //if(ret < 0)
  18. //{
  19. // printf("数据不存在,查找失败!!“);
  20. //}
  21. //else
  22. //{
  23. // printf("数据找到了,在下标为%d的位置!\n", ret);
  24. //}

输出结果为

  1. 1 2 3 4
  2. 数据找到了,在下标为2的位置
  3. //数据不存在,查找失败!!

 2.11 销毁

SeqList.h

void SLDestroy(SL* ps);

 SeqLis.c

  1. void SLDestroy(SL* ps)
  2. {
  3. assert(ps);
  4. if(ps->arr) //如果arr本身就是空的,我们就没必要销毁了
  5. {
  6. free(ps->arr);
  7. }
  8. ps->arr = NULL;
  9. ps->size = ps->capacity = 0;
  10. }

3 整理顺序表代码

SeqList.h

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<assert.h>
  4. typedef int SLDataType//等效替换int,可以实现一键替换想要的数据类型
  5. typedef struct SeqList
  6. {
  7. SLDataType* arr;//存储数据的底层结构
  8. int capacity;//记录顺序表的空间大小
  9. int size;//记录顺序表当前有效的数据个数
  10. }SL;
  11. //typedef struct SeqList SL; //与以上代码相同的去定义SeqList结构体变量SL
  12. //初始化
  13. void SLInit(SL s1);
  14. //扩容
  15. void SLCheckCapacity(SL* ps);
  16. //打印
  17. void SLPrint(SL* ps);
  18. //尾插
  19. void SLPushBack(SL* ps, SLDataType x);
  20. //头插
  21. void SLPushFront(SL* ps, SLDataType x);
  22. //尾删
  23. void SLPopBack(SL* ps);
  24. //头删
  25. void SLPopFront(SL* ps);
  26. //指定位置插入数据
  27. void SLInsert(SL* ps, int pos, SLDataType x);
  28. //删除指定位置数据
  29. void SLErase(SL* ps, int pos);
  30. //查找数据
  31. int SLFind(SL* ps, SLDataType x);
  32. //销毁
  33. void SLDestroy(SL* ps);

SeqList.c

  1. #include "SeqList.h"
  2. //初始化
  3. void SLInit(SL* ps)
  4. {
  5. ps->arr = NULL;
  6. ps->size = ps->capacity = 0;
  7. }
  8. //扩容
  9. void SLCheckCapacity(SL* ps)
  10. {
  11. if(ps->size == ps->capacity)
  12. {
  13. int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
  14. //判断capacity是否为零,因为初始化时capacity为零,如果不为零则扩容两倍
  15. SLDataType* tmp = (SLDataType*)realloc(ps=>arr, newCapacity * sizeof(SLDataType));
  16. //注意newCapacity的单位,要乘上数据类型的大小
  17. //设一个零时变量tmp是为了防止realloc开辟空间失败导致地址丢失
  18. if(tmp == NULL)
  19. {
  20. perror("realloc fail!");
  21. exit(1);
  22. }
  23. //扩容成功
  24. ps->arr = tmp;
  25. ps->capacity = newCapacity;
  26. }
  27. }
  28. //打印
  29. void SLPrint(SL* ps)
  30. {
  31. for(int i = 0; i < ps->size; i++)
  32. {
  33. printf("%d", ps->arr[i]);
  34. }
  35. printf("\n");
  36. }
  37. //尾插
  38. void SLPushBack(SL* ps, SLDataType x)
  39. {
  40. //为了防止传的地址为NULL
  41. //assert--粗暴的解决方式,如果等于NULL或者判断为假就会报错,程序无法继续运行下去
  42. assert(ps);
  43. //assert(ps != NULL);//等同于上一行
  44. //if判断--温柔的解决方式,如果等于NULL就就直接跳过而不插入
  45. //if(ps == NULL)
  46. //{
  47. // return;
  48. //}
  49. //空间不够,扩容
  50. SLCheckCapacity(ps);
  51. //空间足够,直接插入
  52. ps->arr[ps->size++] = x;
  53. }
  54. //头插
  55. void SLPushFront(SL* ps, SLDataType x)
  56. {
  57. assert(ps);
  58. //判断是否扩容
  59. SLCheckCapacity(ps);
  60. //旧的数据往后挪一位
  61. for(int i = ps->size; i > 0; i--) //i最后值为1
  62. {
  63. ps->arr[i] = ps->arr[i-1]; //最后一个循环为 ps->arr[1] = ps->arr[0]
  64. }
  65. ps->arr[0] = x;
  66. ps->size++;
  67. }
  68. //尾删
  69. void SLPopBack(SL* ps)
  70. {
  71. assert(ps);
  72. assert(ps->size); //判断顺序表是否为空,为空不能进行删除操作
  73. //顺序表不为空
  74. ps->size--; //这里属于是看不见就等于没有,这并不影响数据的查找,修改,插入
  75. }
  76. //头删
  77. void SLPopFront(SL* ps)
  78. {
  79. assert(ps);
  80. assert(ps->size);
  81. //不为空执行挪动操作
  82. for(int i = 0; i < size-1; i++) //i最后的值为size-2
  83. {
  84. ps->arr[i] = ps->arr[i+1]; //最后一次循环为 ps->arr[size-2] = ps->arr[size-1]
  85. }
  86. ps->size--;
  87. }
  88. //指定位置插入数据
  89. void SLInsert(SL* ps, int pos, SLDataType x)
  90. {
  91. assert(ps);
  92. assert(pos >= 0 && pos <= ps->size); //限制pos的大小,否则会出现插入失败的情况
  93. SLCheckCapacity(ps);
  94. //pos及之后的数据往后挪动一位,把pos空出来
  95. for(int i = ps->size; i > pos; i--)
  96. {
  97. ps->arr[i] = ps->arr[i-1]; //最后一步ps->arr[pos+1] = ps->arr[pos]
  98. }
  99. ps->arr[pos] = x;
  100. ps->size++;
  101. }
  102. //删除指定位置数据
  103. void SLErase(SL* ps, int pos)
  104. {
  105. assert(ps);
  106. assert(pos >= 0 && ps < ps->size);
  107. //pos以后的数据往前挪一位
  108. for(int i = pos; i < ps->size-1; i++)
  109. {
  110. ps->arr[i] = ps->arr[i+1]; //最后一步ps->arr[i-2] = ps->arr[i-1]
  111. }
  112. ps->size--;
  113. }
  114. //查找数据
  115. int SLFind(SL* ps, SLDataType x)
  116. {
  117. assert(ps); //加上断言对代码的健壮性更好
  118. for(int i = 0; i < ps->size; i++)
  119. {
  120. if(ps->arr[i] == x)
  121. {
  122. return i;
  123. }
  124. }
  125. return -1;
  126. }
  127. //销毁
  128. void SLDestroy(SL* ps)
  129. {
  130. assert(ps);
  131. if(ps->arr) //如果arr本身就是空的,我们就没必要销毁了
  132. {
  133. free(ps->arr);
  134. }
  135. ps->arr = NULL;
  136. ps->size = ps->capacity = 0;
  137. }

 

 

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

闽ICP备14008679号