27 组合数学-子集合问题

目录

DFS或BFS解法

二项组合树解法

位运算解法


DFS或BFS解法

       这个问题很简单,对于给定的集合,输出所有的子集合。对于这个问题,我们不用学习组合数学也知道这是2的n次方种子集合,特殊的有空集和平凡子集(也就是本身)。

       以三个元素的集合为例子,我们可以把所有子集合化成一张图

       上图中,N代表空的意思。也就是对于集合的每个元素,我们选择要这个元素还是不要这个元素。很明显,这是一个树状结构。对于树状结构,我们有两种算法,DFS与BFS。如果我们只用一个路径变量,很难存储每个结点从根部到自己的完整路径,所以采用节点本身存储自身到根部的路径的策略。Null是不能删除的,因为通过NULL凑数,就可以通过节点本身的路径长度判断节点自身所处的树层级,方便取子节点或结束寻找子节点过程。

       思路找到了,代码写起来就非常容易了,以下是DFS算法的python代码:

def from_path(path):
    result = []
    for e in path:
        if e is not None:
            result.append(e)
    return result


# all subsets of set
def subsets(input):
    # DFS用的栈
    stack = [[None]]
    # 路径
    # 所有子集合
    n = len(input)
    all_subsets = []

    while len(stack) > 0:
        node = stack.pop()
        level = len(node)

        if level == n + 1:
            all_subsets.append(from_path(node))
        else:
            # add children
            stack.append(node + [None])
            stack.append(node + [input[level - 1]])
    return all_subsets


if __name__ == '__main__':
    print(subsets([1, 2, 3]))

输出结果:

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

二项组合树解法

       前文已经介绍过,链接如下:

       23 二项组合树_你的指尖有改变世界的力量-CSDN博客

       但是代码有所不同,因为这里不能用剪枝策略。

def combinations(input):
    # 队列元素是集合加上索引的元组
    queue = [([], 0)]
    result = []
    while len(queue) > 0:
        e = queue.pop(0)
        result.append(e[0])
        # 计算children
        subarray = input[e[1]:]
        for i, child in enumerate(subarray):
            x = (e[0] + [child], e[1] + i + 1)
            queue.append(x)
    return result


if __name__ == '__main__':
    print(combinations(['A', 'B', 'C']))

       测试结果:

[[], ['A'], ['B'], ['C'], ['A', 'B'], ['A', 'C'], ['B', 'C'], ['A', 'B', 'C']]

位运算解法

       我们考虑到每一个元素在不在子集合中,可以组成一个二进制的一位。所以N个元素的子集合就可以用二进制数字表示。如此可以避免频繁出栈入栈,不仅代码性能更高,而且更加简短清晰。

def from_mask(input, mask):
    result = []
    for i, e in enumerate(input):
        if (1 << i) & mask != 0:
            result.append(e)
    return result


# all subsets of set
def subsets(input):
    # DFS用的栈
    # 路径
    # 所有子集合
    n = len(input)
    all_subsets = [from_mask(input, mask) for mask in range(0, 1 << n)]
    return all_subsets


if __name__ == '__main__':
    print(subsets([1, 2, 3]))

输出结果

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

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

醒过来摸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值