【算法训练营】算法分析实验(递归实现斐波那契+插入排序、分治思想实现归并排序+快排)附代码+解析

![0](https://img-blog.csdnimg.cn/direct/4f61e5f998ac476d982ef3ac666da646.png)

🌈欢迎来到算法专栏
🙋🏾‍♀️作者介绍:前PLA队员 目前是一名普通本科大三的软件工程专业学生
🌏IP坐标:湖北武汉
🍉 目前技术栈:C/C++、Linux系统编程、计算机网络、数据结构、Mysql、Python(目前在学)
🍇 博客介绍:通过分享学习过程,加深知识点的掌握,也希望通过平台能认识更多同僚,如果觉得文章有帮助,请您动动发财手点点赞,本人水平有限,有不足之处欢迎大家扶正~
🍓 最后送大家一句话共勉:知不足而奋进,望远山而前行。愿大家都能早日进大厂实现财富自由~
————————————————

实验1-2

  • 1.斐波那契数列的递归算法实现
    • 1.1问题描述
    • 1.2算法分析
    • 1.3代码实现
    • 1.4结果分析
  • 2.插入排序问题的递归算法实现
    • 2.1、问题描述
    • 2.2、算法分析
    • 2.3代码实现
    • 2.4结果分析
  • 3.归并排序的分治算法实现
    • 3.1、问题描述
    • 3.2、算法分析
    • 3.3代码实现
    • 3.4结果分析
  • 4.快速排序问题的分治算法实现
    • 4.1、问题描述
    • 4.2、算法分析
    • 4.3 代码实现
    • 4.4结果分析

1.斐波那契数列的递归算法实现

1.1问题描述

斐波那契数列是一个经典的递归问题,其定义如下:
当 n = 0 时,F(0) = 0
当 n = 1 时,F(1) = 1
当 n > 1 时,F(n) = F(n-1) + F(n-2)

1.2算法分析

递归实现的思路就是将问题分解为更小的子问题,直到达到基本情况(递归终止条件),然后逐级返回结果。在斐波那契数列中,递归的思路是利用函数调用来计算前两个数的和。

1.3代码实现

#include <iostream>
#include <vector>void merge(std::vector<int>& arr, int left, int mid, int right) {int n1 = mid - left + 1;int n2 = right - mid;// 创建临时数组std::vector<int> leftArr(n1);std::vector<int> rightArr(n2);// 将数据复制到临时数组 leftArr 和 rightArrfor (int i = 0; i < n1; i++) {leftArr[i] = arr[left + i];}for (int j = 0; j < n2; j++) {rightArr[j] = arr[mid + 1 + j];}// 归并临时数组到 arr[left..right]int i = 0; // 初始化左子数组的索引int j = 0; // 初始化右子数组的索引int k = left; // 初始化合并数组的索引while (i < n1 && j < n2) {if (leftArr[i] <= rightArr[j]) {arr[k] = leftArr[i];i++;}else {arr[k] = rightArr[j];j++;}k++;}/*i、、jk 分别是用于迭代遍历左子数组、右子数组和合并后的数组的索引。
n1 和  分别是左右两个子数组的长度。n2
循环的条件 i < n1 && j < n2 确保只在左右两个子数组都有元素可供比较的情况下执行。
在循环内部,通过比较 leftArr[i] 和 rightArr[j] 的大小,将较小的元素复制到 arr 中,并相应地更新索引。
如果 leftArr[i] 小于等于 rightArr[j],则将 leftArr[i] 复制到 arr[k],并增加 i 的值。
如果 rightArr[j] 小于 leftArr[i],则将 rightArr[j] 复制到 arr[k],并增加 j 的值。
无论是哪种情况,都会增加 k 的值,将合并后的数组的索引向前移动。
这个过程重复进行,直到其中一个子数组的所有元素都被合并到 arr 中。
然后,如果左子数组或右子数组中还有元素未合并,将其余元素依次复制到 arr 中。
最终,整个过程确保 arr 成为一个有序的合并数组。*/// 将剩余的元素复制到 arrwhile (i < n1) {arr[k] = leftArr[i];i++;k++;}while (j < n2) {arr[k] = rightArr[j];j++;k++;}
}void mergeSort(std::vector<int>& arr, int left, int right) {if (left < right) {// 寻找中间点int mid = left + (right - left) / 2;// 递归排序左半部分和右半部分mergeSort(arr, left, mid);mergeSort(arr, mid + 1, right);// 合并两个已排序的子数组merge(arr, left, mid, right);}
}
//
//int main() {
//    std::vector<int> arr = { 12, 11, 13, 5, 6, 7 };
//
//    std::cout << "Original array: ";
//    for (int num : arr) {
//        std::cout << num << " ";
//    }
//    std::cout << std::endl;
//
//    // 调用归并排序
//    mergeSort(arr, 0, arr.size() - 1);
//
//    std::cout << "Sorted array: ";
//    for (int num : arr) {
//        std::cout << num << " ";
//    }
//    std::cout << std::endl;
//
//    return 0;
//}

1.4结果分析

运行结果:
Enter the value of n: 6
F(6) = 8。
分析:
递归实现可能会在计算过程中重复计算相同的子问题,导致效率较低。为了提高效率,可以考虑使用动态规划或者迭代的方式实现斐波那契数列。

2.插入排序问题的递归算法实现

2.1、问题描述

插入排序的递归算法思路是将一个元素插入到已经排好序的数组中,递归地重复这个过程。

2.2、算法分析

插入排序的递归算法的逻辑主要涉及两个部分:递归排序和插入操作。
2.1.递归
递归的终止条件是数组长度小于等于1,因为一个元素的数组已经是排好序的。
在递归调用中,将前 n-1 个元素进行排序,确保它们是按照递增顺序排列的。
2.2.插入操作部
当递归返回时,我们考虑最后一个元素(第 n 个元素),并将其插入到前 n-1 个已经排好序的元素中。
我们将最后一个元素的值存储在变量key中。
使用一个循环找到正确的插入位置,并将比 'key
最终,在正确的位置插入key,使得前 n 个元素仍然是排好序的。
通过递归地应用这个过程,最终整个数组被排序。算法的时间复杂度为 O(n^2),其中 n 是数组的长度。这是因为每个元素都需要与之前的元素进行比较,并进行插入操作

2.3代码实现

#include <iostream>
#include <vector>void insertRecursive(std::vector<int>& arr, int n) {// 递归终止条件if (n <= 1) {return;}// 递归调用,将前 n-1 个元素插入排序insertRecursive(arr, n - 1);// 将最后一个元素插入到已排好序的部分int key = arr[n - 1];int j = n - 2;// 移动比 key 大的元素向后while (j >= 0 && arr[j] > key) {arr[j + 1] = arr[j];j--;}// 插入 key 到正确的位置arr[j + 1] = key;
}//int main() {
//    std::vector<int> arr = { 12, 11, 13, 5, 6 };
//
//    std::cout << "Original array: ";
//    for (int num : arr) {
//        std::cout << num << " ";
//    }
//    std::cout << std::endl;
//
//    // 调用插入排序的递归函数
//    insertRecursive(arr, arr.size());
//    std::cout << "Sorted array: ";
//    for (int num : arr) {
//        std::cout << num << " ";
//    }
//    std::cout << std::endl;
//
//    return 0;
//}

