关于斐波那契的两道算法题:爬楼梯

题目一:爬楼梯

假设你正在爬楼梯。需要n阶你才能到达楼顶。
每次你可以爬 1 或者 2 个台阶。有多少中不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。

示例1:

输入:2
输出:2
解释:有两种方法可以爬到楼顶。
1. 1阶 + 1阶
2. 2阶

示例2:

输入:3
输出:3
解释:有三种方法可以爬到楼顶。
1. 1阶 + 1阶 + 1阶
2. 2阶 + 1阶
3. 1阶 + 2阶

题目分析——思考路线:

做这类题目,如果你是老司机,那么肯定可以直接往正确的思路上想了。
如果你是个小白,那么最好的办法就是先代入几个简单的值,来寻找规律。

当n=1,那只有一种,一步到位。

————————————————

当n=2,两种:
	1. 1阶 + 1阶
	2. 2阶
我们发现我们可以 1 步踏上2层,也可以一次 1 层,踏两步。

当n=3,三种:
	1. 1阶 + 1阶 + 1阶
	2. 2阶 + 1阶
	3. 1阶 + 2阶
我们发现,在完成最后一步前,我们可能停留在第 1 阶,也有可能停留在第 2 阶。
既然已经说了是在完成 最后一步前 的位置,那么就分下面两种情况:
	1. 最后一步前的位置,在第 1 阶,最后一步就跨 2 阶
			满足这种情况的有上面的:
				结果3: 1阶(最后一步前的位置) + 2阶
			这种情况下结果只有1种,我们暂时没发现什么特别明显的规律,不要着急,继续看后面的情况,套用百遍,规律自现。
	2. 最后一步前的位置,在第 2 阶,最后一步就跨 1 阶
			满足这种情况的有上面的:
				结果1:	1阶 + 1阶(最后一步前的位置) + 1阶
				结果2:	2阶(最后一步前的位置)  + 1阶
			这种情况下的结果有两种,别动,别动,好好看看有啥发现没有?
			细细观察后,我们发现了他们的共同点,那就是:最后一步是相同的都是跨 1 阶。
			有共同点,就应该考虑不同点:踏上第 2 阶的方法数。
		
如此看来,最后一步,只是无论是跨 1 阶还是跨 2 阶,总的方法数就取决于最后一步前的位置的方法数之和,公式如下:
	总方法数 = 最后一步前在第 1 阶方法数 + 最后一步前在第 2 阶方法数

到这里,是不是感觉脑子里面,有那么点意思了?
哈哈,不急,我们还需要验证一下,咱们继续往下代入,套用。

当n=4,五种:
	1. 1阶 + 1阶 + 1阶 + 1阶
	2. 1阶 + 1阶 + 2阶
	3. 1阶 + 2阶 + 1阶
	4. 2阶 + 1阶 + 1阶
	5. 2阶 + 2阶
因为1步最多只能跨2阶,所以,最后一步前的位置只能在:
	第 3 阶:
		结果1: 1阶 + 1阶 + 1阶(最后一步前的位置) + 1阶
		结果3: 1阶 + 2阶(最后一步前的位置) + 1阶
		结果4: 2阶 + 1阶(最后一步前的位置) + 1阶
	第 2 阶:
		结果2: 1阶 + 1阶(最后一步前的位置) + 2阶
		结果5: 2阶(最后一步前的位置) + 2阶
啧啧啧,那我们之前大概看出的公式,套用在这里看看成不成立:
	总方法数 = 最后一步前在第 2 阶的方法数 + 最后一步前在第 3 阶的方法数

卧槽,是不是有种豁然开朗的感觉,果然成立了。

既然前面的推测成立了,那第 n 阶的话怎么套用呢?

最后一步踏上第 n 阶,那么前一步应该就是在:
	第 n-1 阶  或者   第 n-2 阶
所以:
	踏上第 n 阶 的方法数 = 踏上第 n-1 阶的方法数 + 踏上第 n-2 阶的方法数
由此得到公式:
	f(n) = f(n-1) + f(n-2)

这么一看,这不就是个 肥boy纳妾(斐波那契) 的数列啊。
那么,我们对着道题的解法就是如下(Golang):

func climbStairs(n int) int {
    res := int(1)
    lres := int(0)
    for i:=1;i<=n;i++ {
        res, lres = res + lres, res
    }
    return res
}

题目二:解码方法

一条包含字母 A-Z 的消息通过以下映射进行了 编码 :

'A' -> 1
'B' -> 2
...
'Z' -> 26

要 解码 已编码的消息,所有数字必须基于上述映射的方法,反向映射回字母(可能有多种方法)。例如,“11106” 可以映射为:

  • “AAJF” ,将消息分组为 (1 1 10 6)
  • “KJF” ,将消息分组为 (11 10 6)

