- 浏览: 147133 次
- 性别:
- 来自: 上海
-
文章分类
最新评论
-
Jonathan樊:
请LZ不要简单的Copy过来,好吧?起码编辑一下啊~~该加的超 ...
直接拿来用!最火的Android开源项目 -
tao71535:
学习了、
不过为还是觉得看视频做例子、
比较好、、
Intent总结
题目:定义Fibonacci数列如下: / 0 n=0
f(n)= 1 n=1
\ f(n-1)+f(n-2) n=2
输入n,用最快的方法求该数列的第n项。
分析:在很多C语言教科书中讲到递归函数的时候,都会用Fibonacci作为例子。因此很多程序员对这道题的递归解法非常熟悉,看到题目就能写出如下的递归求解的代码。
但是,教科书上反复用这个题目来讲解递归函数,并不能说明递归解法最适合这道题目。我们以求解f(10)作为例子来分析递归求解的过程。要求得f(10),需要求得f(9)和f(8)。同样,要求得f(9),要先求得f(8)和f(7)……我们用树形结构来表示这种依赖关系
f(10)
/ \
f(9) f(8)
/ \ / \
f(8) f(7) f(6) f(5)
/ \ / \
f(7) f(6) f(6) f(5)
我们不难发现在这棵树中有很多结点会重复的,而且重复的结点数会随着n的增大而急剧增加。这意味这计算量会随着n的增大而急剧增大。事实上,用递归方法计算的时间复杂度是以n的指数的方式递增的。大家可以求Fibonacci的第100项试试,感受一下这样递归会慢到什么程度。在我的机器上,连续运行了一个多小时也没有出来结果。
其实改进的方法并不复杂。上述方法之所以慢是因为重复的计算太多,只要避免重复计算就行了。比如我们可以把已经得到的数列中间项保存起来,如果下次需要计算的时候我们先查找一下,如果前面已经计算过了就不用再次计算了。
更简单的办法是从下往上计算,首先根据f(0)和f(1)算出f(2),在根据f(1)和f(2)算出f(3)……依此类推就可以算出第n项了。很容易理解,这种思路的时间复杂度是O(n)。
这还不是最快的方法。下面介绍一种时间复杂度是O(logn)的方法。在介绍这种方法之前,先介绍一个数学公式:
{f(n), f(n-1), f(n-1), f(n-2)} ={1, 1, 1,0}n-1
(注:{f(n+1), f(n), f(n), f(n-1)}表示一个矩阵。在矩阵中第一行第一列是f(n+1),第一行第二列是f(n),第二行第一列是f(n),第二行第二列是f(n-1)。)
有了这个公式,要求得f(n),我们只需要求得矩阵{1, 1, 1,0}的n-1次方,因为矩阵{1, 1, 1,0}的n-1次方的结果的第一行第一列就是f(n)。这个数学公式用数学归纳法不难证明。感兴趣的朋友不妨自己证明一下。
现在的问题转换为求矩阵{1, 1, 1, 0}的乘方。如果简单第从0开始循环,n次方将需要n次运算,并不比前面的方法要快。但我们可以考虑乘方的如下性质:
/ an/2*an/2 n为偶数时
an=
\ a(n-1)/2*a(n-1)/2 n为奇数时
要求得n次方,我们先求得n/2次方,再把n/2的结果平方一下。如果把求n次方的问题看成一个大问题,把求n/2看成一个较小的问题。这种把大问题分解成一个或多个小问题的思路我们称之为分治法。这样求n次方就只需要logn次运算了。
实现这种方式时,首先需要定义一个2×2的矩阵,并且定义好矩阵的乘法以及乘方运算。当这些运算定义好了之后,剩下的事情就变得非常简单。完整的实现代码如下所示。
f(n)= 1 n=1
\ f(n-1)+f(n-2) n=2
输入n,用最快的方法求该数列的第n项。
分析:在很多C语言教科书中讲到递归函数的时候,都会用Fibonacci作为例子。因此很多程序员对这道题的递归解法非常熟悉,看到题目就能写出如下的递归求解的代码。
/////////////////////////////////////////////////////////////////////// // Calculate the nth item of Fibonacci Series recursively /////////////////////////////////////////////////////////////////////// long long Fibonacci_Solution1(unsigned int n) { int result[2] = {0, 1}; if(n < 2) return result[n]; return Fibonacci_Solution1(n - 1) + Fibonacci_Solution1(n - 2); }
但是,教科书上反复用这个题目来讲解递归函数,并不能说明递归解法最适合这道题目。我们以求解f(10)作为例子来分析递归求解的过程。要求得f(10),需要求得f(9)和f(8)。同样,要求得f(9),要先求得f(8)和f(7)……我们用树形结构来表示这种依赖关系
f(10)
/ \
f(9) f(8)
/ \ / \
f(8) f(7) f(6) f(5)
/ \ / \
f(7) f(6) f(6) f(5)
我们不难发现在这棵树中有很多结点会重复的,而且重复的结点数会随着n的增大而急剧增加。这意味这计算量会随着n的增大而急剧增大。事实上,用递归方法计算的时间复杂度是以n的指数的方式递增的。大家可以求Fibonacci的第100项试试,感受一下这样递归会慢到什么程度。在我的机器上,连续运行了一个多小时也没有出来结果。
其实改进的方法并不复杂。上述方法之所以慢是因为重复的计算太多,只要避免重复计算就行了。比如我们可以把已经得到的数列中间项保存起来,如果下次需要计算的时候我们先查找一下,如果前面已经计算过了就不用再次计算了。
更简单的办法是从下往上计算,首先根据f(0)和f(1)算出f(2),在根据f(1)和f(2)算出f(3)……依此类推就可以算出第n项了。很容易理解,这种思路的时间复杂度是O(n)。
/////////////////////////////////////////////////////////////////////// // Calculate the nth item of Fibonacci Series iteratively /////////////////////////////////////////////////////////////////////// long long Fibonacci_Solution2(unsigned n) { int result[2] = {0, 1}; if(n < 2) return result[n]; long long fibNMinusOne = 1; long long fibNMinusTwo = 0; long long fibN = 0; for(unsigned int i = 2; i <= n; ++ i) { fibN = fibNMinusOne + fibNMinusTwo; fibNMinusTwo = fibNMinusOne; fibNMinusOne = fibN; } return fibN; }
这还不是最快的方法。下面介绍一种时间复杂度是O(logn)的方法。在介绍这种方法之前,先介绍一个数学公式:
{f(n), f(n-1), f(n-1), f(n-2)} ={1, 1, 1,0}n-1
(注:{f(n+1), f(n), f(n), f(n-1)}表示一个矩阵。在矩阵中第一行第一列是f(n+1),第一行第二列是f(n),第二行第一列是f(n),第二行第二列是f(n-1)。)
有了这个公式,要求得f(n),我们只需要求得矩阵{1, 1, 1,0}的n-1次方,因为矩阵{1, 1, 1,0}的n-1次方的结果的第一行第一列就是f(n)。这个数学公式用数学归纳法不难证明。感兴趣的朋友不妨自己证明一下。
现在的问题转换为求矩阵{1, 1, 1, 0}的乘方。如果简单第从0开始循环,n次方将需要n次运算,并不比前面的方法要快。但我们可以考虑乘方的如下性质:
/ an/2*an/2 n为偶数时
an=
\ a(n-1)/2*a(n-1)/2 n为奇数时
要求得n次方,我们先求得n/2次方,再把n/2的结果平方一下。如果把求n次方的问题看成一个大问题,把求n/2看成一个较小的问题。这种把大问题分解成一个或多个小问题的思路我们称之为分治法。这样求n次方就只需要logn次运算了。
实现这种方式时,首先需要定义一个2×2的矩阵,并且定义好矩阵的乘法以及乘方运算。当这些运算定义好了之后,剩下的事情就变得非常简单。完整的实现代码如下所示。
#include <cassert> /////////////////////////////////////////////////////////////////////// // A 2 by 2 matrix /////////////////////////////////////////////////////////////////////// struct Matrix2By2 { Matrix2By2 ( long long m00 = 0, long long m01 = 0, long long m10 = 0, long long m11 = 0 ) :m_00(m00), m_01(m01), m_10(m10), m_11(m11) { } long long m_00; long long m_01; long long m_10; long long m_11; }; /////////////////////////////////////////////////////////////////////// // Multiply two matrices // Input: matrix1 - the first matrix // matrix2 - the second matrix //Output: the production of two matrices /////////////////////////////////////////////////////////////////////// Matrix2By2 MatrixMultiply ( const Matrix2By2& matrix1, const Matrix2By2& matrix2 ) { return Matrix2By2( matrix1.m_00 * matrix2.m_00 + matrix1.m_01 * matrix2.m_10, matrix1.m_00 * matrix2.m_01 + matrix1.m_01 * matrix2.m_11, matrix1.m_10 * matrix2.m_00 + matrix1.m_11 * matrix2.m_10, matrix1.m_10 * matrix2.m_01 + matrix1.m_11 * matrix2.m_11); } /////////////////////////////////////////////////////////////////////// // The nth power of matrix // 1 1 // 1 0 /////////////////////////////////////////////////////////////////////// Matrix2By2 MatrixPower(unsigned int n) { assert(n > 0); Matrix2By2 matrix; if(n == 1) { matrix = Matrix2By2(1, 1, 1, 0); } else if(n % 2 == 0) { matrix = MatrixPower(n / 2); matrix = MatrixMultiply(matrix, matrix); } else if(n % 2 == 1) { matrix = MatrixPower((n - 1) / 2); matrix = MatrixMultiply(matrix, matrix); matrix = MatrixMultiply(matrix, Matrix2By2(1, 1, 1, 0)); } return matrix; } /////////////////////////////////////////////////////////////////////// // Calculate the nth item of Fibonacci Series using devide and conquer /////////////////////////////////////////////////////////////////////// long long Fibonacci_Solution3(unsigned int n) { int result[2] = {0, 1}; if(n < 2) return result[n]; Matrix2By2 PowerNMinus2 = MatrixPower(n - 1); return PowerNMinus2.m_00; }
发表评论
-
微软等数据结构+算法面试100题
2011-02-23 17:48 1980微软等100题系列V0.1版终于结束了。 从2010年10月 ... -
百度11月4日网上笔试题及答案
2010-10-27 22:27 1259编程: 用C语言实现一个revert函数,它的功能是将输入的字 ... -
程序员面试题精选(18)-用两个栈实现队列
2009-08-15 12:01 1949题目:某队列的声明如 ... -
程序员面试题精选(17)-把字符串转换成整数
2009-08-15 12:00 2435题目:输入一个表示整数的字符串,把该字符串转换成整数并输出。例 ... -
程序员面试题精选(15)-含有指针成员的类的拷贝
2009-08-15 11:57 1520题目:下面是一个数组 ... -
程序员面试题精选(14)-圆圈中最后剩下的数字
2009-08-15 11:55 2350题目:n个数字(0,1,…,n-1)形成一个圆圈,从数字0开始 ... -
程序员面试题精选(13)-第一个只出现一次的字符
2009-08-15 11:52 1780题目:在一个字符串中找到第一个只出现一次的字符。如输入abac ... -
程序员面试题精选(12)-从上往下遍历二元树
2009-08-15 11:49 1224题目:输入一颗二元树,从上往下按层打印树的每个结点,同一层中按 ... -
程序员面试题精选(11)-求二元查找树的镜像
2009-08-15 11:43 1249题目:输入一颗二元查 ... -
程序员面试题精选(10)-在排序数组中查找和为给定值的两个数字
2009-08-15 11:40 1966题目:输入一个已经按升序排序过的数组和一个数字,在数组中查找两 ... -
程序员面试题精选(09)-查找链表中倒数第k个结点
2009-08-15 11:30 1766题目:输入一个单向链表,输出该链表中倒数第k个结点。链表的倒数 ... -
程序员面试题精选(08)-求1+2+...+n
2009-08-08 21:18 2687题目:求1+2+…+n,要求 ... -
程序员面试题精选(07)-翻转句子中单词的顺序
2009-08-08 21:17 1966题目:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺 ... -
程序员面试题精选(06)-判断整数序列是不是二元查找树的后序遍历结果
2009-08-08 21:15 1563题目:输入一个整数数组,判断该数组是不是某二元查找树的后序遍历 ... -
程序员面试题精选(05)-查找最小的k个元素
2009-08-08 21:14 1773题目:输入n个整数,输出其中最小的k个。 例如输入1,2,3, ... -
程序员面试题精选(04)-在二元树中找出和为某一值的所有路径
2009-08-08 21:12 1335题目:输入一个整数和一棵二元树。从树的根结点开始往下访问一直到 ... -
程序员面试题精选(03)-求子数组的最大和
2009-08-08 21:10 1955题目:输入一个整形数组,数组里有正数也有负数。数组中连续的一个 ... -
程序员面试题精选(02)-设计包含min函数的栈
2009-08-08 21:08 1857题目:定义栈的数据结构,要求添加一个min函数,能够得到栈的最 ... -
程序员面试题精选(01)-把二元查找树转变成排序的双向链表
2009-08-08 20:58 1972题目:输入一棵二元查 ...
相关推荐
O(logn)求Fibonacci数列 - **矩阵快速幂法**:利用矩阵快速幂的思想来求解斐波那契数列。 - **核心思想**:将斐波那契数列的递推关系转化为矩阵的形式,然后利用矩阵快速幂的方法求解。 - **代码实现**:展示如何...
16. **Fibonacci数列的O(logn)求解**:使用矩阵快速幂可以在对数时间内计算Fibonacci数列的第n项。 17. **字符串转换为整数**:实现一个函数将字符串转换为整数,需要处理负号、溢出等问题。 18. **两个栈实现队列...
16. **O(logn)求 Fibonacci 数列** - 使用动态规划或矩阵快速幂等方法可以实现O(logn)时间复杂度求解斐波那契数列。 这些题目覆盖了C语言基础、数据结构、算法等多个方面,理解并掌握这些知识点对于程序员来说至关...
LeetCode 是一个在线平台,专为程序员提供各种算法练习,以提升编程技能,特别是对于面试准备非常有帮助。它涵盖了多种编程语言,其中包括 C 语言。在这个“Leetcode:几个面试练习题”的主题中,我们可以深入探讨...
例如,斐波那契数列、背包问题、最长公共子序列等问题都可以通过动态规划高效解决。 5. **回溯法**:常用于解决组合优化问题,如八皇后问题、N皇后问题、迷宫问题等。通过试探性地构建解决方案并在遇到矛盾时回溯,...
例如,斐波那契数列、背包问题、最长公共子序列等。 4. 回溯法:用于解决组合优化问题,如八皇后问题、N皇后问题、子集问题等。 5. 贪心算法:每一步都选择当前最优解,但不一定能得出全局最优解。如霍夫曼编码、...