数据结构与算法分类练习--递归 回溯 分治

递归是在运行过程中调用自己,最常见的例子就是斐波纳契数列。递归算法的优点是它能使一个蕴含递归关系且结构复杂的程序简介精炼, 增加可读性. 缺点是每一级递归都需要调用函数, 会创建新的栈,随着递归深度的增加, 创建的栈越来越多, 效率低, 让费内存空间. 解决方案是把递归转换为尾递归。尾递归基于函数的尾调用, 每一级调用直接返回函数的返回值更新调用栈,而不用创建新的调用栈, 类似迭代的实现, 时间和空间上均优化了一般递归!详参

回溯是从问题的某一种可能出发, 搜索从这种情况出发所能达到的所有可能, 当这一条路走到" 尽头 "再倒回出发点, 从另一个可能出发, 继续搜索.即按深度优先方法从开始节点系统的搜索一个问题的所有解或者任意解。可以用递归实现。

分治是将一个规模为n的问题分解为k个规模较小的子问题,这些子问题互相独立且与原问题相同。递归地解这些问题,然后将各个子问题的解合并成原问题的解。

使用分治法的条件:

  • 1) 该问题的规模缩小到一定的程度就可以容易地解决 
  • 2) 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。 
  • 3) 利用该问题分解出的子问题的解可以合并为该问题的解; 
  • 4) 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。

Combination Sum(无重复数字,可以重复使用)

Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T. The same repeated number may be chosen from C unlimited number of times.

定义一个包含三个主要变量的递归函数,start记录当前的递归到的下标,tmp为一个解,result保存所有已经得到的解,每次进入更深一层递归时,target要减去当前数组的的数。

