数据结构 实验报告01

一、实验目的和要求

完成尽可能多的数据排序,并显示运行时间。

二、实验环境

编译器:Vscode  DevC++ 

系统:Windows10

CPU:i5-8265U@1.60GHz

数据:int范围(排除桶排序,基数排序,计数排序)

三、实验内容

1. 选择合适的排序算法

2. 确定可排序数据长度最大值

3. 固定可排序数据内容

4. 显示运行时间

四、实验过程

4.1 任务定义和问题分析

当前任务分析:

1. 从什么方面去选择排序算法

2. 如何确定可排序数据大小的上限

3. 不同变量对实验的影响

 

问题分析:

  1. 对于排序算法的选择应从实验目的出发

为了处理尽可能多的数据  要求我们考虑数据很多时的情况

应选择时间,空间复杂度较小,足够稳定,能够在处理大数据时拥有较好的性能的算法

  1. 根据经验,首先确定一个大致范围,给设定上界(足够大,大到使算法崩溃)与下界(足够小,小到算法无障碍运行),然后利用二分来确定合适的边界
  2. 本实验的变量有数据集的大小,编译环境(不同编译器),不同排序算法,相同大小数据集的复杂程度(小数据可以进行设计,数据过大时无能为力,这就需要考虑算法的稳定性)。

4.2 数据结构的选择和概要设计

选择归并排序进行实验(内部实现还是依靠数组)

利用二分法来寻找数据上限(一步步逼近极限)

寻找足够多的变量进行对照实验  以期实验可靠性

 

4.3 详细设计

首先是进行对排序算法的选择 可以根据排序算法的时间复杂度和空间复杂来进行挑选

因为各种排序算法的对数据的存储处理手段大都为数组 所以空间复杂度均为O(n),故算法的挑选在于时间复杂度的不同。

下面是一个总的表格,大致总结了我们常见的所有的排序算法的特点。

排序法

平均时间

最差情形

稳定度

额外空间

备注

冒泡

O(n2)

    O(n2)

稳定

O(1)

n小时较好

交换

    O(n2)

    O(n2)

不稳定

O(1)

n小时较好

选择

O(n2)

O(n2)

不稳定

O(1)

n小时较好

插入

O(n2)

O(n2)

稳定

O(1)

大部分已排序时较好

基数

O(nlogRB)

O(nlogRB)

稳定

O(n)

B是真数(0-9)

R是基数(个十百)

Shell

O(nlogn)

O(ns) 1<s<2

不稳定

O(1)

s是所选分组

快速

O(nlogn)

O(n2)

不稳定

O(nlogn)

n大时较好

归并

O(nlogn)

O(nlogn)

稳定

O(1)

n大时较好

O(nlogn)

O(nlogn)

不稳定

O(1)

n大时较好

(表格取自网络)

 

以下为个人实际测试:(针对三个大小为100000的固定的随机数集)

(已忽略数据的读写时间)

排序算法

第一个数据集所花费时间

第二个数据集所花费时间

第三个数据集所花费时间

Bubblesort(冒泡排序)

54317ms

58288ms

61413ms

Insertsort(插入排序)

32999ms

28839ms

29197ms

Shellsort(希尔排序)

53ms

77ms

73ms

Heapsort(堆排序)

46ms

38ms

36ms

Quicksort(快速排序)

24ms

27ms

28ms

Mergesort(归并排序)

21ms

21ms

25ms

 

依据以上两个表格,可以得知最优选择应为归并排序

 

原因有以下几点:

  1. 时间复杂度小
  2. 算法足够稳定
  3. 对于n较大的情况更优

 

敲定排序算法后将进行对可排序数据大小的测试

 

测试思路:使用二分法来寻找极限值

 

目前以100000(10^5)为较小值,2147483647(2^31-1)为较大值进行二分

针对同样的数据集将进行六次统计时间  并求出平均值

实验细节:

考虑到实际等待算法运行完毕的时间还包括数据的读入和输出,因此对于数据的读入使用快速读入,输出则使用printf 来减少等待时间(1亿规模下,算法仅25s左右,而实际需要等待近1分钟)

由于数据过于庞大(且电脑还有其他占内存东西在运作),所以在打开输入输出文件时容易崩溃,解决方案有三个:1. 关闭其他程序   2.仅统计时间,不对处理后的数据进行输出  3.使用电脑自带的程序来打开文件并等待