2.4结果分析

结果:
Original array: 12 11 13 5 6
Sorted array: 5 6 11 12 13
分析:
插入排序在实践中对小规模数组和部分有序的数组是有效的,但对于大规模乱序数组来说,更高效的排序算法(例如快速排序、归并排序)通常更具优势。

3.归并排序的分治算法实现

3.1、问题描述

归并排序是一种分治算法,其基本思想是将一个大问题分解成两个较小的子问题,然后递归地解决这两个子问题,最后将它们的解合并起来。

3.2、算法分析

归并排序是一种经典的分治算法,其实现逻辑可以分为三个主要步骤:分解、解决、合并。

  1. 分解(Divide): - 将待排序的数组分解成两个子数组,这是递归的基本情况。 - 寻找数组的中间点,即 mid = left + (right - left) / 2。 - 递归地对左半部分(leftmid)和右半部分(mid + 1right)进行分解。
  2. 解决(Conquer): - 递归地对左半部分和右半部分进行排序。
  3. 合并(Combine): - 合并两个已排序的子数组,以产生一个新的有序数组。 - merge 函数用于合并,它比较两个子数组的元素,并将它们按照顺序合并到一个临时数组中。 - 将合并后的结果复制回原始数组的相应位置。 整个过程递归地重复执行分解、解决、合并的步骤,直到基本情况下,即子数组长度为1。在这个基本情况下,每个子数组都被视为已经排好序,然后通过合并操作合并为更大的有序数组。 下面是每个步骤的关键实现点:
  • 分解: 递归划分数组为左右两半。
  • 解决: 递归对左右两半进行排序。
  • 合并: 合并两个有序数组。 整个过程保证了每次合并都是在有序的基础上进行,最终得到整个数组的有序排列。算法的时间复杂度为 O(n log n),其中 n 是数组的长度。

