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

目录

写在前面

抽象数据类型(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

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

#include <iostream>
using namespace std;
#include <stdio.h>
#include <malloc.h>

函数执行结果状态代码

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

采用的存储结构

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

基本操作的函数原型说明

//-----基本操作的函数原型说明-----
Status IniTriplet(Triplet& T, ElemType v1, ElemType v2, ElemType v3);
 // 操作结果:构造了三元组T,元素e1,e2,e3分别被赋以v1,v2,v3的值。
Status DestoryTriplet(Triplet& T);
 // 操作结果:三元组T被销毁。
Status Get(Triplet& T, int i, ElemType& e);
 // 初始条件:三元组T已存在,1≤i≤3。
 // 操作结果:用e返回T的第i元的值。
Status Put(Triplet& T, int i, ElemType e);
 // 初始条件:三元组T已存在,1≤i≤3。
 // 操作结果:改变T的第i元的值为e。
Status IsAscending(Triplet T);
 // 初始条件:三元组T已存在。
 // 操作结果:如果T的3个元素按升序排列,则返回1,否则返回0。
Status IsDescending(Triplet T);
 // 初始条件:三元组T已存在。
 // 操作结果:如果T的3个元素按降序排列,则返回1,否则返回0。
Status Max(Triplet T, ElemType& e);
 // 初始条件:三元组T已存在。
 // 操作结果:用e返回T的3个元素中最大值。
Status Min(Triplet T, ElemType& e);
 // 初始条件:三元组T已存在。
 // 操作结果:用e返回T的3个元素中最小值。
void PrintT(Triplet T);
 // 初始条件:三元组T已存在。
 // 操作结果:输出三元组T中的元素。

基本操作的实现

//-----基本操作的实现-----
Status IniTriplet(Triplet& T, ElemType v1, ElemType v2, ElemType v3){
    // 构造三元组T,依次置T的3个元素初值为v1,v2,v3。
    T = (ElemType*)malloc(3 * sizeof(ElemType)); // 分配3个元素的存储空间
    if (!T) exit(OVERFLOW);// 分配存储空间失败
    T[0] = v1; T[1] = v2; T[2] = v3;
    return OK;
}// IniTriplet
Status DestoryTriplet(Triplet& T){
    // 销毁三元组T。
    free(T);
    T = NULL;
    return OK;
}// DestoryTriplet
Status Get(Triplet& T, int i, ElemType& e){
    // 1≤i≤3,用e返回T的第i元的值。
    if ((i < 1 || i>3) || T == NULL)return INFEASIBLE;
    e = T[i - 1];
    return OK;
}// Get
Status Put(Triplet& T, int i, ElemType e){
    // 1≤i≤3,置T的第i元的值为e。
    if ((i < 1 || i>3) || T == NULL)return INFEASIBLE;
    T[i - 1] = e;
    return OK;
}// Put
Status IsAscending(Triplet T){
    // 如果T的3个元素按升序排列,则返回1,否则返回0。
    if (T == NULL)return INFEASIBLE;
    return (T[0] <= T[1] && T[1] <= T[2]);
}// IsAscending
Status IsDescending(Triplet T) {
    // 如果T的3个元素按降序排列,则返回1,否则返回0。
    if (T == NULL)return INFEASIBLE;
    return (T[0] >= T[1] && T[1] >= T[2]);
}// IsDescending
Status Max(Triplet T, ElemType& e){
    // 用e返回指向T的最大元素的值。
    if (T == NULL)return INFEASIBLE;
    e = (T[0] >= T[1]) ? (T[0] >= T[2] ? T[2] : T[1]) : (T[1] >= T[2] ? T[1] : T[2]);
    return OK;
}// Max
Status Min(Triplet T, ElemType& e) {
    // 用e返回指向T的最小元素的值。
    if (T == NULL)return INFEASIBLE;
    e = (T[0] <= T[1]) ? (T[0] <= T[2] ? T[0] : T[2]) : (T[1] <= T[2] ? T[1] : T[2]);
    return OK;
}// Min
void PrintT(Triplet T){
    // 顺序输出三元组T中的元素。
    if (T != NULL) cout << "T中元素有:" << T[0] << " " << T[1] << " " << T[2] << endl;
    else cout << "三元组不存在!\n";
}// PrintT

编写主函数

int main()
{
    Triplet T;
    int e;
    Status flag;// 状态标志
    // 构造三元组T,并赋值
    flag = IniTriplet(T, 90, 95, 100);
    PrintT(T);
    // 返回T的第2元的值
    flag = Get(T, 2, e); 
    if (flag > 0) cout << "T中第2个元素为:" << e << endl;
    else cout << "ErrorType:" << flag << endl;
    // 置T的第3元的值为80
    flag = Put(T, 3, 80);
    if(flag > 0) PrintT(T);
    else cout << "ErrorType:" << flag << endl;
    // 判断T的3个元素是否按升序排列,是则返回1,否则返回0。
    flag = IsAscending(T); 
    if (flag > 0) cout << "T中字符是升序。\n";
    else if (IsDescending(T) > 0)// 判断T的3个元素是否按降序排列,是则返回1,否则返回0。
        cout << "T中字符是降序。\n";
    else if (flag < 0)
        cout << "ErrorType:" << flag << endl;
    else cout << "T中字符无序。\n";
    // 获取T的最大元素的值。
    flag = Max(T, e); 
    if (flag > 0) cout << "T中最大的元素为:" << e << endl;
    else cout << "ErrorType:" << flag << endl;
    // 获取T的最小元素的值。
    flag = Min(T, e);
    if (flag > 0) cout << "T中最小的元素为:" << e << endl;
    else cout << "ErrorType:" << flag << endl;
    // 销毁三元组T
    DestoryTriplet(T);
    PrintT(T);// 验证三元组已销毁
    return 0;
}

测试结果

triplet

顺序表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、用到的库函数、头文件和命名空间
#include <iostream>
using namespace std;
#include <stdio.h>
#include <malloc.h>
//2、函数结果状态代码
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
//Status是函数的类型,其值是函数结果状况代码
typedef int Status;
typedef int ElemType;//将ElemType定义为int类型
//3、采用的存储结构
//-----线性表的动态分配顺序存储结构-----
#define LIST_INIT_SIZE 100// 线性表存储空间的初始分配量
#define LISTINCREMENT 10// 线性表存储空间的分配增量
typedef struct {
	ElemType* elem;// 存储空间基址
	int length;// 当前长度
	int listsize;// 当前分配的存储容量(以sizeof(ElemType)为单位)
}SqList;
//4、基本操作的函数原型说明及实现
//-----基本操作的函数原型说明-----
Status InitList(SqList& L);
// 操作结果:构造一个空的线性表L。
Status DestoryList(SqList& L);
// 初始条件:线性表L已存在。
// 操作结果:销毁线性表L。
Status ListInsert(SqList& L, int i, ElemType e);
// 初始条件:线性表L已存在,1≤i≤ListLength(L)+1。
// 操作结果:在线性表L中第i个位置之前插入新的元素e。
Status ListDelete(SqList& L, int i, ElemType& e);
// 初始条件:线性表L已存在且非空,1≤i≤ListLength(L)。
// 操作结果:在顺序线性表中删除第i个元素,并用e返回其值
void PrintList(SqList L);
// 初始条件:线性表L已存在。
// 操作结果:输出线性表信息。
Status ClearList(SqList& L);
// 初始条件:线性表L已存在。
// 操作结果:将L重置为空表。
Status ListEmpty(SqList L);
// 初始条件:线性表L已存在。
// 操作结果:若L为空表,则返回TURE,否则返回FALSE。

Status InitList(SqList& L) {
	// 构造一个空的线性表L。
	L.elem = (ElemType*)malloc(LIST_INIT_SIZE * sizeof(ElemType));
	if (!L.elem)exit(OVERFLOW);// 存储分配失败
	L.length = 0;// 空表长度为0
	L.listsize = LIST_INIT_SIZE;// 初始存储容量
	return OK;
}// IntiList
Status DestoryList(SqList& L) {
	// 销毁线性表L。
	if (!L.elem)return INFEASIBLE;// 判断elem是否已空
	free(L.elem);// 销毁线性表
	L.elem = NULL;// 指针指空
	return OK;
}
Status ListInsert(SqList& L, int i, ElemType e) {
	// 在顺序线性表L中第i个位置之前插入新的元素e。
	// i的合法值为1≤i≤ListLength(L)+1。
	if (i<1 || i>L.length+1 || L.elem == NULL)return ERROR;// 参数合法
	if (L.length == L.listsize)// 当前存储空间已满,增加分配
	{
		L.listsize += LISTINCREMENT;// 增加存储容量
		ElemType* elem_tmp;// 临时存放原空间元素
		elem_tmp = (ElemType*)malloc(sizeof(ElemType) * L.listsize);
		if (!elem_tmp)exit(OVERFLOW);// 存储空间分配失败
		for (int j = 0; j < L.length; j++)// 向扩容后的空间复制原空间元素
			elem_tmp[j] = L.elem[j];
		free(L.elem);// 释放原空间
		L.elem = elem_tmp;// 完成扩容
	}
	for (int j = L.length; j >= i-1 ; j--)// 插入位置之后的元素右移
		L.elem[j] = L.elem[j - 1];
	L.elem[i - 1] = e;// 插入e
	L.length++;// 表长加1
	return OK;
}// ListInsert
Status ListDelete(SqList& L, int i, ElemType& e) {
	// 在顺序线性表中删除第i个元素,并用e返回其值
	// i的合法值为1≤i≤ListLength(L)
	if (i<1 || i>L.length || L.elem == NULL || L.length == 0)return ERROR;// 不合法情况
	e = L.elem[i - 1];// 被删除元素赋给e
	for (int j = i-1; j < L.length-1; j++)// 被删除元素之后的元素左移
		L.elem[j] = L.elem[j + 1];
	L.length--;// 表长减1
	return OK;
}// ListDelet
void PrintList(SqList L) {
	// 输出线性表信息
	if (!L.elem)exit(INFEASIBLE);// 判断elem是否已空
	cout << "线性表元素有:";
	for (int i = 0; i < L.length; i++)
		cout << L.elem[i] << " ";
	cout << endl << "线性表长度:" << L.length
		<< endl << "线性表容量:" << L.listsize << endl << endl;
}// PrintList
Status ClearList(SqList& L) {
	// 重置线性表L。
	if (!L.elem)return INFEASIBLE;// 判断elem是否已空
	free(L.elem);// 释放指针
	if (InitList(L))return OK;
}
Status ListEmpty(SqList L) {
	if (!L.elem)return INFEASIBLE;// 判断elem是否已空
	if (L.length == 0)return TRUE;
	return FALSE;
}
//5、主函数
int main()
{
	// 初始化测试
	SqList L;
	InitList(L);
	PrintList(L);
	// 插入测试
	for (int i = 1; i <= 100; i++)// i=1时测试表头插入,
		ListInsert(L, i , i );// i>1时测试表尾连续插入LIST_INIT_SIZE-1个元素
	PrintList(L);
	ListInsert(L, 101, 101);// 测试扩容
	PrintList(L);
	ListInsert(L, 89, 0);// 测试中间位置插入
	PrintList(L);
	// 删除测试
	ElemType e;
	ListDelete(L, 89, e);// 测试中间位置删除
	PrintList(L);
	cout << "删除的元素为:" << e << endl << endl;
	ListDelete(L, 1, e);// 测试表头位置删除
	PrintList(L);
	cout << "删除的元素为:" << e << endl << endl;
	ListDelete(L, L.length, e);// 测试表尾位置删除
	PrintList(L);
	cout << "删除的元素为:" << e << endl << endl;
	// 重置测试
	ClearList(L);
	PrintList(L);
	// 销毁测试
	DestoryList(L);
	PrintList(L);// 表销毁会以INFEASIBLE值退出
	return 0;
}

测试结果

测试结果

实验总结

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

       创建时,采用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)。但是采用顺序存储结构,可以与逻辑结构更好地对应,便于理解。

  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
