【算法】【回溯篇】第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種可能,所有可能的擺放方式共有434^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()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章