3.3代码实现

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>int fibonacci(int n) {// 递归终止条件if (n == 0) {return 0;}else if (n == 1) {return 1;}// 递归调用,将问题分解为子问题return fibonacci(n - 1) + fibonacci(n - 2);
}//int main() {
//    int n;
//    std::cout << "Enter the value of n: ";
//    std::cin >> n;
//
//    // 输出斐波那契数列的第 n 项
//    std::cout << "F(" << n << ") = " << fibonacci(n) << std::endl;
//
//    return 0;
//}

3.4结果分析

Original array: 12 11 13 5 6 7
Sorted array: 5 6 7 11 12 13

分析:

归并排序具有一些重要的优点,这些优点使得它在某些场景下成为一种非常有效的排序算法: 1. 稳定性: 归并排序是一种稳定的排序算法,即对于相等的元素,排序前后它们的相对顺序保持不变。这对某些应用是很重要的,例如在对具有相同排序键的记录进行多次排序时。 2. 适用于链表: 归并排序天然适用于链表数据结构,不需要额外的空间来存储中间结果。相比之下,其他一些排序算法,比如快速排序,对链表的性能可能不如对数组的性能。 3. 稳定的时间复杂度: 归并排序在最坏、平均和最好情况下的时间复杂度都是 O(n log n),这使得它在大多数情况下都表现得相当稳定,不受输入数据的影响。 4. 可并行化: 归并排序是一种天然可以并行化的排序算法。由于其分治的性质,可以轻松地将数据划分为多个部分,分别进行排序,然后合并结果。这使得归并排序在多核和分布式系统中有良好的性能表现。 5. 不依赖于初始状态: 归并排序对于输入数据的初始状态不敏感,它总是以相同的时间复杂度运行。这与某些其他排序算法,比如快速排序,有着不同的特点。 尽管归并排序具有这些优点,但它也有一些缺点,主要是它需要额外的存储空间(与输入数据大小成正比)。这使得在内存有限的情况下,归并排序的应用可能受到限制。但总体来说,归并排序是一种非常稳定且通用的排序算法

4.快速排序问题的分治算法实现

4.1、问题描述

快速排序是一种高效的分治排序算法,其核心思想是选择一个基准元素,将数组分成两部分,使得左边的元素都小于等于基准元素,右边的元素都大于等于基准元素,然后对左右两部分分别递归地应用相同的过程。

4.2、算法分析

  1. 选择一个基准元素并通过 partition 将数组分为两部分。
  2. 对分割出的两个子数组递归地应用相同的过程,直到每个子数组的大小为1。
  3. 最终,整个数组就被排序好了,因为每个元素都参与了比较和交换的过程。
    整个过程是递归的,每次递归都会确定一个元素的最终位置,最终所有元素都被放置在正确的位置上,实现了整个数组的排序。算法的时间复杂度平均为 O(n log n),其中 n 是数组的长度。

4.3 代码实现

#include <iostream>
#include <vector>int partition(std::vector<int>& arr, int low, int high) {int pivot = arr[high]; // 选择数组最后一个元素作为基准int i = low - 1; // 初始化较小元素的索引for (int j = low; j < high; j++) {// 如果当前元素小于等于基准,将其与较小元素交换if (arr[j] <= pivot) {i++;std::swap(arr[i], arr[j]);}}// 将基准元素放到正确的位置std::swap(arr[i + 1], arr[high]);return i + 1;
}void quickSort(std::vector<int>& arr, int low, int high) {if (low < high) {// 找到基准元素的位置,使得左边元素都小于等于基准,右边元素都大于等于基准int pivotIndex = partition(arr, low, high);// 递归对基准元素左右两部分进行排序quickSort(arr, low, pivotIndex - 1);quickSort(arr, pivotIndex + 1, high);}
}//int main() {
//    std::vector<int> arr = { 12, 11, 13, 5, 6, 7 };
//
//    std::cout << "Original array: ";
//    for (int num : arr) {
//        std::cout << num << " ";
//    }
//    std::cout << std::endl;
//
//    // 调用快速排序
//    quickSort(arr, 0, arr.size() - 1);
//
//    std::cout << "Sorted array: ";
//    for (int num : arr) {
//        std::cout << num << " ";
//    }
//    std::cout << std::endl;
//
//    return 0;
//}

4.4结果分析

结果:
Original array: 12 11 13 5 6 7
Sorted array: 5 6 7 11 12 13
分析:
快速排序是一种高效的排序算法,具有以下一些优点: 1. 平均时间复杂度为 O(n log n): 在平均情况下,快速排序的时间复杂度是 O(n log n),这使得它在处理大规模数据时表现出色。 2. 原地排序: 快速排序是一种原地排序算法,不需要额外的辅助数组或存储空间。它通过在原始数组上进行交换和重排来实现排序,节省了内存空间。 3. 高效性: 在实际应用中,快速排序通常比其他 O(n log n) 复杂度的算法表现更好,因为它的常数因子较小。这在处理大规模数据时是一个重要的优势。 4. 稳定性: 由于是原地排序,相同元素的相对位置不会改变,因此快速排序是一种稳定的排序算法。 5. 适应性: 对于部分有序的数据,快速排序的表现也相当好。在这样的情况下,分割操作的代价相对较小,递归深度相对较小,

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/181774.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot : ch08 自动配置原理

前言 在现代的Java开发中&#xff0c;Spring Boot已经成为了一个备受欢迎的框架。它以其简化开发流程、提高效率和强大的功能而闻名&#xff0c;使得开发人员能够更加专注于业务逻辑的实现而不必过多地关注配置问题。 然而&#xff0c;你是否曾经好奇过Spring Boot是如何做到…

白盒测试 接口测试 自动化测试

一、什么是白盒测试 白盒测试是一种测试策略&#xff0c;这种策略允许我们检查程序的内部结构&#xff0c;对程序的逻辑结构进行检查&#xff0c;从中获取测试数据。白盒测试的对象基本是源程序&#xff0c;所以它又称为结构测试或逻辑驱动测试&#xff0c;白盒测试方法一般分为…

Python编程基础:数据类型和运算符解析

想要学习Python编程语言&#xff1f;本文将为您介绍Python中常见的数据类型和运算符&#xff0c;为您打下坚实的编程基础。了解不同的数据类型和运算符&#xff0c;掌握它们之间的配合方式&#xff0c;让您能够更轻松地进行数据处理和计算任务。无论您是初学者还是有一定经验的…

电能量数据采集终端是电表采集器吗?

随着科技的发展和能源管理的日益精细化&#xff0c;电能量数据采集终端——电表采集器在保障电力系统稳定运行、实现节能减排等方面发挥着越来越重要的作用。下面&#xff0c;小编来为大家全面介绍电表采集器的功能、应用场景及其在我国能源领域的价值。 一、电表采集器的定义与…

第二十章Java博客

如果一次只完成一件事情&#xff0c;很容易实现。但现实生活中&#xff0c;很多事情都是同时进行的。Java中为了模拟这种状态&#xff0c;引入了线程机制。简单地说&#xff0c;当程序同时完成多件事情时&#xff0c;就是所谓的多线程。多线程应用相当广泛&#xff0c;使用多线…

【Java学习笔记】 74 - 本章作业

1.验证电子邮件格式是否合法 规定电子邮件规则为 1.只能有一个 2. 前面是用户名,可以是a-z A-Z 0-9 _ - 字符 3. 后面是域名&#xff0c;并且域名只能是英文字母&#xff0c;比如sohu.com或者tsinghua.org.cn 4.写出对应的正则表达式&#xff0c;验证输入的字符串是否为满…

浏览器触发下载Excel文件-Java实现

目录 1:引入maven 2:代码实现 3.导出通讯录信息到Excel文件 4.生成并下载Excel文件部分解释 1:引入maven 添加依赖:首先,在你的项目中添加EasyExcel库的依赖。你可以在项目的构建文件(如Maven的pom.xml)中添加以下依赖项:<dependency><groupId>com.alib…

Python基础语法之学习input()函数

Python基础语法之学习input函数 前言一、代码二、效果 前言 一、代码 # 默认是字符串类型 number input("请输入一个数字&#xff1a;") print("输入的数字是",number)二、效果 没有人可以阻止你成为自己想成为的人&#xff0c;只有你自己才能放弃梦想。…

【LeetCode刷题笔记】160.相交链表

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 更多算法知识专栏&#xff1a;算法分析&#x1f525; 给大家跳段街舞感谢…

Spring(2):Spring事务管理机制

Spring事务管理高层抽象主要包括3个接口&#xff0c;Spring的事务主要是由他们共同完成的&#xff1a; PlatformTransactionManager&#xff1a;事务管理器—主要用于平台相关事务的管理。TransactionDefinition&#xff1a; 事务定义信息(隔离、传播、超时、只读)—通过配置如…

LeetCode算法题解(动态规划)|LeetCode198. 打家劫舍、LeetCode213. 打家劫舍 II、LeetCode337. 打家劫舍 III

一、LeetCode198. 打家劫舍 题目链接&#xff1a;198. 打家劫舍 题目描述&#xff1a; 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的…

哪里可了解低代码数据可视化开发平台?

如果想要提升办公协作效率&#xff0c;可以用什么样的平台助力实现这一目标&#xff1f;其实&#xff0c;随着市场竞争的日益加剧&#xff0c;低代码技术平台的应用价值也逐渐凸显出来&#xff0c;其可视化、易操作、灵活便利等优势特点&#xff0c;是很多中大型企业倾向于使用…

Nature子刊最新研究:Hi-C宏基因组揭示土壤-噬菌体-宿主相互作用

土壤中有大量的噬菌体。然而&#xff0c;大多数宿主未知&#xff0c;无法获得其基因组特征。2023年11月23日&#xff0c;最新发表于《Nature communications》期刊题为“Hi-C metagenome sequencing reveals soil phage–host interactions”的文章&#xff0c;通过高通量染色体…

2023 最新版navicat 下载与安装 步骤及演示 (图示版)

2023 最新版navicat 下载与安装 步骤演示 -图示版 1. 下载Navicat2 .安装navicat 160 博主 默语带您 Go to New World. ✍ 个人主页—— 默语 的博客&#x1f466;&#x1f3fb; 《java 面试题大全》 &#x1f369;惟余辈才疏学浅&#xff0c;临摹之作或有不妥之处&#xff0c…

时钟控制模块

时钟控制模块 锁相环电路简单的理解 https://www.bilibili.com/video/BV1yS4y1n7vV/?spm_id_from333.337.search-card.all.click&vd_source712cdb762d6632543eeeadb56271617a一 时钟是从哪里来的 时钟晶振&#xff08;32.768KHz&#xff09;供给RTC使用在IMX6ULL的T16和…

计算机图形学:直线的扫描转换算法解析与实现

直线的扫描转换&#xff1a; DDA算法&#xff1a; 推理&#xff1a; 在计算机显示图形时&#xff0c;由于显示计算机的分辨率是有限的所以我们在绘制图形时需要将图形从连续量转换成离散量才能完成图形的绘制&#xff0c;直线的扫描转换就是将连续量转换为离散量的过程。 对…

wsj0数据集原始文件.wv1.wv2转换成wav文件

文章目录 准备一、获取WSJO数据集二、安装sph2pipe三、转换代码四、结果展示 ​ 最近做语音分离实验需要用到wsj0-2mix数据集&#xff0c;但是从李宏毅语音分离教程里面获取的wsj0-2mix只有一部分。从网上获取到了完整的WSJO数据集后&#xff0c;由于原始的语音文件后缀是wv1或…

怎么在NAS里找照片?教你一招,精准定位

每次拍照 咔咔一顿拍 好多文档 咔咔一顿存 需要到的时候 却依稀只记得时间和部分关键词 那么怎么快速在NAS里精准定位 找到“命中注定”的它呢 嘿还真有 铁威马的Terra Search 精准搜索 快速定位 So easy&#xff01; 01 什么是Terra Search Terra Search 通过建立数据…

中国信通院发布《中国算力发展指数白皮书》(2023)

加gzh“大数据食铁兽”&#xff0c;回复“20231129”&#xff0c;获取材料完整版 导读 2023 年白皮书在 2022 年的基础上&#xff0c;加强了全球和我国算力发展的研究&#xff0c;客观评估我国整体、各省份及各城市现阶段的算力发展水平进一步给出我国算力二十强市榜单&…

网关路由器双栈配置中的IPv6相关选项解析

1、引言 讲知识往往是枯燥无味的&#xff0c;我们先从问题入手。家庭网关&#xff08;光猫&#xff09;、路由器是我们每个人或多或少都有所接触的2种设备。现在一般都是光纤入户&#xff0c;通常每个家庭配备一个光猫和一台家用路由器。 目前有许多网络服务已经提供了IPv6支…