经典算法回顾总结

1. 递归(分治)

最近刷到一些递归题目,总结了一下递归的程序编写框架。针对不同问题,递归函数有两种不同的形式:

  1. 递归累加问题:具有返回值的递归函数;
  2. 全排列问题:没有返回值,但对某一中间变量进行增删的递归函数。

对于递归问题,我一直有个有个很直观的概念,觉得递归树的同一层是同时执行的。实际上,是顺序执行,或者说,该递归树是以后根遍历的方式顺序执行的(因为一般的递归操作最后的返回值与左右子递归树相关,先递,后归,因此可以强行理解为,后根遍历)

1.1 递归累加问题

对于第一种问题的递归函数一般只包含两部分内容:

  1. 返回的边界条件;
  2. 递归公式。

以斐波那契问题为例,我们可以很容得到斐波那契函数的递归函数:

def fib(x):
	# 定义返回的边界条件
    if x < 2:
        return 0 if x == 0 else 1
    # 定义递归公式
    return fib(x - 1) + fib(x - 2)

print(fib(6))  # 打印结果为:8

1.2 第二种问题

这类问题通常可以通过在全排列中添加条件来减少全排列的计算次数,或者本身就是全排列问题。解决这类问题,我们的递归函数通常包含如下内容:

  1. 返回的边界条件(通常是将需要的数字全部排列好);
  2. 递归公式;
  3. 不断修改的全排列数组(先增加某一位,递归后减少该位置(可以设置访问标志位))。

比如全排列问题(给定一个没有重复数字的序列,返回其所有可能的全排列):

class Solution:
    def permute(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
		# 定义全排列数组和访问标志位
        not_visit = [True for _ in range(len(nums))]
        tmp = nums[:]

        def dfs(position):
        	# 定义返回的边界条件
            if position == len(nums):
                res.append(tmp[:])
                return
            # 定义全排列访问
            for index in range(0, len(nums)):
                if not_visit[index]:
                	# 修改全排列数组和访问标志位(已访问)
                    tmp[position] = nums[index]
                    not_visit[index] = False
                  	# 进入递归
                    dfs(position + 1)
                    # 修改全排列数组和访问标志位(该位置未访问)
                    not_visit[index] = True

        res = []
        dfs(0)
        return res

比如N皇后问题(LeetCode 面试题 08.12. 八皇后):

import copy
class Solution:
    def solveNQueens(self, n: int):
    	# 定义全排列数组
        self.QueenLocation = []
        # 全局变量,由递归边界条件返回结果
        self.results = []
        drawresults = []
        # 开始递归
        self.countQueen(n, 0)
        
        for result in self.results:
            drawresults.append(self.draw(result, n))
        return drawresults
        
    def countQueen(self, n, i):
    	# 定义递归边界条件
        if i == n:
            self.results.append(copy.copy(self.QueenLocation))
            return
        # 定义全排列访问
        for j in range(n):
            if not self.allisinline((i,j)):
            	# 修改(增加)全排列数组(同时也是标志位)
                self.QueenLocation.append((i, j))
                # 进入递归
                self.countQueen(n, i+1)
                # 修改(删除)全排列数组(同时也是标志位)
                self.QueenLocation.remove((i, j))
                
    # 确定该位置是否可以放置皇后
    def allisinline(self, b):
        for a in self.QueenLocation:
            if self.isinline(a, b):
                return True
        return False
        
    def isinline(self, a, b):
        if a[1] == b[1] or a[0] == b[0]:
            return True
        elif (a[0] - b[0])/(a[1] - b[1]) == 1 or (a[0] - b[0])/(a[1] - b[1]) == -1:
            return True
        else:
            return False
        
    def draw(self,result, n):
        temp = [['.' for i in range(n)] for i in range(n)]
        for i, j in result:
            temp[i][j] = 'Q'
        
        temp = [''.join(line) for  line in temp]
        return temp

if __name__ == "__main__":
    solu = Solution()
    solu.solveNQueens(4)

2. 最大公因数

2.1 递归方法

def gcd(a: int, b: int):
    if b == 0:
        return a
    else:
        return gcd(b, a % b)

2.2 非递归方法

def NonRecursiveGcd(x,y):
    while(y):
        x , y = y , x % y
    return x 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值