当前位置:   article > 正文

【数据结构从菜鸟到大牛】 顺序表的实现_顺序表采用数组存储有什么特别要求

顺序表采用数组存储有什么特别要求

目录

1.顺序表的概念以及结构

1.1静态顺序表:​

1.2动态顺序表

 2.接口实现

2.1初始化顺序表

2.2打印顺序表

2.3尾插

2.3.1检查扩容

2.4尾删

2.5 头插

2.6头删

 2.7查找

2.8指定位置插入数据

2.9指定位置删除 

 2.10修改

2.11销毁

源码

SeqList.h

SeqList.c


1.顺序表的概念以及结构

        顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构

        一般情况下采用数组存储。在数组上完成数据的增删查改

顺序表要求 数据必须从第一个位置开始连续存储的-(这里和数组是不同的) 

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

1.1静态顺序表:

这种静态顺序表的大小是写死的,存在很明显的缺陷:

        N不知道要定义多大,多了就会浪费,少了就不够

所以一般采用动态的顺序表

1.2动态顺序表

但是还存在一个问题,如果存满了怎么办?(即size==capacity)

那么这时候就需要考虑扩容

而扩容是存在系统资源消耗的,如果一次只扩一个空间,那么需要频繁的向操作系统发送扩容请求,而多扩一些可能又会用不完而导致浪费,所以综合解决方案就是一次扩容两倍

这里扩容要用到realloc,顺便复习一下:

       1. 如果在所扩容的空间后面找到足够的空间,那么就原地扩容

        2.如果在所扩容的空间后面找不到足够的空间,那么他就在内存中另找一块足够大的空间进行扩容,然后把新的地址传给原来的空间,

 2.接口实现

接口以动态顺序表为例

首先我们要有的接口为        

1.初始化顺序表

2.尾插,尾删

3.头插,头删

4.查找,修改,删除

5.销毁,打印

2.1初始化顺序表

由于 顺序表实际上就是数组,初始化我们只需要把顺序表的最大容量capacity设置为0,

当前存放元素个数size 设置为0,然后把a指针置为空(目前还没malloc空间)

  1. //初始化
  2. void SeqListInit(SeqList* ps)
  3. {
  4. assert(ps);
  5. ps->size = ps->capacity = 0;
  6. ps->a = NULL;
  7. }

2.2打印顺序表

打印顺序表思路十分明确,只需要利用变量i 遍历整个顺序表,printf每一个位置的数据就可以了

  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. printf("\n");
  11. }

2.3尾插

尾插我们首先要检查一下是否需要扩容,如果不需要扩容,那么直接把要插入的数据放到size处

(因为数组下标从0开始 所以size处就是目前需要放入的位置)然后size++就可以了

那么怎么检查是否需要扩容呢?

这里可以想一下,只要 是插入数据是不是都需要用到扩容

所以我们干脆直接把检查扩容这一功能封装成一个函数

2.3.1检查扩容

这里还会有一个问题,我们想一次扩容两倍,但是如果这是第一次怎么办?

第一次的容量capacit为0 二倍仍然是0

所以我们利用一个三目运算符,如果capacity为0 就扩容4个位置,否则扩容两倍

具体的看下面代码

  1. //检查扩容
  2. void CheckCapacity(SeqList* ps)
  3. {
  4. assert(ps);
  5. int newcapacity = 0;
  6. if (ps->size == ps->capacity)
  7. {
  8. /*三目运算符*/
  9. newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
  10. SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * newcapacity);
  11. //如果开辟不成功
  12. if (tmp == NULL)
  13. {
  14. perror("realloc");
  15. exit(-1);//扩容不成功直接退出
  16. }
  17. ps->a = tmp;
  18. ps->capacity = newcapacity;
  19. }
  20. }

接下来尾插就很简单了

  1. //尾插
  2. void SeqListPushBack(SeqList* ps, SLDataType x)
  3. {
  4. assert(ps);
  5. //检查是否需要扩容
  6. CheckCapacity(ps);
  7. ps->a[ps->size] = x;
  8. ps->size++;
  9. }

2.4尾删

 尾删,注意我们其实并不是真正意义上的删除,利用数组知识就可以知道,我们只需要让他访问不到最后一个元素不就相当于删除了吗? 所以我们只需要让size--,不就相当于把最后一个元素删除了嘛?

但是 注意!如果你顺序表已经为空了,就不能再删除了,所以需要先检查一个 是不是顺序表已经为空了

  1. void SeqListPopBack(SeqList* ps)
  2. {
  3. assert(ps);
  4. //判断是不是空
  5. assert(ps->size > 0);
  6. //如果不是空
  7. ps->size--;
  8. }

这里我使用暴力检查 assert ,断言size大于0