class Solution(object):
    def combinationSum(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        result = []
        self.combinationSumRec(sorted(candidates), target, 0, [], result)
        return result
    
    def combinationSumRec(self, candidates, target, start, tmp, result):
        if target == 0:
            result.append(list(tmp))
        while start < len(candidates) and candidates[start] <= target:
            tmp.append(candidates[start])
            # 下式不能使用 target -= candidates[start] 因为target值不能变,每一层都要和它比大小
            self.combinationSumRec(candidates, target - candidates[start], start, tmp, result)
            tmp.pop()
            start += 1

附上tmp的变化来更直观的理解调用过程。

Combination Sum II(有重复数字,不可以重复使用)  

Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T. Each number in C may only be used once in the combination. 

在上一题基础上进行两处修改即可:

  • 因为有重复数字,需要在while循环里添加判断:同一层的数值是否相等?如果相等递归下去就会有重复结果
  • 因为数字不可以重复使用,每进一层递归,需要前进一个数值。
class Solution(object):
    def combinationSum2(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        result = []
        self.combinationSum2Rec(sorted(candidates), target, 0, [], result)
        return result
    
    def combinationSum2Rec(self, candidates, target, start, tmp, result):
        if target == 0:
            result.append(list(tmp))
        pre = -1
        while start < len(candidates) and candidates[start] <= target:
            if pre != candidates[start]:
                tmp.append(candidates[start])
                self.combinationSum2Rec(candidates, target - candidates[start], start + 1, tmp, result)
                tmp.pop()
                pre = candidates[start]
            start += 1

同样附上tmp的变化过程:

Permutations (生成排列数)

Given a collection of numbers, return all possible permutations.

For example,

[1,2,3] have the following permutations:

[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], and [3,2,1].

排列和组合不同的地方是:

需要用到一个used数组来标记某个数字是否访问过,每层递归函数均从头开始遍历。

class Solution(object):
    #Backtracking--slow
    def permute1(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        result = []
        used = [0] * len(nums)
        self.permuteRec(result, nums, [], used)
        return result
    
    def permuteRec(self, result, nums, tmp, used):
        if len(tmp) == len(nums):
            result.append(list(tmp))
            return
        for i in range(len(nums)):
            if not used[i]:
                used[i] = 1
                tmp.append(nums[i])
                self.permuteRec(result, nums, tmp, used)
                tmp.pop()
                used[i]= 0

下面是效率更高的插入法:

a1a2a3的全排列有六种,实际上是在a1a2和a2a1的不同的位置上加入a3,同样a1a2和a2a1是在a1的前后两个位置分别加入了a2。

class Solution(object):
    # Insert--fast
    def permute(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        perms = [[]]   
        for n in nums:
            new_perms = []
            for perm in perms:
                for i in xrange(len(perm)+1):   
                    new_perms.append(perm[:i] + [n] + perm[i:])   ###insert n
            perms = new_perms
        return perms

N-Queens (N皇后问题 输出棋盘)

The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.

Given an integer n, return all distinct solutions to the n-queens puzzle.

Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space respectively.

需要用到col,pie,na三个数组,每一位分别表示当前位置三个方向是否占用,从第0行开始递归,每一行以此遍历各列,如果在该位置放置后三个方向均不会有冲突,则下行一行,达到最后一行则生成一种解法,将其存入solutions中,然后继续完成剩余搜索情况。

class Solution(object):
    def solveNQueens(self, n):
        """
        :type n: int
        :rtype: List[List[str]]
        """
        self.solutions = []
        self.col = [0] * n
        self.pie = [0] * (2 * n - 1)
        self.na = [0] * (2 * n - 1)
        self.solveNQueensRec([], 0, n)
        return self.solutions
    
    def solveNQueensRec(self, solution, row, n):
        if row == n:
            #self.solutions.append(map(lambda x: '.' * x + 'Q' + '.' * (n - 1 - x), solution)) 
            # this method is more faster
            self.solutions.append(['.' * x + 'Q' + '.' * (n - 1 - x) for x in solution])
            return
        for i in range(n):
            if not self.col[i] and not self.pie[row + i] and not self.na[row + n - 1 - i]:
                self.col[i] = self.pie[row + i] = self.na[row + n - 1 -i] = 1
                self.solveNQueensRec(solution + [i], row + 1, n)
                self.col[i] = self.pie[row + i] = self.na [row + n -1 -i] = 0

N-Queens II (N皇后问题 输出个数)

Follow up for N-Queens problem.

Now, instead outputting board configurations, return the total number of distinct solutions.

输出个数要比上一题简单,我们改用更加高效的位运算来求解

class Solution(object):
    def totalNQueens(self, n):
        if n < 1: return []
        self.count = 0
        self.DFS(n, 0, 0, 0, 0)
        return self.count
    
    def DFS(self, n, row, cols, pie, na):
        # terminate recursion
        if row >= n:
            self.count += 1
            return
        # 有空的位置
        bits = (~(cols | pie | na)) & ((1 << n) - 1)  
        while bits:
            # 取最右边的1再组成二进制数
            p = bits & -bits  
            # 对角线方向的禁位对下一行的影响需要平移一位
            self.DFS(n, row + 1, cols | p, (pie | p) << 1, (na | p) >> 1)
            # 当前位置置0
            bits = bits & (bits - 1)

Different Ways to Add Parentheses (不同的加括号方法)

Given a string of numbers and operators, return all possible results from computing all the different possible ways to group numbers and operators. The valid operators are +, -and *.

Example

Input: "2*3-4*5"

(2*(3-(4*5))) = -34 ((2*3)-(4*5)) = -14 ((2*(3-4))*5) = -10 (2*((3-4)*5)) = -10 (((2*3)-4)*5) = 10

Output: [-34, -14, -10, -10, 10]

划分左右子树,构造递归。

def diffWaysToCompute(self, input):
    if input.isdigit():
        return [int(input)]
    res = []
    for i in xrange(len(input)):
        if input[i] in "-+*":
            res1 = self.diffWaysToCompute(input[:i])
            res2 = self.diffWaysToCompute(input[i+1:])
            for j in res1:
                for k in res2:
                    res.append(self.helper(j, k, input[i]))
    return res
    
def helper(self, m, n, op):
    if op == "+":
        return m+n
    elif op == "-":
        return m-n
    else:
        return m*n

Median of Two Sorted Arrays (求两个排序数组的中位数)

There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

解法详参 leetcode原版讲解 中文版讲解

class Solution(object):    
    def findMedianSortedArrays(self, nums1, nums2):
        N1, N2 = len(nums1), len(nums2)
        if N1 < N2: 
            nums1, N1, nums2, N2 = nums2, N2, nums1, N1
        l, r = 0, N2*2
        while l <= r:
            j = (l + r) >> 1
            i = N1 + N2 - j
            L1 = -sys.maxint-1 if i == 0 else nums1[(i-1)>>1]
            L2 = -sys.maxint-1 if j == 0 else nums2[(j-1)>>1]
            R1 = sys.maxint if i == 2*N1 else nums1[i>>1]
            R2 = sys.maxint if j == 2*N2 else nums2[j>>1]
            if L1 > R2: l = j + 1
            elif L2 > R1: r = j - 1
            else:
                return (max(L1, L2) + min(R1, R2))/2.0

猜你喜欢

转载自blog.csdn.net/ZJL0105/article/details/81262510
今日推荐