当数据过于庞大时,随机输出数据也是十分耗时,于是要使用输出优化。

上面是我尝试造2147483647(2^31-1)大小的数据集  结果内存险些炸了(本人电脑办公本,内存较少的那种)这还是程序没有跑完 所以换了小数据10^9

结果打不开  无法检验  先行默认正确(毕竟多少给点面子,不过有待验证,因为2*10^9(2^31-1)比110GB要大,但缩减到原来的1/20  却仅剩下4.43GB)

Dev中具有报错,但程序依旧运行(不知道是卡死了  还是在运行)

(经过时间的检验   大概可以判定是卡死)

网上搜索后得知

意思就是int数组开大了,开int数组特别是二维数组需要注意不超过10^8.

经过逼近测试 边界值为498844223

(这个数加一数组就爆了)

带上数据读入所用的时间  比较可观:3分钟左右

五、测试及结果分析

对各种数据运行程序和算法的结果记录和分析,并对错误所作的修改和结果。

针对不同算法 同样大小(100000)的数据集 进行时间测定

排序算法

第一个数据集所花费时间

第二个数据集所花费时间

第三个数据集所花费时间

Bubblesort(冒泡排序)

54317ms

58288ms

61413ms

Insertsort(插入排序)

32999ms

28839ms

29197ms

Shellsort(希尔排序)

53ms

77ms

73ms

Heapsort(堆排序)

46ms

38ms

36ms

Quicksort(快速排序)

24ms

27ms

28ms

Mergesort(归并排序)

21ms

21ms

25ms

针对同样的数据集将进行六次统计时间  并求出平均值

(编译器为vscode)

数据集大小

10^8

(560MB)

2^31-1

10^9

5*10^8

……

498844223

(2GB)

1

25960

失败(造数据阶段就失败了)

失败(申请数组阶段失败)

失败(申请数组阶段失败)

失败(申请数组阶段失败)

111234

2

27346

104743

3

29321

105939

4

27045

104062

5

27274

105212

6

28546

104762

平均

27582

105992

(编译器为dev)各个数据集同上

数据集大小

10^8

560(MB)

2^31-1

10^9

5*10^8

……

498844223

(2GB)

1

29098

失败(造数据阶段就失败了)

失败(申请数组阶段失败)

失败(申请数组阶段失败)

失败(申请数组阶段失败)

108798

2

29098

106985

3

27878

109460

4

25959

107411

5

25737

105682

6

27930

104540

平均

27617

107162.7

        实验的最终结果是

归并排序所能排序的数据最大个数是498844223

所花费时间为:106s

5.1 实验数据

三个100000大小的数据在下面网站data文件内,各排序算法源码在source code 文件夹下

https://gitee.com/Nicet/Data-Structure/tree/master/The%20First%20Test%20

由于10^8的数据文件大小有539MB  暂无法上传

(当然了,大于10^8的498844223的数据文件有2.16GB  所以更是无法上传)

5.2 结果及分析

     我认为,实际算法是可以处理数据的最多不止到498844223  只不过本笔记本或编译器设置内存上限小。

        不过就实验过程来看   可以说结果就是498844223

        就和实验前面提到的这是数组在本机上可以申请到的最大值(int)

六、实验收获

分析数组大小时的收获

1. 函数内申请的变量,数组,是在栈(stack)中申请的一段连续的空间。栈的默认大小为2M或1M,开的比较小。

2. 全局变量,全局数组,静态数组(static)则是开在全局区(静态区)(static)。大小为2G,所以能够开的很大。

3. 而malloc、new出的空间,则是开在堆(heap)的一段不连续的空间。理论上则是硬盘大小。

实验过程有一个长达一天半的小插曲

在认为printf过慢是我调用了输出优化来生成数据  结果忘了输出元素之间的空格 导致程序卡死

下面当时我对这种奇异现象的实验报告描述

甚至我都连实验结果写了

这个是因为10^8之后的数据都忘了生成空格了  

而且由于数据庞大 无法打开检验   

直到看到10^8+1的数据大小比10^8的还要小的时候,我意识到了不对  

打开看了看  发现没有空格 才恍然大悟

翻工重做

边界数据:498844223

这才对------之前一直没有占据很大内存

