数据结构 实验报告02

一、实验目的和要求

收集至少两位同学的的 作业-01 的代码,做代码比较分析。

1.事先设计比较方案。比较内容要涉及到排序中数据的  存储结构、数量、排序方法、代码之间的时间性能、空间性能等指标(但不局限于以上指标)的不同。做出比较方案的预分析结果。

2. 改造代码,保证代码比较的公平公正可比性。

3. 按比较方案实际运行代码,记录实验结果,和预分析结果做对比,再次进行分析总结,对存在的问题尝试提出解决方案。

4. 其它要求同作业-01要求。

二、实验环境

编译器:Vscode

系统:Windows10

CPU:i5-8265U@1.60GHz

数据:int范围

对比代码来源:15-wpf,26-lhy,86-ssh

三、实验内容

综合时间复杂度,空间复杂度,排序算法的稳定性,复杂度对多个代码进行优劣分析并改进

 

四、实验过程

4.1 任务定义和问题分析

问题分析:

  1. 四份代码之间代码风格不同,阅读起来会带来混乱感
  2. 四份代码统计的时间范畴不同
  3. 数据集大小,内容不同,生成方式不同
  4. 数据集读取方式不同
  5. 是否输出最后排序结果

 

任务定义:

  1. 仅对比算法运行时间  不考虑读入输出的耗时
  2. 统一代码风格
  3. 统一统计时间范畴
  4. 统一选择快读  缩减不必要时间
  5. 小数据(小于100000)输出排序结果,大数据情况下不输出排序结果,防止占用不必要内存(太大的数据结果也打不开)

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

        其实本次实验根本上就是对朴素冒泡,优化冒泡,插入排序,归并排序的优劣情况做对比(其实这些工作在我的第一次实验报告里已经粗略完成,下面给出之前做的比较,不止这三个算法(朴素冒泡和优化冒泡可以看作一个))

