【雨学习】算法与数据结构---顺序表篇 超详细的讲解和代码分享!!!

线性表是典型的线性结构,用顺序存储的方式实现ADT List,并在此基础上实现顺序表的输入输出以及插入删除。

[实验目的]

  1. 认识符号常量和宏定义。
  2. 学习编写头文件。
  3. 掌握线性表的顺序存储结构。
  4. 实现顺序表的基本操作。

 [实验内容及要求]

  1. 回顾课堂上老师的讲述,将课本第10页上的预定义常量和类型封装成一个头文件“predef.h”,供以后的每次实验使用。
  2. 参照课本第22页,实现顺序表的存储结构SqList,封装在头文件“sqlist.h”中。
  3. 参照课件(或者课本),实现顺序表的基本操作:求表长度、构造一个空顺序表、获取某个元素的值、插入新元素、删除某个元素等,封装在头文件“sqlist.h”中。
  4. 在源文件“sqlist_try.cpp”中,调用基本操作实现功能函数——顺序表的批量输入:依次输入n个数据元素存放在顺序表中(n由用户在程序运行时输入)。
  5. 在源文件“sqlist_try.cpp”中,调用基本操作实现功能函数——顺序表的批量输出:依次输出当前顺序表中的所有数据元素。
  6. 在源文件“sqlist_try.cpp”中,在主函数中调用上述功能函数和基本操作:批量向顺序表中输入数据元素,尝试插入和删除某个数据元素,输出顺序表中的所有数据元素并观察每次操作的结果是否正确。

 

[测试数据](示例)

  1. 1 2 3 4 5 6 7 8 9 10
  2. 12 32 65 9 21
  3. 98 5 87 66 4 7 54
  4. 101 6 88 76 5 89
  5. 10 109 87 78 86 5 61
  6. ……
  7. ……

 [程序流程图](主要功能模块)

 

[实验过程与结果](抓图和分析)

主测试函数:

 

测试输出:

[实验体会](随笔)

一、容量问题

主要是对于顺序表功能和操作代码的健壮性需要慎重考虑,如在插入元素或批量写入元素时,如果遇到顺序表容量不过够的问题,需要扩大空间

if (L.length + 1 > L.listsize) // 当前存储空间已满,增加分配

{

ElemType* newbase = (ElemType*)realloc(L.elem, (L.listsize + LISTINCREAMENT) * sizeof(ElemType));

if (!newbase) exit(OVERFLOW); // 存储分配失败

L.elem = newbase; // 新基址

L.listsize += LISTINCREAMENT; // 增加存储容量

}

二、越界访问

要注意好元素移动时的下标位置,不要越界访问。

[思考]

1)在程序设计语言中,原子类型和结构类型之间有什么区别与联系?

一、区别:

1. 定义:

   原子类型:是指不可再分的数据类型,通常由编程语言直接提供,如整型(int)、浮点型(float)、字符型(char)等。

   结构类型:是由多个原子类型或其他结构类型组合而成的复合数据类型,如数组(array)、结构体(struct)、联合体(union)、类(class)等。

2. 表示方式:

   原子类型:通常在内存中以固定大小的空间表示,例如一个整型可能占用4个字节。

   结构类型:由多个成员组成,每个成员可以是原子类型或其他结构类型,它们在内存中的布局通常是连续的,但整体大小可能不固定。

3. 操作:

   原子类型:通常只能进行基本的算术运算、关系运算和逻辑运算。

   结构类型:除了可以包含原子类型的操作外,还可以定义更复杂的操作,如函数、方法等。

4. 内存分配:

   原子类型:变量通常在栈上分配内存。

   结构类型:可能需要在堆上分配内存,特别是当结构类型的大小较大或者需要动态管理时。

5. 用途:

   原子类型:用于表示程序中的简单数据,如计数器、坐标点等。

   结构类型:用于表示更复杂的数据结构,如链表节点、用户定义的数据模型等。

二、联系:

1. 组合:结构类型可以包含原子类型作为其成员,原子类型是构成结构类型的基础。

   

2. 操作一致性:尽管结构类型和原子类型的操作不同,但在某些情况下,结构类型可以提供与原子类型类似的操作接口,以提供一致性。

3. 内存管理:无论是原子类型还是结构类型,都需要合理管理内存,避免内存泄漏或越界访问。

4. 类型转换:在某些编程语言中,可以将结构类型转换为原子类型(通常是通过指针或引用),反之亦然。

5. 抽象层次:原子类型通常位于较低层次的抽象,而结构类型则提供更高层次的抽象,但它们都是编程语言中不可或缺的部分。

理解这两种类型的区别与联系对于程序设计来说非常重要,它们分别适用于不同的编程场景,合理使用可以有效地解决各种问题。

(2)在实现顺序表的存储结构SqList及其基本操作的过程中,有什么困难和疑问?

可能会出现越界访问的问题,或是内存空间不足溢出的问题,应该让代码提高健壮性,保障用户的正常使用。

最后将全部代码放出,欢迎大家一起讨论思考!

sqlist_try.cpp

#include <stdio.h>
#include "sqlist.h"

