1. 程式人生 > >演算法44--回溯法1--subsets,subsets2

演算法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