目錄
1 問題描述
求n個正整數構成的一個給定集合A = {a1,a2,a3,...,an}的子集,子集的和要等於一個給定的正整數d。請輸出所有符合條件的子集。
2 解決方案
本文下面編碼思想參考自文末參考資料1,下面的思想講解直接引用文末參考資料1。
2.1 全排列思想求解
方法1:首先,將子集保存在一個數組鏈表中,每次往鏈表中添加一個元素;
從空集增加到最大集,再回溯,遞歸返回的時候,再將鏈表最后一個元素從子集移出;
這樣就實現了,元素在與不在子集中,這兩種狀態。
注意,每次加入一個元素得到的,子集數組中的元素就構成了一個子集。
具體代碼如下:
package com.liuzhen.chapter12; import java.util.ArrayList; public class SubSet { public ArrayList<Integer> list = new ArrayList<Integer>(); //用於存放求取子集中的元素 //求取數組鏈表中元素和 public int getSum(ArrayList<Integer> list) { int sum = 0; for(int i = 0;i < list.size();i++) sum += list.get(i); return sum; } /* * 參數step:選中數組A中第step個元素為子集元素 * 函數功能:求取數組A中一個子集元素,其相加之和等於m */ public void getSubSet(int[] A, int m, int step) { while(step < A.length) { list.add(A[step]); //遞歸執行語句,向數組鏈表中添加一個元素 if(getSum(list) == m) //鏈表中元素和等於m System.out.println(list); step++; getSubSet(A, m, step); list.remove(list.size() - 1); //回溯執行語句,刪除數組鏈表最后一個元素 } } public static void main(String[] args) { SubSet test = new SubSet(); int[] A = new int[6]; for(int i = 0;i < 6;i++) { A[i] = i + 1; } test.getSubSet(A, 8, 0); } }
運行結果:
[1, 2, 5] [1, 3, 4] [2, 6] [3, 5]
2.2 狀態空間樹思想求解
方法2:從元素在與不在子集這兩種狀態來考慮,因為每個元素都有兩種狀態,
可以理解為一種二叉樹的形式,如下圖:
每一個元素帶有一個屬性,在不在子集中,1(true)表示在子集,0(false)表示不再自己中。
每個元素的狀態初始化為1,實際上無需去構造二叉樹。
第一個遞歸將所有元素逐個放入子集,當所有元素放入子集后回溯。
第一次遞歸返回后,將最后一個元素移出子集,這樣每個元素在與不在子集的狀態都可以遍歷到,每次遍歷到最后一個元素時,按照元素的狀態打印字符序列,得到的就是一個子集。
具體代碼如下:
package com.liuzhen.chapter12; import java.util.ArrayList; public class SubSet { public int getSum1(boolean[] visited, int[] A) { int sum = 0; for(int i = 0;i < A.length;i++) { if(visited[i]) sum += A[i]; } return sum; } public void getSubSet1(boolean[] visited, int[] A, int m, int step) { if(step == A.length) { if(getSum1(visited, A) == m) { for(int i = 0;i < A.length;i++) { if(visited[i]) System.out.print(A[i]+" "); } System.out.println(); } return; } visited[step] = true; getSubSet1(visited, A, m, step + 1); visited[step] = false; getSubSet1(visited, A, m, step + 1); } public static void main(String[] args) { SubSet test = new SubSet(); int[] A = new int[6]; boolean[] visited = new boolean[6]; for(int i = 0;i < 6;i++) { A[i] = i + 1; visited[i] = false; } test.getSubSet1(visited, A, 8, 0); } }
運行結果:
1 2 5 1 3 4 2 6 3 5
參考資料:
1. Algorithm:求集合子集的幾種思路和方法(2)回溯法