飞道的博客

数据结构(C/C++)课程阶段总结(一)

415人阅读  评论(0)

目录

写在前面

抽象数据类型(ADT)

ADT定义

ADT表示和实现

三元组Triplet的定义与实现

定义ADT

实现三元组Triplet

用到的函数库、头文件和命名空间

函数执行结果状态代码

采用的存储结构

基本操作的函数原型说明

基本操作的实现

编写主函数

测试结果

顺序表List(创建,销毁,重置,插入,删除)

顺序表List定义

顺序表List实现

测试结果

实验总结



写在前面

课程采用教材《数据结构(C语言版)》严蔚敏,吴伟民,清华大学出版社。

本系列博文用于自我学习总结和期末复习使用,同时也希望能够帮助到有需要的同学。如果有知识性的错误,麻烦评论指出。

抽象数据类型(ADT)

ADT定义

抽象数据类型定义的格式:

ADT 抽象数据类型名{

数据对象:<数据对象的定义>

数据关系:<数据关系的定义>

基本操作:<基本操作的定义>

}ADT 抽象数据类型名

其中,数据对象和数据关系的定义用伪码描述,基本操作的定义格式为:

基本操作名(参数表)

初始条件:<初始条件描述>

操作结果:<操作结果描述>

基本操作有两种参数:赋值参数只为操作提供输入值;引用参数以&打头,除可提供输入值外,还将返回操作结果。“初始条件”描述了操作执行之前数据结构和参数应满足的条件,若不满足,则操作失败,并返回相应出错信息。“操作结果”说明了操作正常完成之后,数据结构的变化状况和应返回的结果。若初始条件为空,则省略之。

ADT表示和实现

需要载入一些函数库,预定义常量和类型,数据结构的表示(存储结构)用类型定义(typedef)描述,基本操作的算法采用函数描述,赋值语句,选择语句,循环语句,结束语句,输入输出语句,注释,基本函数,逻辑运算。

特别说明函数描述的形式:

函数类型 函数名(函数参数表){

// 算法说明

语句序列

} // 函数名

三元组Triplet的定义与实现

定义ADT

ADT Triplet{

数据对象:D = {e1,e2,e3 | e1,e2,e3∈ElemSet(定义了关系运算的某个集合)}

数据关系:R1={<e1,e2>,<e2,e3>}(包含三个元素的三元组,可用数组表示)

基本操作:

        InitTriplet(&T,v1,v2,v3)

          操作结果:构造了三元组T,元素e1,e2,e3分别被赋以v1,v2,v3的值。

        DestroyTriplet(&T)

          操作结果:三元组T被销毁。

        Get(T,i,&e)

          初始条件:三元组T已存在,1≤i≤3。

          操作结果:用e返回T的第i元的值。

        Put(&T,i,e)

          初始条件:三元组T已存在,1≤i≤3。

          操作结果:改变T的第i元的值为e。

        IsAscending(T)

          初始条件:三元组T已存在。

          操作结果:如果T的3个元素按升序排列则返回1,否则返回0。

        IsDescending(T)

          初始条件:三元组T已存在。

          操作结果:如果T的3个元素按降序排列,则返回1,否则返回0。

        Max(T,&e)

          初始条件:三元组T已存在。

          操作结果:用e返回T的3个元素中最大值。

        Min(T,&e)

          初始条件:三元组T已存在。

          操作结果:用e返回T的3个元素中最小值。

}ADT Triplet

今后定义ADT每行起始不再增加缩进和空格,请读者自行脑补。Ψ( ̄∀ ̄)Ψ

实现三元组Triplet

用到的函数库、头文件和命名空间


  
  1. #include <iostream>
  2. using namespace std;
  3. #include <stdio.h>
  4. #include <malloc.h>

函数执行结果状态代码


  
  1. //函数结果状态代码
  2. #define TRUE 1
  3. #define FALSE 0
  4. #define OK 1
  5. #define ERROR 0
  6. #define INFEASIBLE -1
  7. #define OVERFLOW -2
  8. //Status是函数的类型,其值是函数结果状况代码
  9. typedef int Status;
  10. typedef int ElemType; //将ElemType定义为int类型

采用的存储结构


  
  1. //-----采用动态分配的顺序存储结构-----
  2. typedef ElemType* Triplet; //由IniTriplet函数分配3个元素存储空间