/*
author: Electronic_Rain
date:   2024/09/28
*/

int main()
{
	SqList L;
	InitList_Sq(L);					// 初始化顺序表
	ListWriting_Sq(L);				// 批量写入顺序表
	ListShow_Sq(L);					// 显示顺序表
	ListInsertElem_Sq(L, 3, 10);	// 插入元素
	ListShow_Sq(L);					// 显示顺序表
	ListDeleteElem_Sq(L, 1);		// 删除元素
	ListShow_Sq(L);					// 显示顺序表
	int e = GetElem_Sq(L, 3);		// 获取第三个元素
	std::cout << e << std::endl;
	system("pause");
	return 0;
}

predef.h

#pragma once
// 函数结果状态代码
#define TRUE		1
#define FALSE		0
#define OK			1
#define ERROR		0
#define INFEASIBLE	-1
#define OVERFLOW	-2

#define Status      int

sqlist.h

#pragma once
#include "predef.h"
#include <malloc.h>
#include <stdio.h>
#include <iostream>
#include <stdlib.h>

// -----线性表的动态分配顺序表存储结构----- //
#define LIST_INIT_SIZE		100  // 线性表存储空间的初始分配量
#define LISTINCREAMENT		10   // 线性表存储空间的分配增量
#define ElemType			int  // 设定线性表中存储数据类型

typedef struct {
	ElemType* elem;				 // 存储空间基址
	int length;					 // 当前长度
	int listsize;				 // 当前分配的存储容量
}SqList;

// 初始化顺序表
Status InitList_Sq(SqList& 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;
} // InitList_Sq

// 返回顺序表元素个数
int ListLength_Sq(const SqList L)
{
	return L.length;	//返回表长
} // ListLength_Sq

// 获取某个元素的值
ElemType GetElem_Sq(const SqList L, int pos)
{
	return L.elem[pos - 1];		//返回pos-1位置元素
} // GetElem_Sq

// 插入新元素
Status ListInsertElem_Sq(SqList& L, int pos, ElemType ele)
{
	if (pos < 1 || pos > L.length + 1) return ERROR;	// pos值不合理
	if (L.length + 1 > L.listsize)						// 当前存储空间已满,增加分配
	{
		ElemType* newbase = (ElemType*)realloc(L.elem, (L.listsize + LISTINCREAMENT) * sizeof(ElemType));
		if (!newbase) exit(OVERFLOW);					// 存储分配失败
		L.elem = newbase;								// 新基址
		L.listsize += LISTINCREAMENT;					// 增加存储容量
	}
	for (int i = L.length; i >= pos; i--)
	{
		L.elem[i] = L.elem[i - 1];						// 将后面数字向后移位
	}
	L.elem[pos - 1] = ele;								// 在pos-1位置赋值ele
	L.length++;											// 表长加一
	return OK;
} // ListInsertElem_Sq

// 删除某个元素
Status ListDeleteElem_Sq(SqList& L, int pos)
{
	if (pos < 1 || pos > L.length) return ERROR;		// pos值不合理
	for (int i = pos - 1; i < L.length - 1; i++)
	{
		L.elem[i] = L.elem[i + 1];						// 将元素前置一位
	}
	L.length--;											// 表长减一
	return OK;
} // ListDeleteElem_Sq

// 判断顺序表是否为空
Status ListIsEmpty_Sq(const SqList L)
{
	return L.length == 0 ? OK : FALSE;					// 表为空返回OK,不为空返回FALSE
} // ListIsEmpty_Sq

// 显示顺序表
void ListShow_Sq(const SqList L)
{
	if (ListIsEmpty_Sq(L)) printf("表中无元素\n");			// 表为空
	printf("顺序表中元素为:");
	for (int i = 0; i < L.length; i++)
	{
		if (i == L.length - 1)
		{
			std::cout << L.elem[i] << std::endl;
			return;
		}
		std::cout << L.elem[i] << ",";
	}
} // ListShow_Sq

// 在顺序表后添加一个元素
Status ListPushBack_Sq(SqList& L, ElemType x)
{
	if (L.length + 1 > L.listsize)						// 当前存储空间已满,增加分配
	{
		ElemType* newbase = (ElemType*)realloc(L.elem, (L.listsize + LISTINCREAMENT) * sizeof(ElemType));
		if (!newbase) exit(OVERFLOW);					// 存储分配失败
		L.elem = newbase;								// 新基址
		L.listsize += LISTINCREAMENT;					// 增加存储容量
	}
	L.elem[L.length] = x;
	L.length++;
	return OK;
} // ListPushBack_Sq

// 输入顺序表数据
Status ListWriting_Sq(SqList& L)
{
	ElemType x;
	int n;
	std::cout << "请输入顺序表中元素个数:";
	std::cin >> n;
	int count = 0;
	while (n--)
	{
		count++;
		std::cout << "请输入顺序表中第" << count << "位元素:";
		std::cin >> x;
		ListPushBack_Sq(L, x);
	}
	return OK;
} // ListWriting_Sq
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Electronic_Rain

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

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

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

打赏作者

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

抵扣说明:

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

余额充值