数据结构与算法是计算机科学中非常重要的一门课程,它涉及到用来存储和组织数据的结构以及解决实际问题的算法。习题 c 可能是指该课程中的某一个具体习题。 对于习题 c,我暂时无法给出具体的回答,因为不清楚习题的具体内容。但是我可以给出一些关于数据结构与算法习题的一般性建议和思路: 1. 理解问题:首先要仔细阅读并理解习题的要求,明确问题的输入和输出,以及需要解决的具体任务。 2. 分解问题:将问题分解为更小的子问题,这样更易于理解和解决。可以将问题抽象为适当的数据结构和算法之间的关系。 3. 设计算法:根据问题的要求和数据结构的特点,设计解决问题的算法。算法的设计可能涉及到遍历、查找、排序等常见的操作。 4. 分析算法:对设计的算法进行时间和空间复杂度的分析,评估其效率和可行性。这样能够更好地选择合适的算法。 5. 编码实现:根据设计好的算法,使用合适的编程语言将其实现出来。在实现过程中要注意代码的可读性和可维护性。 6. 测试和调试:进行充分的测试以验证算法的正确性和鲁棒性。若有问题,在调试阶段进行适当的修正和改进。 总结来说,解决数据结构与算法习题需要理解问题、分析问题、设计解决方案、实现代码以及测试和调试。通过不断练习习题,可以提高对数据结构与算法的理解和应用能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kFkPkDkN

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值