不过  没多大会  数据变动成了

这下好了  cpu都不占用了   

不过耐心的我还是等了下去  

后来我把应用程序打开一看:

结束了已经跑出来结果了

七、参考文献

各种排序算法总结

C / C++ 计算程序运行的时间

超详细十大经典排序算法总结(java代码)c或者cpp的也可以明白

八、附录(源代码)

下面将贴上主要代码:

void Merge(int arr[], int aux[], int left, int mid, int right)

{

    int i = left;

    int j = mid + 1;

    int k = left;



    while (i <= mid && j <= right)

    {

        if (arr[i] > arr[j])

        {

            aux[k++] = arr[j++];

        }

        else

        {

            aux[k++] = arr[i++];

        }

    }

    while (i <= mid)

    {

        aux[k++] = arr[i++];

    }

    while (j <= right)

    {

        aux[k++] = arr[j++];

    }

    for (int i = left; i <= right; i++)

    {

        arr[i] = aux[i];

    }

}

void MergeSort(int arr[], int aux[], int left, int right)

{

    if (left < right)

    {

        int mid = left + (right - left) / 2;

        MergeSort(arr, aux, left, mid);

        MergeSort(arr, aux, mid + 1, right);

        Merge(arr, aux, left, mid, right);

    }

}

void MergeSort(int arr[], int length)

{

    clock_t start, end;

    start = clock();

    int *aux = new int[length];

    MergeSort(arr, aux, 0, length - 1);

    delete[] aux;

    end = clock();

    cout << end - start << "\n";

    // for (int i = 0; i < length; i++)

    //     printf("%d ", arr[i]);

}

以上是本实验所选算法--------归并排序

下面这个函数是本实验所选读入优化

void read(int &x)

{

    int f = 1;

    x = 0;

    char s = getchar();

    while (s < '0' || s > '9')

    {

        if (s == '-')

            f = -1;

        s = getchar();

    }

    while (s >= '0' && s <= '9')

    {

        x = x * 10 + s - '0';

        s = getchar();

    }

    x *= f;

}

下面这个函数是本实验为减少造数据时间所选的输出优化

void print(int x) 

{

    if (x > 9)

        print(x / 10);

    putchar(x % 10 + '0');

}

其余算法的code请移步到gitee上clone

很多图片上传失败   如果认为阅读有障碍 请移步到gitee上下载观看