基本操作的函数原型说明


  
  1. //-----基本操作的函数原型说明-----
  2. Status IniTriplet(Triplet& T, ElemType v1, ElemType v2, ElemType v3);
  3. // 操作结果:构造了三元组T,元素e1,e2,e3分别被赋以v1,v2,v3的值。
  4. Status DestoryTriplet(Triplet& T);
  5. // 操作结果:三元组T被销毁。
  6. Status Get(Triplet& T, int i, ElemType& e);
  7. // 初始条件:三元组T已存在,1≤i≤3。
  8. // 操作结果:用e返回T的第i元的值。
  9. Status Put(Triplet& T, int i, ElemType e);
  10. // 初始条件:三元组T已存在,1≤i≤3。
  11. // 操作结果:改变T的第i元的值为e。
  12. Status IsAscending(Triplet T);
  13. // 初始条件:三元组T已存在。
  14. // 操作结果:如果T的3个元素按升序排列,则返回1,否则返回0。
  15. Status IsDescending(Triplet T);
  16. // 初始条件:三元组T已存在。
  17. // 操作结果:如果T的3个元素按降序排列,则返回1,否则返回0。
  18. Status Max(Triplet T, ElemType& e);
  19. // 初始条件:三元组T已存在。
  20. // 操作结果:用e返回T的3个元素中最大值。
  21. Status Min(Triplet T, ElemType& e);
  22. // 初始条件:三元组T已存在。
  23. // 操作结果:用e返回T的3个元素中最小值。
  24. void PrintT(Triplet T);
  25. // 初始条件:三元组T已存在。
  26. // 操作结果:输出三元组T中的元素。

基本操作的实现


  
  1. //-----基本操作的实现-----
  2. Status IniTriplet(Triplet& T, ElemType v1, ElemType v2, ElemType v3){
  3. // 构造三元组T,依次置T的3个元素初值为v1,v2,v3。
  4. T = (ElemType*) malloc( 3 * sizeof(ElemType)); // 分配3个元素的存储空间
  5. if (!T) exit(OVERFLOW); // 分配存储空间失败
  6. T[ 0] = v1; T[ 1] = v2; T[ 2] = v3;
  7. return OK;
  8. } // IniTriplet
  9. Status DestoryTriplet(Triplet& T){
  10. // 销毁三元组T。
  11. free(T);
  12. T = NULL;
  13. return OK;
  14. } // DestoryTriplet
  15. Status Get(Triplet& T, int i, ElemType& e){
  16. // 1≤i≤3,用e返回T的第i元的值。
  17. if ((i < 1 || i> 3) || T == NULL) return INFEASIBLE;
  18. e = T[i - 1];
  19. return OK;
  20. } // Get
  21. Status Put(Triplet& T, int i, ElemType e){
  22. // 1≤i≤3,置T的第i元的值为e。
  23. if ((i < 1 || i> 3) || T == NULL) return INFEASIBLE;
  24. T[i - 1] = e;
  25. return OK;
  26. } // Put
  27. Status IsAscending(Triplet T){
  28. // 如果T的3个元素按升序排列,则返回1,否则返回0。
  29. if (T == NULL) return INFEASIBLE;
  30. return (T[ 0] <= T[ 1] && T[ 1] <= T[ 2]);
  31. } // IsAscending
  32. Status IsDescending(Triplet T) {
  33. // 如果T的3个元素按降序排列,则返回1,否则返回0。
  34. if (T == NULL) return INFEASIBLE;
  35. return (T[ 0] >= T[ 1] && T[ 1] >= T[ 2]);
  36. } // IsDescending
  37. Status Max(Triplet T, ElemType& e){
  38. // 用e返回指向T的最大元素的值。
  39. if (T == NULL) return INFEASIBLE;
  40. e = (T[ 0] >= T[ 1]) ? (T[ 0] >= T[ 2] ? T[ 2] : T[ 1]) : (T[ 1] >= T[ 2] ? T[ 1] : T[ 2]);
  41. return OK;
  42. } // Max
  43. Status Min(Triplet T, ElemType& e) {
  44. // 用e返回指向T的最小元素的值。
  45. if (T == NULL) return INFEASIBLE;
  46. e = (T[ 0] <= T[ 1]) ? (T[ 0] <= T[ 2] ? T[ 0] : T[ 2]) : (T[ 1] <= T[ 2] ? T[ 1] : T[ 2]);
  47. return OK;
  48. } // Min
  49. void PrintT(Triplet T){
  50. // 顺序输出三元组T中的元素。
  51. if (T != NULL) cout << "T中元素有:" << T[ 0] << " " << T[ 1] << " " << T[ 2] << endl;
  52. else cout << "三元组不存在!\n";
  53. } // PrintT

