LeetCode偶爾一題 —— 39. Combination Sum(回溯算法系列)

題目描述

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

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

Note:

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

大意:給定一組不含重複數字的數組和一個目標數字,在數組中找出所有數加起來等於給定的目標數字的組合。

輸入

candidates = [2,3,6,7], target = 7

輸出

[
  [7],
  [2,2,3]
]

分析題目

由於我們需要找到多個組合,簡單的使用 for 循環肯定是不行的,這時候我們可以使用回溯算法來解決這個問題。

用回溯算法解決問題的一般步驟:

  1. 針對所給問題,定義問題的解空間,它至少包含問題的一個(最優)解。
  2. 確定易於搜索的解空間結構,使得能用回溯法方便地搜索整個解空間 。
  3. 以深度優先的方式搜索解空間,並且在搜索過程中用剪枝函數避免無效搜索。

根據題目的描述我們知道它滿足了我們所說的步驟一,下面我們來確定搜索的思路👇

搜索的思路

回溯一般需要遍歷所有的情況來找出問題的解,在寫代碼之前我們不妨先畫出一個遞歸樹,理清我們寫代碼的思路👇

由於數組中的數字是可以被重複使用的,所以對於同一個數字也要向下遞歸。但是,對於 [2,2,3][2,3,2] 這樣的結果 其實是重複的,我們需要剔除掉重複的項。
可以這樣優化遞歸樹👇

其他問題

如何保存數據

剛刷題目的時候看到這類多種解的問題經常會感到懵逼,其實這裏通常的解法是傳入一個臨時數組,進入遞歸前 push 一個結果,結束之前可以用一個全局數組保存下來,結束之後在臨時數組pop 掉它。

如何確定結束條件

結束條件通常題目中就會給出,一般來說找到給出的解或者遞歸層數達到上限就可以結束遞歸

示例代碼

function foo (nums, target) {
    let result = []
    
    dfs(0, 0, [])
    return result
    
    function dfs (index, sum, tmp) {
        if (sum === target) {
            result.push(tmp.slice())
        }
        if (sum > target) {
            return
        }
        for (let i = index; i < nums.length; i++) {
            tmp.push(nums[i])
            dfs(i, sum + nums[i], tmp)
            tmp.pop()
        }
    }
}

總結

對於這類題目,使用回溯算法顯然很方便。當然,除了遞歸之外也能用 或者 隊列 來解決。另外,對於前端同學,遇到需要開闢臨時數組的題目時,如果存在賦值操作,記得返回它的淺複製,如 result.push(tmp.slice()),否則會對結果產生影響。

原題地址: Combination Sum
代碼不定時更新,歡迎 star 我的 repo

掃描下方的二維碼或搜索「tony老師的前端補習班」關注我的微信公衆號,那麼就可以第一時間收到我的最新文章。

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