之前我们看过的算法 BottomUpSort 和 MergeSort,前者是迭代的,而后者是递归的。
在这里我们可以思考一下,既然能够利用算法 BottomUpSort,为什么还要借助于像 MergeSort 那样的递归算法呢?尤其是考虑到因使用栈而需要的额外空间数,以及由处理递归调用内在开销带来的额外空间。
而且从实践的观点来看,似乎没有理由赞成用递归算法替代其等价的迭代算法。
但是从理论的观点来看,递归算法具有对问题易于叙述、领会和分析等优点。为了理解这一点,我们可以对算法 MergeSort 和 BottomUpSort 的代码作比较,很明显后者需要花更多的时间调试和理解代码的含义。这启示我们设计算法可以从递归描述的框架着手,如果可能,以后再将算法改进并转化为一个迭代的算法。
每个递归算法总是可以被转换为迭代算法的,而且两者在解决问题每个实例时他们的功能是完全相同的。
点这里查看 ButtomUpSort
点这里查看 MergeSort
最后再用著名的 Fibonacci 序列来稍微比较一下递归和迭代所耗费的时间。
在数学上,Fibonacci 序列是用递归来定义的,它的性质如下所示:
F0 = 0
F1 = 1
F2 = 1
F3 = 2
F4 = 3
F5 = 5
.
.
.
Fn = Fn-1 + Fn-2
我们可以用 递归 和 迭代 两种方法来求解 Fibonacci(n) 的值,然后非常直观地比较这两种方法所耗费的时间。
以下这两个方法到了 index=140 的时候,会因为计算结果超过 decimal 类型的最大值而抛出异常
namespace ConsoleApplication.Fibonacci
{
public class Fibonacci
{
public static decimal FibonacciRecursion(int index)
{
if (index == 0)
return 0M;
else if (index == 1)
return 1M;
return FibonacciRecursion(index - 1) + FibonacciRecursion(index - 2);
}
public static decimal FibonacciIteration(int index)
{
if (index == 0)
return 0M;
else if (index == 1)
return 1M;
decimal x = 0M, y = 1M, sum = 0M;
int i = 2;
while (i <= index)
{
sum = x + y;
x = y;
y = sum;
i++;
}
return sum;
}
}
}
然后写一串测试代码来感觉一下花费的时间:
namespace ConsoleApplication
{
public class Program
{
static void Main(string[] args)
{
try
{
for (int i = 0; i < 10000; i++)
{
Console.WriteLine("Fibonacci({0}) = {1}",
i, Fibonacci.Fibonacci.FibonacciIteration(i));
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
try
{
for (int i = 0; i < 10000; i++)
{
Console.WriteLine("Fibonacci({0}) = {1}",
i, Fibonacci.Fibonacci.FibonacciRecursion(i));
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
}
}
程序运行截图
运行程序时,可以非常直观的察觉到用迭代方法来计算,结果都是瞬间计算出来的,
而递归算得却很慢,特别是当 index=33-35 时,几乎要花掉1秒钟来计算,而且越到后面需要的时间更是成指数增长
分享到:
相关推荐
本篇文章将概述几种常见的排序算法,包括快速排序、归并排序、堆排序、Shell排序、插入排序、冒泡排序、交换排序、选择排序以及基数排序。 1. **快速排序**(QuickSort): - 快速排序采用了分治策略,以一个支点...
实验涉及的主要算法包括二分搜索、合并排序以及可选的阶乘计算(分别用递归和分治方法实现)。使用的编程语言是Python。 分治算法是一种解决问题的策略,它将一个大问题分解成若干个规模较小、相互独立、与原问题...
快速排序算法是基于分治算法的思想,通过将数据分割成独立的两部分,然后递归地对这两部分数据进行快速排序,最后合并两部分的排序结果,得出整个数据的排序结果。 三、 Partition 函数 Partition 函数是快速排序...
2. **归并排序**:归并排序是基于分治法的排序算法,将数据不断分成两半,直到每个子序列仅包含一个元素,然后将这些元素按顺序合并回原序列。归并排序具有稳定性,但需要额外的空间来存储子序列。 3. **堆排序**:...
### 排序算法的稳定性和时间复杂度小结 #### 一、引言 排序算法是计算机科学中的基本算法之一,广泛应用于各种场景之中。排序算法不仅关注排序的速度(时间复杂度),还关注排序过程中是否能够保持相等元素原有的...
这篇博客“常用排序算法小结(附Java实现)”提供了一种深入理解并掌握常见排序算法的途径,尤其对于Java开发者来说非常实用。文章可能涵盖了如冒泡排序、选择排序、插入排序、快速排序、归并排序、堆排序等多种经典...
归并排序通过分治策略将大问题分解为小问题,然后合并解决。在对稳定性有要求且空间不是特别紧张的情况下,归并排序是不错的选择。 在选择排序算法时,需要综合考虑待排序数据的特性,如元素数量、元素分布、内存...
**基本原理**:归并排序也是一种基于分治法的排序算法,它将数组分成两半,递归地对这两半进行排序,然后将两个已排序的半部分合并成一个有序的整体。 **程序实现**:虽然在给定的代码片段中没有完整展示归并排序的...
5. **归并排序**:采用分治策略,将数组分为两半分别排序,然后合并。时间复杂度为O(n log n),需要额外的O(n)空间,因此不是原地排序,但它是稳定的排序算法。 6. **基数排序**:根据元素的每一位进行排序,从最低...
6. **归并排序**:归并排序通过分治将序列拆分成两半,然后递归地合并,保证了稳定性。时间复杂度在所有情况下都是O(n log n)。 7. **堆排序**:堆排序利用堆结构进行排序,不保证稳定性,时间复杂度为O(n log n)。 ...
4. **归并排序**:采用分治法策略,将数组分成两部分,分别对这两部分进行排序,然后合并为一个完整的有序数组。 5. **分配排序** - **桶排序**:通过将数组分到有限数量的桶里去实现排序。 - **基数排序**:一种...
本篇文章将详细解析Java中常见的排序方法,结合"javaeye 收集的java排序小结"资料,旨在帮助读者理解和掌握这些排序算法。 1. 冒泡排序(Bubble Sort) 冒泡排序是最简单的排序算法之一,通过重复遍历数组,比较...
7.10.3 简单算法 7.10.4 多路合并 7.10.5 多相合并 7.10.6 替换选择 小结 练习题 参考文献第8章 不相交集类 8.1 等价关系 8.2 动态等价性问题 8.3 基本数据结构 8.4 灵巧求并算法 8.5 路径压缩...
7.10.3 简单算法 7.10.4 多路合并 7.10.5 多相合并 7.10.6 替换选择 小结 练习题 参考文献第8章 不相交集类 8.1 等价关系 8.2 动态等价性问题 8.3 基本数据结构 8.4 灵巧求并算法 8.5 路径压缩...
4.8.2 合并排序算法示例 126 4.9 排序算法的效率 129 4.10 排序算法的其他应用 130 4.10.1 反序排序 130 4.10.2 字符串数组的排序 132 4.10.3 字符串的排序 135 4.11 小结 137 第5章 查找算法 138 5.1 查找...
在本项目“算法设计结课代码”中,包含了一些核心的算法实现,主要涉及动态规划、贪心算法、分治策略以及回溯法这四种重要算法。这些算法是计算机科学和软件工程中解决复杂问题的关键工具,对于提升程序效率和优化...
书的内容包括表、栈、队列、树、散列表、优先队列、排序、不相交集算法、图论算法、算法分析、算法设计、摊还分析、查找树算法、k-d树和配对堆等。本书适合作为计算机相关专业本科生的数据结构课程和研究生算法分析...