重温算法Day7:递归

递归是一种应用非常广泛的算法
递归需要满足的三个条件
1. 一个问题的解可以分解为几个子问题的解
2. 这个问题与分解之后的子问题,除了数据规模不同,求解思路完全一样
3. 存在递归终止条件

写递归代码最关键的是写出递推公式,找到终止条件

示例:
有 n 个台阶,每次你可以跨 1 个台阶或者 2 个台阶,请问走这 n 个台阶有多少种走法?
分析:
n=1时,1种;
n=2时,2种;
n>2时,第一步走1个台阶,则有f(n-1)种;第一步走2个台阶,则有f(n-2)种;
所以 f(n) = f(n-1) + f(n-2) 

func f(n int) int {
  if n == 1 {
    return 1
  } 
  if n == 2 {
    return 2
  } 
  return f(n-1) + f(n-2)
}

写递归代码的关键就是找到如何将大问题分解为小问题的规律,并且基于此写出递推公式,然后再推敲终止条件,最后将递推公式和终止条件翻译成代码。

注意点:

1.要警惕堆栈溢出,限制递归调用的最大深度.
2.要警惕重复计算。通过一个数据结构(比如散列表)来保存已经求解过的 f(k)。当递归调用到 f(k) 时,先看下是否已经求解过了。

例题:

https://leetcode-cn.com/problems/climbing-stairs/

func climbStairs(n int) int {
    //方法2
    // var tempMap = make(map[int]int)
    // return climb2(0, n, tempMap)

    //方法1,3,4
    return climb4(n)
}

//dp原理:dp[i] = dp[i-1] + dp[i-2]

//方法1 暴力法
func climb1(i, n int) int {
	if i == n {
		return 1
	}

	if i > n {
		return 0
	}

	return climb1(i+1, n) + climb1(i+2, n)
}

//方法2 记忆
func climb2(i, n int, tempMap map[int]int) int {
	if i == n {
		return 1
	}

	if i > n {
		return 0
	}

    if tempMap[i] == 0 {
        tempMap[i] = climb2(i + 1, n, tempMap) + climb2(i + 2, n, tempMap)
    }

	return tempMap[i]
}

//方法3 动态规划
func climb3(n int) int {
    var tempMap = make(map[int]int)
    tempMap[1] = 1
    tempMap[2] = 2
    for i := 3; i <= n; i++ {
        tempMap[i] = tempMap[i-1] + tempMap[i-2]
    }

    return tempMap[n]
}

//方法4 动态规划 + 优化
func climb4(n int) int {
    if n == 1 {
        return 1
    }
    one, two := 1, 2
    for i := 3; i <= n; i++ {
        one, two = two, one + two
    }

    return two
}

因为递归调用一次就会在内存栈中保存一次现场数据,所以在分析递归代码空间复杂度时,需要额外考虑这部分的开销.

发布了142 篇原创文章 · 获赞 24 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/Linzhongyilisha/article/details/105646828
今日推荐