注意,消息不能分组为 (1 11 06) ,因为 “06” 不能映射为 “F” ,这是由于 “6” 和 “06” 在映射中并不等价。

给你一个只含数字的 非空 字符串 s ,请计算并返回 解码 方法的 总数 。

题目数据保证答案肯定是一个 32 位 的整数。

示例 1:

输入:s = "12"
输出:2
解释:它可以解码为 "AB"(1 2)或者 "L"(12)。

示例 2:

输入:s = "226"
输出:3
解释:它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。

示例 3:

输入:s = "0"
输出:0
解释:没有字符映射到以 0 开头的数字。
含有 0 的有效映射是 'J' -> "10" 和 'T'-> "20" 。
由于没有字符,因此没有有效的方法对此进行解码,因为所有数字都需要映射。

示例 4:

输入:s = "06"
输出:0
解释:"06" 不能映射到 "F" ,因为字符串含有前导 0("6" 和 "06" 在映射中并不等价)。

提示:

  • 1 <= s.length <= 100
  • s 只包含数字,并且可能包含前导零。

题目分析——思考路线:

这个题目,起始就是要把一个数字序列分割成一个个大于0小于27的数(因为字母有26个,所以不能超过27),求分割方法数。

看到这道题目是不是有种似曾相识的感觉?像不像爬楼梯题目的兄弟?

那这道题怎么解决呢?

首先我们需要先把目标数字序列的每个数字,按照以下规则进行换算:

(字母数字:大于0小于27的数字)

成为字母数字的条件:
	A:当前数字大于 0
	B:当前数字能和前一个数字组合成字母数字
规则:
	A成立 B成立:换算为 2
	A成立 B不成立:换算为 1
	A不成立 B成立:换算为 1
	A不成立 B不成立:换算为 0

举例,按照以上规则换算下面数字:

1235127102178123
1221121111221122
1235127102170123
1221121111220122

这个时候,可能小伙伴还不知道换算的数值有啥用,别着急,听我细细说与你听。

换算的值只有三个:0、1、2
那么它和分割方法数有什么关系呢?

举例:

1216718010110
1222112011121

这里我们将举例说明 换算值 k(n) 和 分割方法数f(n) 的关系:

当 k(n) = 0 时:
整个数列无法分割成一个所有元素都为字母数字的数列
如上面例子中 0,其分割情况为:80,0
两个都不能成为字母数字
所以 f(n) = 0

当 k(n) = 1 且 第 n 个数字不为 0 时:
当前数字要么:
	只能独自成为字母数字
要么:
	和前一个数字组合成为字母数字
如上面的例子中的 7 的分割方法有以下几种:
	(1、2、1、6)、7
	(1、2、16)、7
	(1、21、6)、7
	(12、1、6)、7
	(12、16)、7
我们把1216分割的所有方法写成:(1216)
则 7 的分割方法写成如下:
	(1216)、7
而(1216)= f(4)
我们发现一共5种,f(4) = 5 ,数字 7 在(1216)的每一种情况后面加了一个 7 
并没有额外增加分割方法,所以:
	f(5) = f(4) ✖️ 1
即 : f(n) = f(n-1)

当 k(n) = 1 且 第 n 个数字为0 时:
	当 k(n-1) = 1 时: 
		如上面例子中的倒数第二个 0 的分割方法有以下几种:
		(12167180)、10
		而 (12167180)= f(8)
		数字 0 在(121671801)的每一种情况最后的1变成了10
		并没有额外增加分割方法,所以:
			f(10) = f(8)✖️ 1
		即 : f(n) = f(n-2)
	当 k(n-1) = 2 时:
		如上面例子中的最后一个数字 0 的分割方法有以下几种:
			(12167180101)、10
		而(12167180101)= f(11)
		数字 0 被限制在了 第 12 个数字上,所以第 12 个数字不能和前面的数字绑定了
		所以:
			f(13) = f(11) ✖️ 1
		即 :f(n) = f(n-2)

当 k(n) = 2 时:
	当 k(n-1) = 1 时:
		如上面例子中的 8 的分割方法有以下几种:
			(12167)、1、8
			(12167)、18
		则 f(7) = f(5) ✖️ 2
		即 :f(n) = f(n-2) ✖️ 2
	当 k(n-1) = 2 时:
		如上面例子中的 6 的分割方法有以下几种:
			(121)、6
			(12)、16
		则 :f(4) = f(3) + f(2)
		即 :f(n) = f(n-1) + f(n-2)

总上所述,计算分割方法数的公式为:

当 n = 1 时: f(n) = 1

