【算法】【回溯篇】第6节:子集问题

本期任务:介绍算法中关于回溯思想的几个经典问题

【算法】【回溯篇】第1节:八皇后问题

【算法】【回溯篇】第2节:解数独问题

【算法】【回溯篇】第3节:正则表达式问题

【算法】【回溯篇】第4节:全排列问题

【算法】【回溯篇】第5节:组合问题

【算法】【回溯篇】第6节:子集问题

【算法】【回溯篇】第7节:0-1背包问题


一、问题描述

问题来源:LeetCode 78. 子集


给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例:

输入: nums = [1,2,3]
输出:
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

二、算法思路

  • 使用回溯思想,暴力穷举,在示例中,每一个位置都可能有1,2,3,[](空集)共4种可能,所有可能的摆放方式共有 4 3 4^3 ,穷举过程遵循深度优先搜索规则。
  • 剪枝策略:只遍历当前数字之后的数字或空集。
  • 结算情形:进行了三轮递归时

以nums=[1, 2]为例,递归树如下:

在这里插入图片描述


三、Python代码实现

class Solution:
    def subsets(self, nums):

        self.nums = nums
        self.size = len(self.nums)

        self.ans = []
        self.nums.append([])  # 增加一个空集,保证每个叶子节点都递归self.size层

        self.helper([], 0)
        return self.ans

    def helper(self, arr, index):
        if index == self.size:  # 总共进行n轮
            # 对结果做排序和去重
            arr.sort()
            if arr not in self.ans:
                self.ans.append(arr)
            return

        for v in self.nums[index:]:  # 每次可选择的有self.size-index种(包含空集)
            if v not in arr:
                self.helper(arr + [v] if v != [] else arr, index + 1)


def main():
    client = Solution()
    print(client.subsets([1, 2, 3]))


if __name__ == '__main__':
    main()

输出结果:

[[1, 2, 3], [1, 2], [1, 3], [1], [2, 3], [2], [3], []]

四、问题变形

问题来源:LeetCode 90. 子集 II

  1. 题目描述
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例:

输入: [1,2,2]
输出:
[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]
  1. 解决方案:维护一个existed数组,记录各个位置的被访问情况,同时使用集合existed_f来记录访问过的状态进一步剪枝。

  2. 具体代码

class Solution:
    def subsetsWithDup(self, nums):
        self.nums = nums
        self.size = len(self.nums)

        self.ans = []
        self.nums.append([])  # 增加一个空集,保证每个叶子节点都递归self.size层
        self.existed = [0] * (self.size + 1)
        self.existed_f = set()  # 使用集合存储已计算过的子问题

        self.helper([], 0)
        return self.ans

    def helper(self, arr, index):
        if index == self.size:  # 总共进行n轮
            # 对结果做排序和去重
            arr.sort()
            if arr not in self.ans:
                self.ans.append(arr)
            return

        for i, v in enumerate(self.nums):  # 每次可选择的有self.size-index种(包含空集)
            if i >= index and self.existed[i] == 0 or i == self.size:  # 空集可以多次使用
                new_arr, new_index = arr + [v] if v != [] else arr, index + 1
                new_arr.sort()
                new_arr1 = tuple(new_arr)
                if (new_arr1, new_index) not in self.existed_f:
                    self.existed_f.add((new_arr1, new_index))
                    self.existed[i] = 1
                    self.helper(new_arr, new_index)
                    self.existed[i] = 0


def main():
    client = Solution()
    print(client.subsetsWithDup([1, 4, 3, 5, 4, 4, 7, 7, 8, 0]))
    

if __name__ == '__main__':
    main()
原创文章 36 获赞 32 访问量 2741

猜你喜欢

转载自blog.csdn.net/weixin_43868754/article/details/105682631