算法筆記_074:子集和問題(Java)


目錄

1 問題描述

2 解決方案

2.1 全排列思想求解

2.2 狀態空間樹思想求解

 


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)回溯法 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM