LeetCode 77 回溯与深浅拷贝

题目

LeetCode 77:组合

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。

解题思路

回溯算法:

  • 首先确定参数:n和k肯定是需要的(题目给的条件);返回的答案存在参数res中;每一种组合记录在参数route
  • 确定终止条件:回溯算法需要在每一次回溯开始时判断是否满足结束条件,本题的结束条件是len(route)达到k
  • 画出回溯树(以n=4,k=2为例)
    LeetCode 77 回溯与深浅拷贝_第1张图片
  • 回溯树画完之后发现一个问题:根节点1往下回溯时子节点就没有1了,根节点2往下回溯时子节点就没有1和2了(为了控制数字不重复[1,1],路径不重复[1,2]和[2,1])
  • 如何控制这个树的形状,需要引入一个新的参数startindex,代表每一次向下回溯从哪一个数开始

代码

def combine(n, k, res, route, startindex):
    if len(route) == k:
        res.append(route)
        print(route)
        return

    for i in range(startindex, n+1):
        route.append(i)
        combine(n, k, res, route, i+1)
        route.pop()
    return res

问题

上述写法得到的res为内容全部为空的列表,但print(route)得到的答案却是正确的
这是为什么呢?

深拷贝和浅拷贝

将第三行代码改为如下形式,即可得到正确答案

res.append(route[:])

原来代码中的res.append(route),是将route直接加入res中,所以当route每一次发生变化时,res中每一个元素都会发生变化。
对route进行拷贝route[:],确保添加近res中的每一个数不再与参数route产生关联。

  • 浅拷贝

1、切片操作:a = route[:] 或者a = [each for each in route]
2、工厂函数:a = list(route)
3、copy函数:a = copy.copy(route)

  • 深拷贝(copy.deepcopy(route))
  • 区别:浅拷贝只拷贝了一层
    即如果route中有列表,那么对route中列表进行修改,a也会发生变化
route = [1, 2, ['a','b','c']]
a = route[:]
route[2].append('hhhhhh')
print(route,a)

>>>[1, 2, ['a', 'b', 'c', 'hhhhhh']] [1, 2, ['a', 'b', 'c', 'hhhhhh']]

更多的疑问

以下代码来自于:
https://leetcode-cn.com/problems/combinations/solution/dfs-bfsliang-chong-fang-fa-python3ban-be-uylt/

class Solution:
    def combine(self, n: int, k: int):
        result = []

        # DFS
        # 已取的为temp 从剩下[cur..n]区间中取或不取数 直到temp有k个元素
        def backtrack(temp, cur, n):
            # 如果加上剩下的也取不够k个(之前不取的太多了) 直接剪枝
            if len(temp) + n - cur + 1 < k:
                return

            # 如果已经取够了
            if len(temp) == k:
                result.append(temp)
                return

            # 不选取当前位置  (深度优先)
            backtrack(temp, cur + 1, n)

            # 选取当前位置
            backtrack(temp + [cur], cur + 1, n)

        backtrack([], 1, n)
        return result

为什么这个不用copy,直接append?
欢迎交流讨论!

你可能感兴趣的:(python,leetcode,算法)