编写主函数


  
  1. int main()
  2. {
  3. Triplet T;
  4. int e;
  5. Status flag; // 状态标志
  6. // 构造三元组T,并赋值
  7. flag = IniTriplet(T, 90, 95, 100);
  8. PrintT(T);
  9. // 返回T的第2元的值
  10. flag = Get(T, 2, e);
  11. if (flag > 0) cout << "T中第2个元素为:" << e << endl;
  12. else cout << "ErrorType:" << flag << endl;
  13. // 置T的第3元的值为80
  14. flag = Put(T, 3, 80);
  15. if(flag > 0) PrintT(T);
  16. else cout << "ErrorType:" << flag << endl;
  17. // 判断T的3个元素是否按升序排列,是则返回1,否则返回0。
  18. flag = IsAscending(T);
  19. if (flag > 0) cout << "T中字符是升序。\n";
  20. else if (IsDescending(T) > 0) // 判断T的3个元素是否按降序排列,是则返回1,否则返回0。
  21. cout << "T中字符是降序。\n";
  22. else if (flag < 0)
  23. cout << "ErrorType:" << flag << endl;
  24. else cout << "T中字符无序。\n";
  25. // 获取T的最大元素的值。
  26. flag = Max(T, e);
  27. if (flag > 0) cout << "T中最大的元素为:" << e << endl;
  28. else cout << "ErrorType:" << flag << endl;
  29. // 获取T的最小元素的值。
  30. flag = Min(T, e);
  31. if (flag > 0) cout << "T中最小的元素为:" << e << endl;
  32. else cout << "ErrorType:" << flag << endl;
  33. // 销毁三元组T
  34. DestoryTriplet(T);
  35. PrintT(T); // 验证三元组已销毁
  36. return 0;
  37. }

测试结果

顺序表List(创建,销毁,重置,插入,删除)

顺序表List定义

1、线性结构特点

线性结构的特点是:在数据元素的非空有限集中,(1)存在唯一的一个被称做“第一个”的数据元素;(2)存在唯一的一个被称做“最后一个”的数据元素; (3)除第一个之外,集合中的每个数据元素均只有一个前驱;(4)除最后一个之外,集合中每个数据元素均只有一个后继。

2、线性表的类型定义

       线性表是最常用的且最简单的一种数据结构,它的长度可根据需要增长或缩短,对线性表的数据元素不仅可以进行访问,还可以进行插入和删除等。

       本次实验中抽象数据类型线性表的定义如下:

ADT List{

数据对象:D={ai| ai∈ElemSet,i=1,2,…,n,n≥0}

数据关系:R1={<ai-1,ai>| ai-1,ai ∈D,i=1,2,…,n }

基本操作:

InitList(&L)

操作结果:构造一个空的线性表L。

DestroyList(&L)

初始条件:线性表L已存在。

操作结果:销毁线性表L。

ClearList(&L)

初始条件:线性表L已存在。

操作结果:将L重置为空表。

ListInsert(&L,i,e)

初始条件:线性表L已存在,1≤i≤ListLength(L)+1。

  操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1。

ListDelete(&L,i,&e)

初始条件:线性表L已存在且非空,1≤i≤ListLength(L)。

  操作结果:删除L第i个数据元素,并用e返回其值,L的长度减1。

PrintList(L)

初始条件:线性表L已存在。

操作结果:输出线性表信息。

}ADT List

3、线性表的顺序表示和实现

       顺序存储:把线性表的结点按逻辑顺序依次存放在一组地址连续的存储单元里。用这种方法存储的线性表简称顺序表。

顺序存储的线性表的特点:(1)线性表的逻辑顺序与物理顺序一致;(2)数据元素之间的关系是以元素在计算机内“物理位置相邻”来体现。

这里采用数组来描述数据结构中的顺序存储结构,并采用malloc函数申请空间,来实现顺序表的长度可变。

