1. 程式人生 > >生成非空集合--逐步生成結果_遞迴

生成非空集合--逐步生成結果_遞迴

1.問題描述:

 請編寫一個方法,返回某集合的所有非空子集。
 給定一個int陣列A和陣列的大小int n,請返回A的所有非空子集。
 保證A的元素個數小於等於20,且元素互異

2.在紙上進行簡單分解問題之後我們發現有一種技巧。(從陣列中的第一個元素和空集開始考慮,而且每次可以保持這個集合也和可以向集合中添加當前元素

先從簡單一點的例子來著手進分析,陣列A中的元素分別是1,2,3

假如把陣列中的每個元素分割來進行考慮的,可以分成{1},{2},{3},然後分別進行考慮,{1}可以保持這個集合,也可以新增2變成{1,2},即可以變成{1},{1,2},同理{2}可以變成{2},{2,3},{3}就不能夠增加了,再對{1},{1,2}進行增加元素,可以變成:{1},{1,2},{1,3},{1,2,3}

分析是可以這樣分析但是使用遞迴的程式碼來實現的話可能有點困難,因為這裡涉及到的是三棵樹(可以把陣列中每個元素看成是一棵樹)但是使用迴圈加上遍歷的方法也是可以實現的

我們可以使用另外一方法來進行分析:一開始的時候我們可以將陣列中的元素分成空集合第一個元素這裡是{A[0]} 和 {}即{{1}}和{}

然後對{} 和{1}開始處理,我們可以保留{},也可以增加當前元素,可以變成{1},{1,2},{},{2},對於上面的結果我們可以使用同樣的方法進行處理,保留原來這個集合或者向這個集合新增元素{1},{1,3},{1,2},{1,2,3},{2},{2,3},{3},{}

那麼就可以得到所有的子集的結果,而上面的思路是可以遞迴的方法來進行實現的,因為只涉及到一棵樹,而且我們數的每一層結果的處理都是相同的

每一次都可以對上一次返回的進行遍歷再往集合中新增元素進行層層更新得到最終的答案進行返回

其中可以使用Set這個資料結構來儲存元素,因為每一次都要往集合中新增集合,所以使用集合巢狀巢狀集合的方法進行處理

需要注意的問題是假如需要求解非空集合的話,不能在for迴圈中從Set中邊遍歷和邊刪除元素,因為這樣是不允許的,會丟擲ConcurrentModificationException異常

總結:分解問題的時候需要注意技巧,分析能不能使用遞迴來實現,假如可以需要注意的是遞迴每一次處理的方法都是相同的,

所以分解的問題必須是相同的,假如不同就不能夠使用遞迴來處理,其次要注意遞迴解決的時候出口的問題,因為涉及到邊界的問題,自己在寫程式碼的時候可以邊想邊測試一下

     

3.下面是具體的程式碼實現

import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
public class Main{
    public static void main(String[] args) {
        Scanner sc  = new Scanner(System.in);
        int n = sc.nextInt();
        int A[] = new int[n];
        for(int i = 0; i < n; i++){
            A[i] = sc.nextInt();
        }
        Set<Set<Integer>> set = solve(A, n , n - 1);
        for(Set<Integer> set1 : set){
            if(set1.size() == 0){
                //集合不可以一邊遍歷一邊刪除
                set.remove(set1);
                //break;
            }
        }
        System.out.println(set);
    }

    private static Set<Set<Integer>> solve(int[] A, int n, int cur) {
        //cur最多為2
        Set<Set<Integer>> newSet = new HashSet<>();
        if(cur == 0){
            Set<Integer> nil = new HashSet<>();
            Set<Integer> first = new HashSet<>();
            first.add(A[0]);
            newSet.add(nil);
            newSet.add(first);
            return newSet;
        }
        Set<Set<Integer>> oldSet = solve(A, n, cur - 1);

        //對每一層返回的結果進行遍歷
        for(Set<Integer> set : oldSet){
            //不要加入到oldSet中因為正在遍歷著oldSet
            //保留原來的集合
            newSet.add(set);

            //複製set原來的例項需要進行強轉成HashSet才可以使用clone方法
            Set<Integer> clone = (Set<Integer>) ((HashSet) set).clone();
            clone.add(A[cur]);
            newSet.add(clone);
        }

       //最後方法會返回最終呼叫solve(A, n , n -1)的結果
        return newSet;
    }
}