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

065d23dce3005634ca790d27cccbd104.png

写在前面

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

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

更多请见

数据结构(C/C++)课程阶段总结(一)_数据结构,经验分享,c语言_越来越废的SB的博客-CSDN博客​blog.csdn.net
aa4fba60912e09341b1194f6efed3db5.png

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

函数执行结果状态代码

//函数结果状态代码 
#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;
}

测试结果

8b2dae7c2d2f196f6cab3f66211a6117.png

顺序表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 GetElem(SqList L, int i, ElemType& e);
// 初始条件:线性表L已存在,1≤i≤ListLength(L)。
// 操作结果:用e返回L中第i个数据元素的值。
int LocateElem();
// 初始条件:三元组T已存在。
// 操作结果:用e返回T的3个元素中最大值。

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;
}

测试结果

9600d554720aafb7719bdd2cfedc8a36.png

实验总结

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

创建时,采用malloc函数申请LIST_INIT_SIZE * sizeof(ElemType)个空间,并使L.length = 0,L.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)。但是采用顺序存储结构,可以与逻辑结构更好地对应,便于理解。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值