当然也可以使用 if语句 ---
 

  1. void SeqListPopBack(SeqList* ps)
  2. {
  3. assert(ps);
  4. //判断是不是空
  5. if(ps->size==0
  6. {
  7.         printf("顺序表已空,无法删除\n");
  8.    return;
  9. }
  10. //如果不是空
  11. ps->size--;
  12. }

2.5 头插

头插需要在顺序表的首元素前面插入一个数据,而顺序表是数组,如果想在顺序表最前面插入数据,必须要做的一件事就是把数组所有的元素整体向后挪动,把前面空出一个位置,把要插入的数据放进去就可以了

显然 头插的时间复杂度为O(N)

下面就是代码:

使用一个end变量标识数组的最后一个元素

因为如果从第一个元素开始向后挪动,一定会把后面的元素给覆盖,出错!

所以从最后一个元素开始向后挪动,当end每次挪动向前走,直到end走到0 也就是第一个元素再结束

  1. //头插
  2. void SeqListPuchFront(SeqList* ps, SLDataType x)
  3. {
  4. assert(ps);
  5. CheckCapacity(ps);
  6. int end = ps->size - 1;
  7. while (end >= 0)
  8. {
  9. ps->a[end + 1] = ps->a[end];
  10. --end;
  11. }
  12. ps->a[0] = x;
  13. ps->size++;
  14. }

2.6头删

 首先,头删就需要检查一下顺序表是不是空

然后头删显然需要把第一个位置的元素删除,仍然需要挪动数据,把后面的元素依次向前挪动,只要把第一个位置的元素覆盖,就相当于删除了

注意:头删需要从前面开始向后走 来挪动数据,否则也会导致原来的数据被覆盖

  1. //头删
  2. void SeqListPopFront(SeqList* ps)
  3. {
  4. assert(ps);
  5. assert(ps->size > 0);
  6. //从前向后挪动(覆盖)
  7. int begin = 0;
  8. while (begin < ps->size-1)
  9. {
  10. ps->a[begin] = ps->a[begin + 1];
  11. ++begin;
  12. }
  13. ps->size--;
  14. }

 2.7查找

查找十分简单,只需要利用一个变量遍历顺序表,找到目标元素,返回下标,否则返回-1

之所以这样设计,是因为这样方便之后利用返回值配合修改函数和删除指定位置元素进行操作

数组下标不会为-1,所以-1就代表了找不到

  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 (ps->a[i] == x)
  9. return i;
  10. }
  11. //找不到返回-1
  12. return -1;
  13. }

2.8指定位置插入数据

 既然是指定位置,那么就需要先找到那个数据的位置,这就用到了查找函数(函数复用)

注意,需要对查找函数的返回值进行检查 

注意 这里pos是<=size (因为考虑到如果是尾插)

找到位置之后,需要把从这个位置开始向后到最后的所有元素整体向后挪动一个位置,把指定位置处空出来,然后把数据放入就可以了

所以需要从最后一个位置开始向后挪动(防止覆盖)

最后别忘了size++(毕竟插入了数据)

(其实指定位置插入数据 可以复用到尾插和头插里面,是完全没有问题的)

  1. //指定位置插入数据
  2. void SeqListInsert(SeqList* ps, int pos, SLDataType x)
  3. {
  4. assert(ps);
  5. if (pos == -1)
  6. {
  7. printf("找不到\n");
  8. return;
  9. }
  10. assert(pos >= 0 && pos <= ps->size);
  11. //从pos位置向后移动
  12. int end = ps->size-1;
  13. while (end>=pos)
  14. {
  15. ps->a[end + 1] = ps->a[end];
  16. --end;
  17. }
  18. ps->a[pos] = x;
  19. ps->size++;
  20. }

2.9指定位置删除 

 首先需要用查找函数找到目标元素的位置(下标)

然后 把该位置之后的数据整体向前挪动一个位置,把指定位置覆盖就可以了

这里利用begin(因为要从前面开始向前挪动)

别忘了对pos检查

  1. //指定位置删除
  2. void SeqListErase(SeqList* ps, int pos)
  3. {
  4. assert(ps);
  5. assert(pos >= 0 && pos < ps->size);
  6. //往前移动
  7. int begin = pos;
  8. while (begin < ps->size-1)
  9. {
  10. ps->a[begin] = ps->a[begin + 1];
  11. ++begin;
  12. }
  13. ps->size--;
  14. }

 2.10修改

首先用查找函数找到下标,有了下标直接访问数组该位置元素进行修改就可以了

(检查pos的有效性)

  1. //修改
  2. void SeqListModify(SeqList* ps, int pos, SLDataType x)
  3. {
  4. assert(ps);
  5. assert(pos >= 0 && pos < ps->size);
  6. ps->a[pos] = x;
  7. }

2.11销毁

销毁就更简单了:

1.把size和capacity置空

2.把malloc的空间释放

  1. void SeqListDestory(SeqList* ps)
  2. {
  3. assert(ps);
  4. ps->size = ps->capacity = 0;
  5. free(ps->a);
  6. ps->a = NULL;
  7. }

源码