当 n > 1 时:
——————————————————————————————
	当 k(n) = 0 时: f(n) = 0
	__________________________
	当 k(n) = 1 时:
		当 数字为 0 时:
			当 k(n-1) = 1 时: f(n) = f(n-2)
			————————————————————————————————
			当 k(n-1) = 2 时: f(n) = f(n-2)
			————————————————————————————————
		当数字不为 0 时:
			 f(n) = f(n-1)
			————————————————————————————————
	当 k(n) = 2 时:
		当 k(n-1) = 1 时:f(n) = f(n-2) ✖️ 2
		————————————————————————————————————
		当 k(n-1) = 2 时:f(n) = f(n-1) + f(n-2)

由此可的得到代码如下(Golang):

func numDecodings(s string) int {
    if len(s) == 0 || s[0] == 48 {
		return 0
	}
	// 初始化 f(n)
	res := 1
	// 初始化 k(n)
	r := 1
	// 记录 f(n-1)
	lres := 1
	ls := int32(0)
	for _, ss := range s {
		// ss 为byte格式 48-54 代表 数字0-6
		if ss == 48 && (ls > 50 || ls < 49) {
			return 0
		}
		if ss > 48 && (ls == 49 || (ls == 50 && ss >= 48  && ss <= 54)){
			if r == 1 {
				res, lres = res * 2, res
			} else if r == 2 {
				res, lres = lres+res, res
			}
			r = 2
		} else {
			if ss == 48 {
				res, lres = lres, res
			} else {
				lres = res
			}
			r = 1
		}
		ls = ss
	}
	return res
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 假设你正在楼梯,需要n阶你才能到达楼顶。每次你可以1或2个台阶。那么你有多少种不同的方法可以到楼顶呢? 答案是斐波数列。斐波数列是指:1、1、2、3、5、8、13、21、34、……,即第一项和第二项为1,从第三项开始,每一项都等于前两项之和。 因为每次只能1或2个台阶,所以到第n阶的方法数就等于到第n-1阶和到第n-2阶的方法数之和。而到第1阶和第2阶的方法数都是1,所以可以得到递推公式: f(n) = f(n-1) + f(n-2) 其中f(n)表示到第n阶的方法数。根据递推公式,可以用递归或循环的方式求解斐波数列,从而得到楼梯的方法数。 ### 回答2: 这个问属于经典的动态规划问,我们可以用递推的方法来解决。 当 n=1 时,显然只有一种方法可以到楼顶,也就是一步一步。 当 n=2 时,有两种方法可以到达楼顶,一种是一次两步,另一种是先一步,再一步。 当 n>2 时,我们假设到达第 i 阶的方法有 f(i) 种。那么到达第 i 阶可以分为两种情况: 1. 在第 i-1 阶时向上一步。 2. 在第 i-2 阶时向上两步。 因此,f(i) = f(i-1) + f(i-2)。 最后只需要算出 f(n) 就可以知道到达楼顶的不同方法了。 我们可以用一个数组来存储 f(i)。初始时,f(1)=1,f(2)=2。 然后从 f(3) 开始,每个 f(i) 都等于前面两项的和,也就是 f(i-1) + f(i-2)。 最后返回 f(n) 就是到达楼顶的不同方法数了。 总结一下,假设你需要 n 阶楼梯才能到达楼顶,你有 f(n) 种不同的方法可以到楼顶。 其中,f(1)=1,f(2)=2,f(i)=f(i-1) + f(i-2) (i>=3)。 ### 回答3: 我们可以用动态规划的思想来解决这个问。设 f(i) 为到第 i 阶台阶时楼的方法数目,因为每一步都只能向上 1 阶或 2 阶台阶,所以到达第 i 阶台阶的方法只有两种:从第 i-1 阶向上 1 阶,或者从第 i-2 阶向上 2 阶。所以我们可以得到状态转移方程: f(i) = f(i-1) + f(i-2) 同时,为了递推出 f(i) 的值,我们还需要初始化 f(1) 和 f(2) 的值。因为到第 1 阶台阶只有一种方法,我们有:f(1) = 1;到第 2 阶台阶有两种方法,我们有:f(2) = 2。所以最终的思路就是:从第 3 阶台阶开始递推,每次用上面的状态转移方程求出 f(i) 的值,直到求出 f(n) 的值为止。 以下是代码实现: int climbStairs(int n) { int f[n+1]; memset(f, 0, sizeof(f)); f[1] = 1; f[2] = 2; for (int i = 3; i <= n; i++) { f[i] = f[i-1] + f[i-2]; } return f[n]; } 算法的时间复杂度为 O(n),空间复杂度为 O(n),可以满足数据规模限制。因此,我们可以用这个算法来计算楼梯的方法数目。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值