leetcode39&40_Combination Sum& CombinationSumII

一.问题描述

Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.

The same repeated number may be chosen from C unlimited number of times.

Note:

  • All numbers (including target) will be positive integers.
  • The solution set must not contain duplicate combinations.

For example, given candidate set [2, 3, 6, 7] and target 7
A solution set is: 

[
  [7],
  [2, 2, 3]
]
即给定一个int的list,即一个目标和target,找出list中所有和为target的int值组合,其中list中的int可以重复使用,也可以不按顺序,但是返回值中不能有重复的组合。


二.代码编写

   看到这种题型我的第一反应是dp,但是dp并不适合这种要返回所有具体组合的题,(dp更适合返回是否存在这样的组合)。个人认为,因为dp是用空间换取时间,但这里需要存储的组合数过多。

   这种题是典型的DFS的思想,想到DFS就一定要想到回溯,实际上回溯就是DFS加上剪枝的思想(剪枝就是当不满足条件的时候及时回退,不继续往下遍历)。

这个题目要注意的是停止条件的确定,用到栈的思想,先入栈不满足条件的时候将其弹出。

我们始终

代码编写上要注意的是,我们用list_a存储当前的栈,一旦满足条件(==target)时要将其append到输出list中,由于list是可变类型,所以赋值的时候要用到deepcopy()。

代码如下:

class Solution(object):
    def combinationSum(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        # backtracking
        list_a = []
        return_list = []
        i = 0
        # step1: sort the candidates
        candidates.sort()
        sum = 0
        while i < len(candidates) or list_a!=[]:   # stopping condition
            while i >= len(candidates):  # when this way going to an end
                if list_a == []:  # when list_a is None and i>=len(Candidates), then stop searching
                    break
                aaa = list_a.pop() # then up backtracking
                sum -= aaa
                i = candidates.index(aaa)+1
            if i>=len(candidates):   # stop searching
                break
            sum += candidates[i]
            list_a.append(candidates[i])   # add it to the stack
            if sum > target:  # backtracking
                ind = list_a.pop()
                sum -= ind
                if len(list_a) != 0:
                    ind = list_a.pop()
                    sum -= ind
                    #i += 1
                i = candidates.index(ind)+1
                #i = candidates.index(list_a[-1])
                #i += 1
            elif sum == target:
                #list_a.append(candidates[i])
                return_list.append(copy.deepcopy(list_a))   # dont append list_a directly!!!
                ind = list_a.pop()
                sum -= ind
                #i += 1
                if len(list_a) != 0:
                    ind = list_a.pop()
                    sum -= ind
                    #list_a.pop()
                    #i += 1
                i = candidates.index(ind)+1
            '''
            else:
                sum += candidates[i]
                list_a.append(candidates[i])
            '''
        '''
        sum = 0
        list_b = []
        while candidates[-1]!=target and sum <= target:
            list_b.append(candidates[-1])
            sum += candidates[-1]
            if sum == target and (return_list==[] or return_list[-1]!=list_b):
                return_list.append(copy.deepcopy(list_b))
                break
        '''
        return return_list


回溯图,举例如下:



基本上就是树的DFS(Depth First Search),回溯的时候网上回溯两个节点,最上面形式化了一个头结点。


三.CombinationSumII

这一题和39的不同之处在于每个元素只能使用一次,当然了,不是说在整个寻找过程中只能用一次,而是说在一个结果中不能重复使用。

实现的注意点:

1)每次循环指针i加1;

2)有重复元素,index的时候要index到最后一个(python对list没有rindex,所以自定义实现了)

3)一种特殊情况(candidates = [3,1,3,5,1,1], target = 8),存在一种情况就是分支走到最后一个元素,sum仍然小于target,这时候的回溯要直接回溯到head,否则会不停循环,TLE



代码:

'''
@ author: wttttt at 2016.11.19
@ problem description see: https://leetcode.com/problems/combination-sum-ii/
@ solution explanation see: http://blog.csdn.net/u014265088/article/details/53138838
@ backtracking(pruned dfs)
'''
import copy
class Solution(object):
    # the function used to find the last targeted element in the list, somewhat like rindex() for str.
    def findlast(self,list_test,tar):
        i = 0
        while tar in list_test[i:]:
            i += list_test[i:].index(tar)
            i += 1
        return i-1
    def combinationSum2(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        # backtracking
        list_a = []
        return_list = []
        i = 0
        # step1: sort the candidates
        candidates.sort()
        sum = 0
        while i < len(candidates) or list_a != []:  # stopping condition
            # another difference contrast to leetcode39 is:
            # one conditon that the sumii should notice is that because u can just use each element once,
            # so that u may backtracking when sum<target, at this situation, u should pop() all the element in list_a.
            while i >= len(candidates) and list_a!=[]:  # when this way going to an end
                if list_a == []:  # when list_a is None and i>=len(Candidates), then stop searching
                    break
                aaa = list_a.pop()  # then up backtracking
                sum -= aaa
                i = self.findlast(candidates,aaa) + 1
            if i >= len(candidates):  # stop searching
                break
            sum += candidates[i]
            list_a.append(candidates[i])  # add it to the stack
            if sum > target:  # backtracking
                ind = list_a.pop()
                sum -= ind
                if len(list_a) != 0:
                    ind = list_a.pop()
                    sum -= ind
                i = self.findlast(candidates,ind)
            elif sum == target:
                # list_a.append(candidates[i])
                return_list.append(copy.deepcopy(list_a))  # dont append list_a directly!!!
                ind = list_a.pop()
                sum -= ind
                # i += 1
                if len(list_a) != 0:
                    ind = list_a.pop()
                    sum -= ind
                i = self.findlast(candidates, ind)
            # main difference contrast to leetcode39
            i += 1
        return return_list

So = Solution()
candi = [3,1,3,5,1,1]
target = 8
print So.combinationSum2(candi,target)
#print So.findlast([1,1,2,5,6,7,10],1)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值