本期任務:介紹算法中關於回溯思想的幾個經典問題
一、問題描述
問題來源:LeetCode 78. 子集
給定一組不含重複元素的整數數組 nums,返回該數組所有可能的子集(冪集)。
說明:解集不能包含重複的子集。
示例:
輸入: nums = [1,2,3]
輸出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
二、算法思路
- 使用回溯思想,暴力窮舉,在示例中,每一個位置都可能有1,2,3,[](空集)共4種可能,所有可能的擺放方式共有,窮舉過程遵循深度優先搜索規則。
- 剪枝策略:只遍歷當前數字之後的數字或空集。
- 結算情形:進行了三輪遞歸時
以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
- 題目描述
給定一個可能包含重複元素的整數數組 nums,返回該數組所有可能的子集(冪集)。
說明:解集不能包含重複的子集。
示例:
輸入: [1,2,2]
輸出:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
-
解決方案:維護一個existed數組,記錄各個位置的被訪問情況,同時使用集合existed_f來記錄訪問過的狀態進一步剪枝。
-
具體代碼
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()