題目: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);
}
}