以下为个人实际测试:(针对三个大小为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

 

这次实验的比较准备以100000为数据集大小  三个数据集来进行对比

预计结果:

时间优劣方面上:归并排序最优,插入排序次之,冒泡排序最差

空间优劣方面上:冒泡和插入都差不多(用时间换空间)  归并排序较弱

稳定性上:三个算法都能保证排序过程中的稳定

复杂度上:冒泡最简单,插入次之,归并排序最复杂

4.3 详细设计

收集了三位同学的排序代码  

用的算法分别是  朴素冒泡排序,插入排序,优化后的冒泡排序。

加上我自己用的是归并排序

 

所用算法

时间复杂度

空间复杂度

稳定性

复杂性

平均情况

最好情况

最坏情况

辅助存储

Nice_try

归并排序

O(nlogn)

O(nlogn)

O(nlogn)

O(n)

稳定

复杂

wpf

朴素冒泡

O(n^2)

O(n^2)

O(n^2)

O(1)

稳定

简单

lhy

插入排序

O(n^2)

O(n)

O(n^2)

O(1)

稳定

简单

ssh

优化冒泡

O(n^2)

O(n)

O(n^2)

O(1)

稳定

简单

时间复杂度中的最好情况是指整个序列已处于有序情况下去跑

最坏情况是指整个序列处于倒序情况下去跑

关于排序稳定性的定义:

通俗地讲就是能保证排序前两个相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同。在简单形式化一下,如果Ai = Aj,Ai原来在位置前,排序后Ai还是要在Aj位置前。

在拿到三位同学代码后 对比了一下我们几个的实验一的结果

 

数据集最大值

完成排序所需时间

是否统计除排序以外的时间

如何读入数据集

Nice_try

498844223

106s

文件流

wpf

5000

1.115s

主函数内随机生成

lhy

100000

27s

主函数内随机生成

ssh

400000

未记录成功

主函数内随机生成

不难发现  我们之间代码风格  统计等等方面存在着差异

首先初步对比了wpf和ssh的代码,可以判定 ssh算法是wpf算法的升级优化版本

下面给出佐证:

for (int j = 0; j < 4000000; j++)

    {

        for (int t = j + 1; t < 4000000; t++)
for (int i = 0; i < n; i++)

    {

        for (int j = 0; j < n; j++)

 

这是两份代码中唯一有区别的地方  不难看出第一个代码比第二个代码在第二层循环中优化了一个常数

所以可以暂时丢弃弱版本的冒泡排序  

接下来仅进行对冒泡(优化),插入,归并排序的实验记录

首先第一步  在不改变代码运算效率的前提下统一三份代码格式

(比较看出冒泡排序可以用上次实验使用的版本  大同小异)

 

算法

The first data

The second data

The third data

归并

16ms

15ms

17ms

冒泡

28952ms

29014ms

29169ms

插入

6323ms

6374ms

6379ms

做比较到这里  能够看出时间复杂度上  归并最优 插入次之   冒泡最弱

再看空间花费  冒泡和插入一般无二 都是开了一个数组来存储数据  

然而归并却申请了动态数组将花费提到了O(n).

复杂程度上  归并操作较多  本人代码将近100行  而另外两种算法代码不过50行   归并还有分治思想需要去领会   较难上手

 

下面给出输出界面示例(仅输出了前100位的元素)

做比较到这里  能够看出时间复杂度上  归并最优 插入次之   冒泡最弱

 

 

再看空间花费  冒泡和插入一般无二 都是开了一个数组来存储数据  

然而归并却申请了动态数组将花费提到了O(n).

复杂程度上  归并操作较多  本人代码将近100行  而另外两种算法代码不过50行   归并还有分治思想需要去领会   较难上手

 

 

                           算法名称

 

所能承受的数组大小

 

归并排序

 

498844223

 

冒泡排序

 

498846271

 

插入排序

 

498846271

这个结果也可以佐证三者之间空间花费比较  冒泡和插入都差不多(用时间换空间)  归并排序花费较多

实际测试了一番发现  会出现这种报错

terminate called after throwing an instance of 'std::bad_alloc'

  what():  std::bad_alloc

超出了内存   运行程序过程中 cpu和内存都占到了98%以上  差点宕机

内存泄漏  没有及时释放内存

(看来可能是硬件性能问题)

等到虚拟机对cpu占用结束后  我又重新运行代码  得到了结果

 

数据集大小

所耗时间

归并

 

 

 

498844223

 

106s

插入

 

超时未测出

冒泡

 

超时未测出

占用内存一直不发生变化(暂定卡死)

五、测试及结果分析

5.1 实验数据

数据还是使用上次实验使用的小数据100000大小

已上传至本人gitee仓库

5.2 结果及分析

和预想的结果一般无二

时间优劣方面上:归并排序最优,插入排序次之,冒泡排序最差

空间优劣方面上:冒泡和插入都差不多(用时间换空间)  归并排序较弱

稳定性上:三个算法都能保证排序过程中的稳定

复杂度上:冒泡最简单,插入次之,归并排序最复杂

至于要将插入排序和冒泡排序进一步优化  我认为并不可行

毕竟他们的先天条件就在这(时间复杂度)优化的话就是换算法 (牺牲空间来换取时间)

六、实验收获

做好实验记录(数据要上传至云端)

小细节:

将所有代码用文件流输入  要想在vscode中显示出

单纯的system(“pause”);已经无法停留界面  于是我在代码最后追加了一个死循环  成功记录下了代码运行成功的界面

七、参考文献

本人博客

八、附录(源代码)

仅提供代码主干;

插入排序主干

void InsertSort(int R[], int n)
{
    int i, j;
    int tmp;
    for (i = 1; i < n; i++)
    {
        if (R[i] < R[i - 1])
        {
            tmp = R[i];
            j = i - 1;
            do
            {
                R[j + 1] = R[j];
                j--;
            } while (j >= 0 && R[j] > tmp);
            R[j + 1] = tmp;
        }
    }
}

冒泡排序主要内容

 for (int j = 0; j < n; j++)
        for (int t = j + 1; t < n; t++)
            if (s[j] > s[t])
                swap(s[j], s[t]);

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是数据结构二叉树的实验报告。 一、实验目的 1. 了解二叉树的概念、性质和基本操作; 2. 掌握二叉树的遍历算法; 3. 实现二叉树的建立、遍历和查找等基本操作。 二、实验原理 二叉树是一种树形结构,它的每个节点最多有两个子节点。二叉树的性质如下: 1. 每个节点最多有两个子节点,分别称为左子节点和右子节点; 2. 左子树和右子树都是二叉树; 3. 空树也是一棵二叉树。 二叉树的遍历有三种方式: 1. 先序遍历:先访问根节点,然后遍历左子树,最后遍历右子树; 2. 中序遍历:先遍历左子树,然后访问根节点,最后遍历右子树; 3. 后序遍历:先遍历左子树,然后遍历右子树,最后访问根节点。 三、实验步骤 本次实验我们将实现二叉树的建立、遍历和查找等基本操作。 1. 定义二叉树结构体。 ```c typedef struct Node { int data; struct Node *left; struct Node *right; } Node, *pNode; ``` 2. 实现二叉树的创建函数。 ```c pNode createTree() { int data; scanf("%d", &data); if(data == -1) { return NULL; } else { pNode node = (pNode)malloc(sizeof(Node)); node->data = data; node->left = createTree(); node->right = createTree(); return node; } } ``` 3. 实现二叉树的先序遍历函数。 ```c void preOrder(pNode node) { if(node != NULL) { printf("%d ", node->data); preOrder(node->left); preOrder(node->right); } } ``` 4. 实现二叉树的中序遍历函数。 ```c void inOrder(pNode node) { if(node != NULL) { inOrder(node->left); printf("%d ", node->data); inOrder(node->right); } } ``` 5. 实现二叉树的后序遍历函数。 ```c void postOrder(pNode node) { if(node != NULL) { postOrder(node->left); postOrder(node->right); printf("%d ", node->data); } } ``` 6. 实现二叉树的查找函数。 ```c pNode search(pNode node, int data) { if(node == NULL) { return NULL; } else if(node->data == data) { return node; } else if(node->data > data) { return search(node->left, data); } else { return search(node->right, data); } } ``` 7. 编写主函数进行测试。 ```c int main() { pNode root = createTree(); printf("先序遍历结果:"); preOrder(root); printf("\n中序遍历结果:"); inOrder(root); printf("\n后序遍历结果:"); postOrder(root); printf("\n请输入要查找的节点值:"); int data; scanf("%d", &data); pNode node = search(root, data); if(node == NULL) { printf("未找到该节点!"); } else { printf("已找到该节点,节点值为:%d", node->data); } return 0; } ``` 四、实验结果 经过测试,程序能够正确地实现二叉树的建立、遍历和查找等基本操作。 五、实验总结 通过本次实验,我们深入理解了二叉树的概念、性质和基本操作,并实现了二叉树的建立、遍历和查找等基本操作。同时,也加深了对指针和动态内存分配的理解。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值