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)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章