顺序表List实现


  
  1. //1、用到的库函数、头文件和命名空间
  2. #include <iostream>
  3. using namespace std;
  4. #include <stdio.h>
  5. #include <malloc.h>
  6. //2、函数结果状态代码
  7. #define TRUE 1
  8. #define FALSE 0
  9. #define OK 1
  10. #define ERROR 0
  11. #define INFEASIBLE -1
  12. #define OVERFLOW -2
  13. //Status是函数的类型,其值是函数结果状况代码
  14. typedef int Status;
  15. typedef int ElemType; //将ElemType定义为int类型
  16. //3、采用的存储结构
  17. //-----线性表的动态分配顺序存储结构-----
  18. #define LIST_INIT_SIZE 100// 线性表存储空间的初始分配量
  19. #define LISTINCREMENT 10// 线性表存储空间的分配增量
  20. typedef struct {
  21. ElemType* elem; // 存储空间基址
  22. int length; // 当前长度
  23. int listsize; // 当前分配的存储容量(以sizeof(ElemType)为单位)
  24. }SqList;
  25. //4、基本操作的函数原型说明及实现
  26. //-----基本操作的函数原型说明-----
  27. Status InitList(SqList& L);
  28. // 操作结果:构造一个空的线性表L。
  29. Status DestoryList(SqList& L);
  30. // 初始条件:线性表L已存在。
  31. // 操作结果:销毁线性表L。
  32. Status ListInsert(SqList& L, int i, ElemType e);
  33. // 初始条件:线性表L已存在,1≤i≤ListLength(L)+1。
  34. // 操作结果:在线性表L中第i个位置之前插入新的元素e。
  35. Status ListDelete(SqList& L, int i, ElemType& e);
  36. // 初始条件:线性表L已存在且非空,1≤i≤ListLength(L)。
  37. // 操作结果:在顺序线性表中删除第i个元素,并用e返回其值
  38. void PrintList(SqList L);
  39. // 初始条件:线性表L已存在。
  40. // 操作结果:输出线性表信息。
  41. Status ClearList(SqList& L);
  42. // 初始条件:线性表L已存在。
  43. // 操作结果:将L重置为空表。
  44. Status ListEmpty(SqList L);
  45. // 初始条件:线性表L已存在。
  46. // 操作结果:若L为空表,则返回TURE,否则返回FALSE。
  47. Status InitList(SqList& L) {
  48. // 构造一个空的线性表L。
  49. L.elem = (ElemType*) malloc(LIST_INIT_SIZE * sizeof(ElemType));
  50. if (!L.elem) exit(OVERFLOW); // 存储分配失败
  51. L.length = 0; // 空表长度为0
  52. L.listsize = LIST_INIT_SIZE; // 初始存储容量
  53. return OK;
  54. } // IntiList
  55. Status DestoryList(SqList& L) {
  56. // 销毁线性表L。
  57. if (!L.elem) return INFEASIBLE; // 判断elem是否已空
  58. free(L.elem); // 销毁线性表
  59. L.elem = NULL; // 指针指空
  60. return OK;
  61. }
  62. Status ListInsert(SqList& L, int i, ElemType e) {
  63. // 在顺序线性表L中第i个位置之前插入新的元素e。
  64. // i的合法值为1≤i≤ListLength(L)+1。
  65. if (i< 1 || i>L.length+ 1 || L.elem == NULL) return ERROR; // 参数合法
  66. if (L.length == L.listsize) // 当前存储空间已满,增加分配
  67. {
  68. L.listsize += LISTINCREMENT; // 增加存储容量
  69. ElemType* elem_tmp; // 临时存放原空间元素
  70. elem_tmp = (ElemType*) malloc( sizeof(ElemType) * L.listsize);
  71. if (!elem_tmp) exit(OVERFLOW); // 存储空间分配失败
  72. for ( int j = 0; j < L.length; j++) // 向扩容后的空间复制原空间元素
  73. elem_tmp[j] = L.elem[j];
  74. free(L.elem); // 释放原空间
  75. L.elem = elem_tmp; // 完成扩容
  76. }
  77. for ( int j = L.length; j >= i -1 ; j--) // 插入位置之后的元素右移
  78. L.elem[j] = L.elem[j - 1];
  79. L.elem[i - 1] = e; // 插入e
  80. L.length++; // 表长加1
  81. return OK;
  82. } // ListInsert
  83. Status ListDelete(SqList& L, int i, ElemType& e) {
  84. // 在顺序线性表中删除第i个元素,并用e返回其值
  85. // i的合法值为1≤i≤ListLength(L)
  86. if (i< 1 || i>L.length || L.elem == NULL || L.length == 0) return ERROR; // 不合法情况
  87. e = L.elem[i - 1]; // 被删除元素赋给e
  88. for ( int j = i -1; j < L.length -1; j++) // 被删除元素之后的元素左移
  89. L.elem[j] = L.elem[j + 1];
  90. L.length--; // 表长减1
  91. return OK;
  92. } // ListDelet
  93. void PrintList(SqList L) {
  94. // 输出线性表信息
  95. if (!L.elem) exit(INFEASIBLE); // 判断elem是否已空
  96. cout << "线性表元素有:";
  97. for ( int i = 0; i < L.length; i++)
  98. cout << L.elem[i] << " ";
  99. cout << endl << "线性表长度:" << L.length
  100. << endl << "线性表容量:" << L.listsize << endl << endl;
  101. } // PrintList
  102. Status ClearList(SqList& L) {
  103. // 重置线性表L。
  104. if (!L.elem) return INFEASIBLE; // 判断elem是否已空
  105. free(L.elem); // 释放指针
  106. if (InitList(L)) return OK;
  107. }
  108. Status ListEmpty(SqList L) {
  109. if (!L.elem) return INFEASIBLE; // 判断elem是否已空
  110. if (L.length == 0) return TRUE;
  111. return FALSE;
  112. }
  113. //5、主函数
  114. int main()
  115. {
  116. // 初始化测试
  117. SqList L;
  118. InitList(L);
  119. PrintList(L);
  120. // 插入测试
  121. for ( int i = 1; i <= 100; i++) // i=1时测试表头插入,
  122. ListInsert(L, i , i ); // i>1时测试表尾连续插入LIST_INIT_SIZE-1个元素
  123. PrintList(L);
  124. ListInsert(L, 101, 101); // 测试扩容
  125. PrintList(L);
  126. ListInsert(L, 89, 0); // 测试中间位置插入
  127. PrintList(L);
  128. // 删除测试
  129. ElemType e;
  130. ListDelete(L, 89, e); // 测试中间位置删除
  131. PrintList(L);
  132. cout << "删除的元素为:" << e << endl << endl;
  133. ListDelete(L, 1, e); // 测试表头位置删除
  134. PrintList(L);
  135. cout << "删除的元素为:" << e << endl << endl;
  136. ListDelete(L, L.length, e); // 测试表尾位置删除
  137. PrintList(L);
  138. cout << "删除的元素为:" << e << endl << endl;
  139. // 重置测试
  140. ClearList(L);
  141. PrintList(L);
  142. // 销毁测试
  143. DestoryList(L);
  144. PrintList(L); // 表销毁会以INFEASIBLE值退出
  145. return 0;
  146. }

测试结果

实验总结

本次实验实现了线性表的顺序存储结构,以及顺序表的创建,插入,删除,重置,销毁功能。

       创建时,采用malloc函数申请LIST_INIT_SIZE * sizeof(ElemType)个空间,并使L.length = 0L.listsize = LIST_INIT_SIZE,完成顺序表L的初始化。销毁时,释放L.elem并使其指空,再对顺序表L操作时,L.elem为空,不可操作。重置时,如果L.elem不空,就再调用一次初始化函数InitList(L),重置顺序表L。

    插入时,满足初始条件后,先判断顺序表长度L.length与容量L.listsize是否相等,一般情况下,不会出现长度大于容量的情况,故判断是否等于即可。如果等于,则表明当前容量已满,需要再申请LISTINCREMENT个空间。具体操作是:

(1)先扩充容量,L.listsize += LISTINCREMENT

(2)用临时指针elem_tmp指向比原来多LISTINCREMENT个空间的首地址

(3)将原空间的数据元素复制到新申请的空间中,这里采用for循环,一次一个赋值。

(4)释放原空间,让L.elem指向存有原数据元素的新空间。

       之后,先将插入位置以后的元素都往后移一位,把插入数据元素放入插入位置,最后L.length++

       删除时,将删除位置的元素先通过e传出,再把删除位置之后的元素依次往前移一位,执行删除操作前的最后一位元素和已占用的多余空间可以不用处理。如果需要处理,可以采用插入时申请新空间一样的方法,不过新空间要比原来的空间少LISTINCREMENT。最后L.length--

       最后采用for循环逐个输出顺序表中的数据元素及当前元素个数和存储容量。

       通过本次实验,可以明显感受到对顺序表执行插入和删除操作有诸多的不便,占用额外的空间,插入删除操作不考虑改变存储空间时,时间复杂度为O(n)。但是采用顺序存储结构,可以与逻辑结构更好地对应,便于理解。


转载:https://blog.csdn.net/sbdsj_0111/article/details/106410461
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场