文章目录
讲在开头
实验代码很长,建议对应一个功能一个功能看,切忌一个文件从头看到尾,会十分费劲。
1、上机名称
实现顺序表的各种基本操作
2、上机要求
领会顺序表存储结构和掌握顺序表中的各种基本运算算法设计。 编写一个程序,实现顺序表的各种基本运算和整体建表算法(假设顺序表的元素类型为int),并在此基础上设计一个主程序,完成如下功能:
- 构建基于动态数组的顺序表 list,并初始化顺序表。
- 依次将 file.txt 文件中的数据输入到顺序表中,输入的数据量依次为
10、100、1000、10000、100000、1000000,要求实现动态的扩容,记录运行时间。 - 输出顺序表 L 长度,记录运行时间。
- 判断顺序表 L 是否为空,记录运行时间。
- 分别输出顺序表 L 的第 30、300、3000、30000、300000个元素值,记录相应时间。
- 分别输出值为-46、-7、-42、42、1234、2000 的元素首次出现的位置,如果不存在就显示为-1,记录相应时间。
- 依次在第 4、40、400、4000、40000、400000个元素位置上插入整数 234,记录相应的时间。
- 依次删除在第 4、40、400、4000、40000、400000个元素位置上的元素,记录相应的时间。
- 依次输出顺序表 L 的元素,记录运行时间。
- 依次删除前 1000 和后 1000 的元素,要实现动态的缩容,记录运行时间。
- 清空顺序表 L。
- 释放顺序表 L。
3、上机环境
visual studio 2022
4、程序清单
4.1 代码和注释讲解部分
4.1.1 头文件list_sq.h:
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<time.h>
#pragma warning(disable:4996)
#define INIT_LEN 100
typedef int Data;
typedef int Status;
typedef struct List_sq {
Data* data;
int curlen;
int size;
}sqlist;
//初始化
void SqlistInit(sqlist* list);
//在指定位置增加数据
Status SqlistAdd(sqlist* list, int pos, Data add);
//在指定位置读取数据
Data SqlistRead(sqlist* list, int pos);
//删除指定位置的数据
Data SqlistDelpos(sqlist* list, int pos);
Data SqlistDelend(sqlist* list);
//遍历一定范围的数据
void SqlistVisit(sqlist* list, int from, int to);
//判断是否为空
Status Sqlist0(sqlist* list);
//取现在的长度
int SqlistCurlen(sqlist* list);
//删除整个顺序表
void SqlistDelall(sqlist* list);
//找到值相同元素的个数
int SqlistValCnt(sqlist* list, Data val);
//打印相关信息
void SqlistPrint(sqlist* list);
//输入时间测试
float timetest(sqlist* list, int n,int repeat);
//在进行实验前的一些准备工作
void SqlistReady(sqlist* list);
void SqlistReady(sqlist* list,int n);
//插入时间测试
float addtest(sqlist* list, int pos, int repeat);
void Addtest(sqlist* list, int pos, int repeat);
//删除时间测试
float deltest(sqlist* list, int pos, int repeat);
//遍历时间测试
float vistest(sqlist* list, int len, int repeat,char buff[]);
//删除1000元素(前后之分)时间测试
void FroBhddel(sqlist* list, int len, int repeat);
//盘算是否为空测试
void isEmpty(sqlist* list,int len);
//读取指定位置
void readtest(sqlist* list, int pos);
//输出长度测试
void lentest(sqlist* list, int len);
//获取元素首次出现的位置
int SqlistFstPos(sqlist* list, Data aim);
//出现位置测试
void fpostest(sqlist* list, Data aim);
4.1.2 实现文件list_sq.cpp:
#include "list_sq.h"
void SqlistInit(sqlist*list){
list->data = (Data*)malloc(INIT_LEN * sizeof(Data));
list->curlen = 0;
list->size = INIT_LEN;
}
Status SqlistAdd(sqlist* list, int pos, Data add){
//增加之前确定长度是否足够
//如果不足重新申请:
if (list->curlen == list->size) {
list->size *= 2;
Data* ndata = (Data*)malloc(list->size * sizeof(Data));
for (int i = 0; i < list->curlen; i++) {
ndata[i] = list->data[i];
}
free(list->data);
list->data = ndata;
}
//如果充沛:
if (pos<0 || pos>list->curlen) {
printf("pos invalid!");
return -1;
}
for (int i = list->curlen; i > pos; i--) {
list->data[i] = list->data[i - 1];
}
list->data[pos] = add;
list->curlen++;
return 1;
}
Data SqlistRead(sqlist* list, int pos){
return list->data[pos];
}
Data SqlistDelpos(sqlist* list, int pos){
Data posdata = list->data[pos];
for (int i = pos ; i < list->curlen - 2; i++) {
list->data[i] = list->data[i + 1];
}
return posdata;
}
Data SqlistDelend(sqlist* list){
Data last = SqlistDelpos(list, list->curlen - 1);
return last;
}
void SqlistVisit(sqlist* list, int from, int to) {
for (int i = from; i < to; i++) {
printf("at %d:\t%d\n", i, list->data[i]);
}
}
Status Sqlist0(sqlist* list){
free(list->data);
list->curlen = 0;
list->data = (Data*)malloc(sizeof(Data) * list->size);
return 0;
}
int SqlistCurlen(sqlist* list){
return list->curlen;
}
void SqlistDelall(sqlist* list){
free(list->data);
list->data = NULL;
list->curlen = 0;
list->size = 0;
}
int SqlistValCnt(sqlist* list, Data val){
int cnt = 0;
for (int i = 0; i < list->curlen; i++) {
if (val == list->data[i])cnt++;
}
return cnt;
}
void SqlistPrint(sqlist* list){
printf("curlen:%d,size:%d\n", list->curlen, list->size);
}
float timetest(sqlist* list, int n,int repeat) {
//从文本读取数据存储到testData
char filename[] = "file.txt";
Data* testData = (Data*)malloc(n * sizeof(Data));
FILE* fp;
if (!fopen_s(&fp, filename, "r")) {
for (long i = 0; i < n; i++) {
fscanf(fp, "%d\n", &testData[i]);
}
}
if (fp != NULL)fclose(fp);
//将testData数据读取到list中并计时
clock_t start, end;
start = clock();
for (int i = 0; i < repeat; i++) {
Sqlist0(list);
for (int j = 0; j < n; j++) {
SqlistAdd(list, j, testData[j]);
}
}
end = clock();
float during = difftime(end, start) / repeat;
printf("读取%d个数据,用时:%f ms\n", n, during);
return during;
}
void SqlistReady(sqlist* list){
//读元素
SqlistInit(list);
int n = 1000000;
char filename[] = "file.txt";
Data* testData = (Data*)malloc(n * sizeof(Data));
FILE* fp;
if (!fopen_s(&fp, filename, "r")) {
for (long i = 0; i < n; i++) {
fscanf(fp, "%d\n", &testData[i]);
}
}
if (fp != NULL)fclose(fp);
//写元素
for (int j = 0; j < n; j++) {
SqlistAdd(list, j, testData[j]);
}
}
void SqlistReady(sqlist* list,int n) {
//读元素
SqlistInit(list);
char filename[] = "file.txt";
Data* testData = (Data*)malloc(n * sizeof(Data));
FILE* fp;
if (!fopen_s(&fp, filename, "r")) {
for (long i = 0; i < n; i++) {
fscanf(fp, "%d\n", &testData[i]);
}
}
if (fp != NULL)fclose(fp);
//写元素
for (int j = 0; j < n; j++) {
SqlistAdd(list, j, testData[j]);
}
}
float addtest(sqlist* list, int pos,int repeat) {
clock_t start, end;
start = clock();
for (int i = 0; i < repeat; i++) {
SqlistAdd(list, pos, 234);
}
end = clock();
float during = difftime(end, start)/repeat;
printf("在位置%d插入元素时间为:%f ms\n", pos, during);
return during;
}
void Addtest(sqlist* list, int pos, int repeat) {
SqlistReady(list);
addtest(list, pos, repeat);
}
float deltest(sqlist* list, int pos, int repeat){
clock_t start, end;
start = clock();
for (int i = 0; i < repeat; i++) {
SqlistDelpos(list, pos);
}
end = clock();
float during = difftime(end, start) / repeat;
printf("在位置%d删除元素时间为:%f ms\n", pos, during);
return during;
}
float vistest(sqlist* list, int len,int repeat,char buff[]) {
clock_t start, end;
start = clock();
for (int i = 0; i < repeat; i++) {
SqlistVisit(list, 0, len);
}
end = clock();
float during = difftime(end, start) / repeat;
sprintf(buff,"遍历%d个元素时间为:%f ms\n", len, during);
return during;
}
void FroBhddel(sqlist* list, int len, int repeat) {
SqlistReady(list, len);
clock_t start, end;
start = clock();
for (int i = 0; i < repeat; i++) {
SqlistDelpos(list, 0);
}
end = clock();
float during = difftime(end, start) / repeat;
printf("在长度为%d时,前端删除1000个元素时间为:%f ms\n",len,during);
start = clock();
for (int i = 0; i < repeat*10e6; i++) {
SqlistDelend(list);
}
end = clock();
during = difftime(end, start) / (repeat*10e6);
printf("在长度为%d时,后端删除1000个元素时间为:%f ms\n", len, during);
}
void isEmpty(sqlist* list,int len) {
SqlistReady(list, len);
clock_t start, end;
start = clock();
for (int i = 0; i < 16000000; i++) {
SqlistCurlen(list);
}
end = clock();
float during = difftime(end, start) / 16000000;
printf("在长度为%d时,判断是否为空的时间为:%f ms\n", len, during);
}
void readtest(sqlist* list, int pos) {
SqlistReady(list);
clock_t start, end;
start = clock();
for (int i = 0; i < 16000000; i++) {
SqlistRead(list, pos);
}
end = clock();
float during = difftime(end, start) / 16000000;
printf("读取%d位置的时间为:%f ms\n", pos, during);
}
void lentest(sqlist* list, int len){
SqlistReady(list, len);
clock_t start, end;
start = clock();
for (int i = 0; i < 16000000; i++) {
SqlistCurlen(list);
}
end = clock();
float during = difftime(end, start) / 16000000;
printf("输出%d长度顺序表的长度所需的时间为:%f ms\n", len, during);
}
int SqlistFstPos(sqlist* list, Data aim) {
for (int i = 0; i < list->curlen; i++) {
if (list->data[i] == aim) return i;
}
return -1;
}
void fpostest(sqlist* list, Data aim) {
clock_t start, end;
start = clock();
int pos = -1;
for (int i = 0; i < 800000; i++) {
pos = SqlistFstPos(list, aim);
}
end = clock();
float during = difftime(end, start) / 800000;
printf("%d 位置:%d 时间:%f ms\n",aim, pos, during);
}
4.1.3 源文件main.cpp:
#include"list_sq.h"
int main(int argc,char* argv[]) {
sqlist list;
SqlistInit(&list);
//动态添加数据时间测试
timetest(&list, 10, 10000000);
timetest(&list, 100, 1000000);
timetest(&list, 1000, 100000);
timetest(&list, 10000, 10000);
timetest(&list, 100000, 1000);
timetest(&list, 1000000, 100);
system("pause");
system("cls");
//输出长度测试
lentest(&list, 10);
lentest(&list, 100);
lentest(&list, 1000);
lentest(&list, 10000);
lentest(&list, 100000);
lentest(&list, 1000000);
system("pause");
system("cls");
//判断是否为空
isEmpty(&list, 10);
isEmpty(&list, 100);
isEmpty(&list, 1000);
isEmpty(&list, 10000);
isEmpty(&list, 100000);
isEmpty(&list, 1000000);
system("pause");
system("cls");
//获取特定元素值实验
readtest(&list, 30);
readtest(&list, 300);
readtest(&list, 3000);
readtest(&list, 30000);
readtest(&list, 300000);
system("pause");
system("cls");
//获取元素首次出现位置(下标)
SqlistReady(&list, 10000);
fpostest(&list, -46);
fpostest(&list, 42);
fpostest(&list, -7);
fpostest(&list, -42);
fpostest(&list, 1234);
fpostest(&list, 2000);
system("pause");
system("cls");
//在特定位置插入和删除元素时间测试
Addtest(&list, 4, 800);
deltest(&list, 4, 800);
Addtest(&list, 40, 800);
deltest(&list, 40, 800);
Addtest(&list, 400, 800);
deltest(&list, 400, 800);
Addtest(&list, 4000, 800);
deltest(&list, 4000, 800);
Addtest(&list, 40000, 800);
deltest(&list, 40000, 800);
Addtest(&list, 400000, 800);
deltest(&list, 400000, 800);
system("pause");
system("cls");
//遍历时间测试
SqlistInit(&list);
SqlistReady(&list);
char buff1[800] = {};
char buff2[800] = {};
char buff3[800] = {};
char buff4[800] = {};
char buff5[800] = {};
char buff6[800] = {};
vistest(&list, 10, 8000,buff1);
vistest(&list, 100, 800,buff2);
vistest(&list, 1000, 80,buff3);
vistest(&list, 10000, 8,buff4);
vistest(&list, 100000, 3,buff5);
vistest(&list, 1000000, 2,buff6);
printf("%s%s%s%s%s%s", buff1, buff2, buff3, buff4, buff5, buff6);
system("pause");
system("cls");
//依次删除前后1000个元素时间测试
FroBhddel(&list, 50000, 16);
FroBhddel(&list, 100000, 16);
FroBhddel(&list, 200000, 16);
FroBhddel(&list, 400000, 16);
FroBhddel(&list, 800000, 16);
system("pause");
system("cls");
//销毁和清除
SqlistReady(&list);
SqlistPrint(&list);
SqlistDelall(&list);
SqlistPrint(&list);
SqlistReady(&list);
Sqlist0(&list);
SqlistPrint(&list);
system("pause");
system("cls");
}
4.2 实验结果:
4.2.1动态添加数据
4.2.2输出顺序表长度
4.2.3判断是否为空
4.2.4 获取特定位置元素值实验
4.2.5 获取元素首次出现的位置
4.2.6 在特定位置上插入、删除元素
4.2.7 遍历循序表
4.2.8 依次删除前后1000个元素
4.2.9清除和销毁
5、上机体会
5.1关于一些问题的思考
- 带固定增量的顺序表的扩容方式和采用倍增的方式扩容的算法性能上有多大区别?分析这两种算法的时间复杂度,请用具体数据给出性能的比较。答:当数据量比较小时,两种扩容方法结果相差不大,固定增量甚至还要快些,但数据量指数增长时,倍增扩容法的优势完全显露出来,两者开始出现指数级的差异。
- 当 L.len==L.capacity 时,顺序表进行扩容,将 capacity 变为原先的两倍,而能否当L.len ==L.capacity/2 时,进行缩容将 capacity 缩小一半?给出理由。答:尽量不要这么做,数据量达到capacity时,若要在这个水平上增加删除数据,会导致动态内存分配频繁,减缓执行速度。
- 顺序表的优点和缺点分别是什么?它适合应用于什么样子的场景?答:顺序表是一段地址连续的存储单元依次存储数据元素的线性结构他的优点是存取速度高效,通过下标来直接存取(随机存取) 缺点是插入和删除比较慢,不可以实时增长长度适用于需要大量访问元素的,而增加或者删除元素较少的程序。
- 顺序表插入和删除元素过程中,移动元素的方向和起始元素如果设置的不正确会存在哪些问题呢?答:导致头部或者尾部的一些数据溢出,实际操作元素与希望操作的元素不符,数据覆盖,污染,导致无法正确进行后续操作。
5.2上机体会
本次实验重点在于实现顺序表的各种基本运算并对线性表的各种操作的性能进行分析。
(1)动态添加数据实验,我们采用倍增扩容的方法,随着问题规模指数级增长,操作时间上升愈渐缓和,较之等值扩容法,时间复杂度有着指数级的提升。
(2)输出顺序表长度的实验中,由于数据来源是结构体中的curlen,单独保存,所以算法时间复杂度上是O(1),与数据量无关。
(3)判断顺序表是否为空实验与上述实验相通,时间复杂度也是O(1)。
(4)获取特定位置元素值实验结果中,由于顺序表具有随机存取的特点,只要通过下标就能快速定位到需要的元素,所以时间复杂度为O(1),正因为如此,顺序表很适合用于需要频繁读取数据的系统中。
(5)获取元素首次出现的位置实验中,由于数据不同情况下具有不确定性,平均需要遍历n/2的元素,所以时间复杂度为O(n)。
(6)在特定位置上插入与删除元素的实验里,时间随着位置的后移而减少,这是由于删除/增加元素后需要移动的元素数量减少了。值得一提的是,删除元素后需要自前而后移动元素,增加元素前需要自后而前移动元素,否则会导致数据不断覆盖,后果很严重。
(7)历顺序表实验时间复杂度为O(n),时间大致与问题规模成正比,同时发现,数据量大时,遍历时间比预期要短许多。
(8)除前后1000个元素实验结果里,实验数据相差很大,因为删除前1000个元素当中每一个时,都要将后方元素前移,而删除尾部元素可以省却这些操作的时间。
(9)销毁顺序表中,清楚操作不会改变顺序表大小,list->data还能用,销毁操作则是释放空间,无法使用了。