实验1:顺序表基本操作 一、实验目的 1.学会定义线性表的顺序存储类型,实现C程序的基本结构,对线性表的一些基本操作和具体的函数定义。 2.掌握顺序表的基本操作,实现顺序表的插入、删除、查找以及求并集等运算。 3.掌握对多函数程序的输入、编辑、调试和运行过程。 二、实验要求 1.预习C语言中结构体的定义与基本操作方法。 2.对顺序表的每个基本操作用单独的函数实现。 3.编写完整程序完成下面的实验内容并上机运行。 4.整理并上交实验报告。 三、实验内容: 1.编写程序实现顺序表的下列基本操作: (1)初始化顺序表La。 (2)将La置为空表。 (3)销毁La。 (4)在La中插入一个新的元素。 (5)删除La中的某一元素。 (6)在La中查找某元素,若找到,则返回它在La中第一次出现的位置,否则返回0。 (7)打印输出La中的元素值。 2.编写程序完成下面的操作: (1)构造两个顺序线性表La和Lb,其元素都按值非递减顺序排列。 (2)实现归并La和Lb得到新的顺序表Lc,Lc的元素也按值非递减顺序排列。 (3)假设两个顺序线性表La和Lb分别表示两个集合A和B,利用 union_Sq操作实现A=A∪B。 四、思考与提高 假设两个顺序线性表La和Lb分别表示两个集合A和B,如何实现A=A ∩B ? 实验2:单链表基本操作 一、 实验目的 1. 学会定义单链表的结点类型,实现对单链表的一些基本操作和具体的函数定义,了解并掌握单链表的类定义以及成员函数的定义与调用。 2. 掌握单链表基本操作及两个有序表归并、单链表逆置等操作的实现。 二 、实验要求 1.预习C语言中结构体的定义与基本操作方法。 2.对单链表的每个基本操作用单独的函数实现。 3.编写完整程序完成下面的实验内容并上机运行。 4.整理并上交实验报告。 三、实验内容 1.编写程序完成单链表的下列基本操作: (1)初始化单链表La。 (2)在La中第i个元素之前插入一个新结点。 (3)删除La中的第i个元素结点。 (4)在La中查找某结点并返回其位置。 (5)打印输出La中的结点元素值。 2 .构造两个带有表头结点的有序单链表La、Lb,编写程序实现将La、Lb合并成一个有序单链表Lc。 合并思想是:程序需要3个指针:pa、pb、pc,其中pa,pb分别指向La表与Lb表中当前待比较插入的结点,pc 指向Lc表中当前最后一个结点。依次扫描La和Lb中的元素,比较当前元素的值,将较小者链接到*pc之后,如此重复直到La或Lb结束为止,再将另一个链表余下的内容链接到pc所指的结点之后。 3.构造一个单链表L,其头结点指针为head,编写程序实现将L逆置。(即最后一个结点变成第一个结点,原来倒数第二个结点变成第二个结点,如此等等。) 四、思考与提高 1.如果上面实验内容2中合并的表内不允许有重复的数据该如何操作? 2.如何将一个带头结点的单链表La分解成两个同样结构的单链表Lb,Lc,使得Lb中只含La表中奇数结点,Lc中含有La表的偶数结点? 实验3:循环队列基本操作 一 、实验目的 1.熟悉并能实现循环队列的定义和基本操作。 2.了解用队列解决实际应用问题。 二、实验要求 1.进行队列的基本操作时要注意队列“先进先出”的特性。 2.复习关于队列操作的基础知识。 3.编写完整程序完成下面的实验内容并上机运行。 4.整理并上交实验报告。 三、实验内容 1.任意输入队列长度和队列中的元素值,构造一个顺序循环队列,对其进行清空、插入新元素、返回队头元素以及删除队头元素操作。 2.约瑟夫环的实现:设有n个人围坐在圆桌周围,现从某个位置 i 上的人开始报数,数到 m 的人就站出来。下一个人,即原来的第m+1个位置上的人,又从1开始报数,再是数到m的人站出来。依次重复下去,直到全部的人都站出来,按出列的先后又可得到一个新的序列。由于该问题是由古罗马著名的史学家Josephus提出的问题演变而来,所以通常称为Josephus 问题。 例如:当n=8,m=4,i=1时,得到的新序列为: 4,8,5,2,1,3,7,6 编写程序选择循环队列作为存储结构模拟整个过程,并依次输出出列的各人的编号。 实验4:矩阵的压缩存储及相关操作 (第11周星期三7、8节) 一 、实验目的 1.掌握下三角矩阵的输入、输出、转置算法。 2.理解稀疏矩阵的三元组表类型定义,掌握稀疏矩阵的输入、输出、转置算法。 二 、实验要求 1.认真阅读和掌握本实验的算法思想。 2.编写完整程序完成下面的实验内容并上机运行。 三、实验内容 1.所谓上(下)三角矩阵是指矩阵的下(上)三角中的元素均为常数或零的n阶矩阵。此时除了存储上(下)三角矩阵中的元素之外再加一个存储常数的空间即可。三角矩阵中的重复元素c可共享一个存储空间,其余的元素正好有n×(n+1)/2个,因此,三角矩阵可压缩到向量Sa[0……n×(n+1)/2]中,其中c存放在向量的最后一个分量中。用向量Sa[0……n×(n+1)/2]压缩存储下三角矩阵,编写程序任意输入一个下三角矩阵,对其进行转置,输出转置后的矩阵。 2.用三元组顺序表压缩存储稀疏矩阵,编写程序任意输入一个稀疏矩阵,对其进行转置,输出转置后的矩阵。 四、思考与提高 如何计算一个三元组表表示的稀疏矩阵对角线元素之和以及两个三元组表表示的稀疏矩阵的乘积? 实验5:二叉树的建立及遍历 (第十三周星期三7、8节) 一 、实验目的 1.学会实现二叉树结点结构和对二叉树的基本操作。 2.掌握对二叉树每种操作的具体实现,学会利用递归方法编写对二叉树这种递归数据结构进行处理的算法。 二 、实验要求 1.认真阅读和掌握和本实验相关的教材内容。 2.编写完整程序完成下面的实验内容并上机运行。 3.整理并上交实验报告。 三、实验内容 1.编写程序任意输入二叉树的结点个数和结点值,构造一棵二叉树,采用三种递归遍历算法(前序、中序、后序)对这棵二叉树进行遍历并计算出二叉树的高度。 2 .编写程序生成下面所示的二叉树,并采用中序遍历的非递归算法对此二叉树进行遍历 四、思考与提高 1.如何计算二叉链表存储的二叉树中度数为1的结点数? 2.已知有—棵以二叉链表存储的二叉树,root指向根结点,p指向二叉树中任一结点,如何求从根结点到p所指结点之间的路径? 实验6:二分查找、Hash查找算法的程序实现 (第十五三周星期三7、8节) 一、 实验目的 1 .熟练掌握二分查找算法并能在有序表中进行查找操作。 2. 掌握Hash表的相关算法。 二 、实验要求 1.认真阅读和掌握和本实验相关的教材内容。 2.复习顺序表及二叉树的基本操作过程。 3.编写完整程序完成下面的实验内容并上机运行。 4.整理并上交实验报告。 三、实验内容 1.二分查找又称为折半查找,它要求要查找的顺序表必须是有序表,即表中结点按关键字有序.并且要用顺序存储结构。 基本思想是:首先将给定值key与表中中间位置记录的关键字相比较,若二者相等,则查找成功,否则根据比较的结果确定下次查找的范围是在中间记录的前半部分还是后半部分,然后在新的查找范围内进行同样的查找,如此重复下去,直到在表中找到关键字与给定值相等的记录,或者确定表中没有这样的记录。 编写程序构造一个有序表La,从键盘接收一个关键字key,用二分查找法在La 中查找key,若找到则提示查找成功并输出key所在的位置,否则提示没有找到信息。 2.编写程序实现Hash表的建立、删除、插入以及查找操作。 程序应包含的主要功能函数有: Hash( ):计算哈希地址 InitialHash( ):初始化哈希表 SearchHash( ):在哈希表中查找关键字 InsertHash( ):向哈希表中插入关键字 DeleteHash( ):删除哈希表中某一关键字 PrintHash ( ):打印输出哈希表 四、思考与提高 如何利用二分查找算法在一个有序表中插入一个元素x,并保持表的有序性? 实验7:至少三种排序算法的程序实现 (第十六周星期三7、8节) 一、 实验目的 1.掌握简单插入排序、冒泡排序、快速排序、堆排序以及归并排序的算法并加以应用。 2.对各种查找、排序技术的时间、空间复杂性有进一步认识。 二 、实验要求 1.认真阅读和掌握和本实验相关的教材内容。 2.编写完整程序完成下面的实验内容并上机运行。 3.整理并上交实验报告。 三、实验内容 编写程序实现下述五种算法至少三种,并用以下无序序列加以验证: 49,38,65,97,76,13,27,49 1.简单插入排序 2.冒泡排序 3.快速排序 4.归并排序 5.堆排序 四、思考与提高 1.设有1000个无序的元素,希望用最快的速度挑出其中前10个最大的元素,采用哪一种排序方法最好?为什么? 2.如何构造一种排序方法,使五个整数至多用七次比较就可以完成排序任务? 实验8:集成实验 一、 实验目的 目的:扩大编程量,完成模块化程序设计的全过程。 二 、实验要求 1.认真阅读和掌握和本实验相关的教材内容。 2.编写完整程序完成下面的实验内容并上机运行。 3.整理并上交实验报告。 三、实验内容 将已经实现的模块装配在一起,由菜单进行管理,形成一个小型多功能软件。 while(1){ menuList(); scanf(&n); switch(n){ case 1:……;break; case 2:……;break; . . case 0:return; } } 如: • MenuSelect( ){ int select; SqList A , B , C ; MenuList( ); /* 打印菜单 */ scanf(“%d”,&select;); switch(select){ case 1: InitList_Sq( &A ); break; case 2: InitList_Sq( &B ); break; case 3: Insert(&A);break; case 4: Insert(&B );break; case 5: Union(&A ,B);break; ………….. default: printf(“\n ERROR\n”); } } 四、思考与提高 1. 优化已完成的程序,使整个实验形成至少三级菜单管理; 2.美化输出界面,使得操作友好。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值