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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值