DFS算法模板-排列组合-python

DFS算法模板:

def dfs(array or root, cur_layer, path, result):
    if cur_layer == len(array) or not root:
        result.append(path)
        return
     
    for i in range(cur_layer, len(array)):
        do something with array[cur_layer:i+1] or nodes with this layer
        path.append(xxx) # 修改path 或者 其他操作
        dfs(array, i + 1 or cur_layer + 1, path, result)
        path.pop() # 还原path 或者 还原之前的操作

排列组合是组合学最基本的概念。所谓排列,就是指从给定个数的元素中取出指定个数的元素进行排序。组合则是指从给定个数的元素中仅仅取出指定个数的元素,不考虑排序。

https://baike.baidu.com/item/排列组合/706498

(一)组合

//组合
def recursive_c(cur, m, cur_list, original_list, result_list):
    if m == 0:
        result_list.append(list(cur_list))
        return
    for i in range(cur, len(original_list)):
        cur_list.append(original_list[i])
        recursive_c(i + 1,  m - 1, cur_list, original_list, result_list)
        cur_list.pop()

>>original_list = [1, 2, 3, 4]
>>result_list =[[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]
>>共4种

【 题目描述】给定一个数组 num (整数)和一个整数 target. 找到 num 中所有的数字之和为 target 的组合.

原文链接:https://blog.csdn.net/qq_19446965/article/details/81775702

/查找和为target的所有组合,DFS法
def dfs(cur, sum, target, path_list, original_list, result_list):
    if sum == target:
        result_list.append(list(path_list))
        return

    if sum > target:
        return

    for i in range(cur, len(original_list)):
        path_list.append(original_list[i])
        dfs(i + 1, sum + original_list[i], target, path_list, original_list, result_list)
        path_list.pop()

【题目描述】给定一个数组 num 和一个整数 target. 找到 num 中所有的数字(不重复)之和为 target 的组合.
原文链接:https://www.jiuzhang.com/solutions/combination-sum-ii/

注意:if i > 0 and original_list[i] == original_list[i-1]:

def dfs(cur, sum, target, path_list, original_list, result_list):
    if target == sum:
        result_list.append(path_list)
        return
    if sum > target:
        return
    for i in range(cur, len(original_list)):
        if i > 0 and original_list[i] == original_list[i-1]:
            continue
        dfs(i + 1, sum + original_list[i], target, path_list + [original_list[i]], original_list, result_list)

【题目描述】给定一个数组 num 和一个整数 target. 找到 num 中所有的数字之和为 target 的组合(不重复).
https://www.jiuzhang.com/solutions/combination-sum-ii/

注意:if i > cur and original_list[i] == original_list[i-1]:

def dfs(cur, sum, target, path_list, original_list, result_list):
    if target == sum:
        result_list.append(path_list)
        return
    if sum > target:
        return
    for i in range(cur, len(original_list)):
        if i > cur and original_list[i] == original_list[i-1]:
            continue
        dfs(i + 1, sum + original_list[i], target, path_list + [original_list[i]], original_list, result_list)

【题目描述】
给定一个候选数字的集合 candidates 和一个目标值 target. 找到 candidates 中所有的和为 target 的组合.
在同一个组合中, candidates 中的某个数字不限次数地出现.
https://www.jiuzhang.com/solutions/combination-sum/

分析:
Combination Sum 一个数可以选很多次,搜索时从 index 开始而不是从 index + 1

dfs(candidates, index, target - candidates[index], combination, results);

(二)排列

问题模型:求出所有满足条件的“排列”。
判断条件:组合中的元素是顺序“相关”的。
时间复杂度:与 n! 相关。

//全排列
def recursive_a(m, cur_list, original_list, result_list):
    if m == 0:
        result_list.append(list(cur_list))
        return
    for i in range(len(original_list)):
        cur_list.append(original_list[i])
        temp_list = deepcopy(original_list)
        temp_list.pop(i)
        recursive_a(m - 1, cur_list, temp_list, result_list)
        cur_list.pop()
		
>>original_list = [1, 2, 3, 4]
>>result_list = [[1, 2, 3], [1, 2, 4], [1, 3, 2], [1, 3, 4], [1, 4, 2], [1, 4, 3], [2, 1, 3], [2, 1, 4], [2, 3, 1], [2, 3, 4], [2, 4, 1], [2, 4, 3], [3, 1, 2], [3, 1, 4], [3, 2, 1], [3, 2, 4], [3, 4, 1], [3, 4, 2], [4, 1, 2], [4, 1, 3], [4, 2, 1], [4, 2, 3], [4, 3, 1], [4, 3, 2]]
>>共24种

https://blog.csdn.net/qq_19446965/article/details/102463792        

【题目】给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2]
输出:
[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutations-ii

方法一:先生成再去重

def helper(self, nums, res, path):
    if not nums and path not in res:
        res.append(path)
    else:
        for i in range(len(nums)):
            self.helper(nums[:i] + nums[i+1:], res, path + [nums[i]])

耗时:1356 ms
这样的做法是在每个path生成之后才去做的判断,因此效率一点都不高。

方法二:深度拷贝

    def helper(self, nums, res, path):
        if not nums and path not in res:
            res.append(list(path))
            return

        for i in range(len(nums)):
            path.append(nums[i])
            temp_list = deepcopy(nums)
            temp_list.pop(i)
            self.helper(temp_list, res, path)
            path.pop()	

耗时:超时

方法三:回溯法

下面这个做法是标准的回溯法,需要用到visited来表示哪些位置已经被添加到path中了。
为什么有重复?
在这个例子中,我们在第一个1开始的排列中已经取了第二个1的情况;如果在第二个1开始的排列中仍然取第一个1,就有重复了。
如何去重呢?
1)排序
2)不是第一个数字,并且现在的数字和前面的数字相等,同时前面的数字还没有访问过,我们是不能搜索的,需要直接返回。
原因是,这种情况下,必须是由前面搜索到现在的这个位置,而不能是由现在的位置向前面搜索。

def helper(self, nums, res, val, visit):
    if len(val) == len(nums) :
        res.append(val)
        
    for i in range(len(nums)):
        if i > 0 and not visit[i - 1] and nums[i - 1] == nums[i]:
            continue
            
        if not visit[i]:
            visit[i] = True
            self.helper(nums, res, val+[nums[i]], visit)
            visit[i] = False

耗时:56 ms

方法四:交换思想

def swap(nums, i, cur):
    nums[cur], nums[i] = nums[i], nums[cur]


def helper_1(cur, nums, res):
    if cur == len(nums):
        res.append(list(nums))

    for i in range(cur, len(nums)):
        if nums[i] not in nums[i+1:]:
            swap(nums, i, cur)
            helper(cur + 1, nums, res)
            swap(nums, i, cur)

耗时:44 ms    

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值