演算法44--回溯法1--subsets,subsets2
1.Subsets
Given a set of distinct integers, nums, return all possible subsets (the power set).
Note: The solution set must not contain duplicate subsets.
Example:
Input: nums = [1,2,3] Output: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]
給定一個集合,返回所有可能的子集集合。
rr=[] 儲存最終所有子集 r=[]用來迭代所有取值情況
從子集的元素位數考慮,
當元素個數為0時,直接加入[]即可
當元素個數為1時,r取值有1,2,3 分別加入rr中,當r=[1]時,可以繼續遞迴迭代,考慮再次基礎上元素數為2的取值情況,直到所有元素已經使用完畢,此時r應該回退回上一次狀態繼續回溯。
初始時程式碼:
def subsets(nums=[1,2,3]): rr=[] rr.append([]) r=[] backsubsets(rr, r, 0, nums) #print(rr) return rr def backsubsets(rr, r, index, nums): if index>len(nums)-1: return for i in range(index, len(nums)): r.append(nums[i]) print('r',r) rr.append(r) print('rr', rr) backsubsets(rr, r, i+1, nums) r.pop(-1) #rr.pop(-1)
發現最終結果不對,列印一下:
感覺後一次修改的同時修改了前一次,可能是深拷貝的問題
rr.append(r)只是在rr中儲存了r的指標 如果後序r發生變化則rr中元素也會發生變化,此處應該使用深拷貝: r.copy()
def subsets(nums=[1,2,3]): rr=[] rr.append([]) r=[] backsubsets(rr, r, 0, nums) #print(rr) return rr def backsubsets(rr, r, index, nums): if index>len(nums)-1: return for i in range(index, len(nums)): r.append(nums[i]) print('r',r) rr.append(r.copy()) print('rr', rr) backsubsets(rr, r, i+1, nums) r.pop(-1) #rr.pop(-1)
同時這是一個組合問題,可以使用二進位制演算法來實現
給定陣列nums=[1,2,3]其組合數有pow(2,3)=8種情況,分別對應000--111 其中1表示對應陣列位置取值 0表示不取,從000變化到111即可以變化所有情況,例如110 則表示23 111則表示123
class Solution {
public List<List<Integer>> subsets(int[] nums) {
int N = nums.length;
if (N == 0)
return new ArrayList();
List<List<Integer>> res = new ArrayList();
long numOfCombination = (long) Math.pow(2, N);
for (int i = 0; i < numOfCombination; i++) {
List<Integer> l = new ArrayList();
for (int j = 0; j < N; j++) {
if ((i&(1 << j)) > 0)
l.add(nums[j]);
}
res.add(l);
}
return res;
}
}
大神的寫法:
def subsets(self, nums):
return [list(j) for i in range(len(nums)+1) for j in combinations(nums,i)]
標準回溯法:
一個用來儲存所有狀態 一個用來遍歷所有情況取值
直接儲存當前狀態,
在當前狀態之上考慮下一種狀態,
遞迴下一種狀態
回溯回到當前狀態,進行當前狀態的下一種取值可能
class Solution:
def subsets(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
rr=[]
nums.sort()
self.backsubsets(rr, [], 0, nums)
return rr
def backsubsets(self, rr, r, index, nums):
rr.append(r.copy())
for i in range(index, len(nums)):
r.append(nums[i])
self.backsubsets(rr, r, i+1, nums)
r.pop(-1)
2.Subsets2
Given a collection of integers that might contain duplicates, nums, return all possible subsets (the power set).
Note: The solution set must not contain duplicate subsets.
Example:
Input: [1,2,2] Output: [ [2], [1], [1,2,2], [2,2], [1,2], [] ]
給出去重的組合子集
class Solution:
def subsetsWithDup(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
rr=[]
nums.sort()
self.backtrack(rr, [], 0, nums)
return rr
def backtrack(self, rr, r, index, nums):
rr.append(r.copy())
for i in range(index, len(nums)):
if i>index and nums[i]==nums[i-1]:
continue
r.append(nums[i])
self.backtrack(rr, r, i+1, nums)
r.pop(-1)
考慮nums=[1,2,2]
依次遞迴的順序為[] 1 12 122 12 2 22 3
考慮重複元素 0-----index---i-----len
index用來標識當前是陣列第幾位 i用來在index基礎上繼續新增一位的可能情況,重複出現的情況是 在index基礎上考慮下一位時
i=index index+1 index+2 ....當nums[i]值有重複時,會出現重複情況,因此應該去重
if i>index and nums[i]==nums[i-1]:
continue
如果遍歷i時出現nums[i-1]==nums[i]時,直接跳過進行下一次迴圈,這有點型別與三元素去重求和等於0:
def threeSum(nums=[-2,0,0,2,2]):
nums.sort()
#print(nums)
rr = []
for k in range(len(nums)-1):
if k>=1 and nums[k-1]==nums[k]:
continue
i = k+1
j = len(nums)-1
while i<j:
#print(k, i, j)
sum = nums[k] + nums[i] + nums[j]
if sum==0:
rr.append([nums[k], nums[i], nums[j]])
while i<j and nums[i+1]==nums[i]:
i += 1
while i<j and nums[j-1]==nums[i]:
j -= 1
i += 1
j -= 1
elif sum<0:
i += 1
else:
j -= 1
return rr