SeqList.h

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #pragma warning(disable:6031)
  3. #pragma once
  4. #include<stdio.h>
  5. #include<stdlib.h>
  6. #include<assert.h>
  7. #define SLDataType int
  8. typedef struct SeqList {
  9. SLDataType* a;//指向动态开辟的数组
  10. int size;//数据的个数
  11. int capacity;//容量
  12. }SeqList;
  13. //顺序表初始化
  14. void SeqListInit(SeqList* ps);
  15. //打印
  16. void SeqListPrint(SeqList* ps);
  17. //尾插
  18. void SeqListPushBack(SeqList* ps,SLDataType x);
  19. //尾删
  20. void SeqListPopBack(SeqList* ps);
  21. //头插
  22. void SeqListPuchFront(SeqList* ps, SLDataType x);
  23. //头删
  24. void SeqListPopFront(SeqList* ps);
  25. //查找
  26. int SeqListFind(SeqList* ps, SLDataType x);
  27. //指定位置插入
  28. void SeqListInsert(SeqList* ps, int pos, SLDataType x);
  29. //删除pos位置的元素
  30. void SeqListErase(SeqList* ps, int pos);
  31. //修改
  32. void SeqListModify(SeqList* ps, int pos, SLDataType x);
  33. //销毁
  34. void SeqListDestory(SeqList* ps);

SeqList.c

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #pragma warning(disable:6031)
  3. #include"SeqList.h"
  4. //打印
  5. void SeqListPrint(SeqList* ps)
  6. {
  7. assert(ps);
  8. int i = 0;
  9. for (i = 0; i < ps->size; i++)
  10. {
  11. printf("%d ", ps->a[i]);
  12. }
  13. printf("\n");
  14. }
  15. //初始化
  16. void SeqListInit(SeqList* ps)
  17. {
  18. assert(ps);
  19. ps->size = ps->capacity = 0;
  20. ps->a = NULL;
  21. }
  22. //检查扩容
  23. void CheckCapacity(SeqList* ps)
  24. {
  25. assert(ps);
  26. int newcapacity = 0;
  27. if (ps->size == ps->capacity)
  28. {
  29. newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
  30. SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * newcapacity);
  31. //如果开辟不成功
  32. if (tmp == NULL)
  33. {
  34. perror("realloc");
  35. exit(-1);
  36. }
  37. ps->a = tmp;
  38. ps->capacity = newcapacity;
  39. }
  40. }
  41. //尾插
  42. void SeqListPushBack(SeqList* ps, SLDataType x)
  43. {
  44. assert(ps);
  45. //检查是否需要扩容
  46. CheckCapacity(ps);
  47. ps->a[ps->size] = x;
  48. ps->size++;
  49. }
  50. //尾删
  51. void SeqListPopBack(SeqList* ps)
  52. {
  53. assert(ps);
  54. //判断是不是空
  55. assert(ps->size > 0);
  56. //如果不是空
  57. ps->size--;
  58. }
  59. //头插
  60. void SeqListPuchFront(SeqList* ps, SLDataType x)
  61. {
  62. assert(ps);
  63. CheckCapacity(ps);
  64. int end = ps->size - 1;
  65. while (end >= 0)
  66. {
  67. ps->a[end + 1] = ps->a[end];
  68. --end;
  69. }
  70. ps->a[0] = x;
  71. ps->size++;
  72. }
  73. //头删
  74. void SeqListPopFront(SeqList* ps)
  75. {
  76. assert(ps);
  77. assert(ps->size > 0);
  78. //从前向后挪动(覆盖)
  79. int begin = 0;
  80. while (begin < ps->size-1)
  81. {
  82. ps->a[begin] = ps->a[begin + 1];
  83. ++begin;
  84. }
  85. ps->size--;
  86. }
  87. //查找
  88. int SeqListFind(SeqList* ps, SLDataType x)
  89. {
  90. assert(ps);
  91. int i = 0;
  92. for (i = 0; i < ps->size; i++)
  93. {
  94. if (ps->a[i] == x)
  95. return i;
  96. }
  97. //找不到返回-1
  98. return -1;
  99. }
  100. //指定位置插入数据
  101. void SeqListInsert(SeqList* ps, int pos, SLDataType x)
  102. {
  103. assert(ps);
  104. if (pos == -1)
  105. {
  106. printf("找不到\n");
  107. return;
  108. }
  109. assert(pos >= 0 && pos <= ps->size);
  110. //从pos位置向后移动
  111. int end = ps->size-1;
  112. while (end>=pos)
  113. {
  114. ps->a[end + 1] = ps->a[end];
  115. --end;
  116. }
  117. ps->a[pos] = x;
  118. ps->size++;
  119. }
  120. //指定位置删除
  121. void SeqListErase(SeqList* ps, int pos)
  122. {
  123. assert(ps);
  124. assert(pos >= 0 && pos < ps->size);
  125. //往前移动
  126. int begin = pos;
  127. while (begin < ps->size-1)
  128. {
  129. ps->a[begin] = ps->a[begin + 1];
  130. ++begin;
  131. }
  132. ps->size--;
  133. }
  134. //修改
  135. void SeqListModify(SeqList* ps, int pos, SLDataType x)
  136. {
  137. assert(ps);
  138. assert(pos >= 0 && pos < ps->size);
  139. ps->a[pos] = x;
  140. }
  141. //销毁
  142. void SeqListDestory(SeqList* ps)
  143. {
  144. assert(ps);
  145. ps->size = ps->capacity = 0;
  146. free(ps->a);
  147. ps->a = NULL;
  148. }

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

闽ICP备14008679号