LeetCode HOT 100:子集(簡單易懂的回溯)

煜航發表於2022-12-22

題目:78. 子集

題目描述:

給你一個整數陣列,陣列中元素互不相同。返回陣列中所有可能的子集,且子集不能重複
什麼是子集?舉個例子:原陣列[1, 2, 3][]、[1]、[1, 2]、[1, 3]、[1, 2, 3]、[2]、[2, 3]、[3]這些都是原陣列的子集。所以說子集就是原陣列中零個或幾個元素組成的集合。而且,子集是無序的,[1, 2][2, 1]是同一個子集,所以說雖然[2, 1]也是原陣列的子集,但是題目要求子集不能重複,所以不能在結果中一起出現。

思路:

其實這一題思路和組合總數全排列很像,也是回溯。只不過每次遞迴的時候,不能像別的題可以從0開始窮舉,那樣會導致子集重複,所以要每次遞迴的時候,傳入一個開始下標startIndex,這樣就能避免子集重複問題。

步驟:

回溯模版可以看前面的全排列或者組合總數題目講解,這裡就不贅述。
本題主要是回溯方法怎麼寫,所以下面步驟是回溯方法的步驟。
1、先定義好回溯方法的入參,陣列,本次遞迴窮舉的開始下標
2、定義好回溯方法後,方法裡首先確定回溯結束的條件
這一題可以不需要回溯結束條件。因為這一題本身就是中間狀態的子集也需要收集,並沒有說子集長度多少或者什麼別的條件的時候才收集。而且不加回溯結束條件也不會導致無限遞迴,因為迴圈中,開始下標是不斷增大的,等到開始下標達到陣列長度的時候,自然迴圈終止!
3、下面就是開始窮舉,虛擬碼如下

for (int i = startIndex; i < nums.length; i++) {
	將元素放入陣列
	迭代回溯方法(傳入的開始下標是 i + 1,因為下個迭代從 i + 1 開始遍歷)
	將元素從陣列中刪除,回溯
}

程式碼:

    List<List<Integer>> ans;
    List<Integer> list;

    public List<List<Integer>> subsets(int[] nums) {
        ans = new ArrayList<>();
        list = new ArrayList<>();

        process(nums, 0);
        return ans;
    }

    public void process(int[] nums, int startIndex) {
        ans.add(new ArrayList<>(list));

        // 子集不講究順序,所以[1,2]和[2,1]是同一子集
        // 題目要求不能返回重複子集
        // 所以 i 不能從 0 開始,要從 startIndex 開始。
        // 這樣才能保證不斷往後找,不能回頭找
        for (int i = startIndex; i < nums.length; i++) {
            list.add(nums[i]);
            process(nums, i + 1);
            list.remove(list.size() - 1);
        }
    }

相關文章